1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import java.io.BufferedReader;
18 import java.io.FileReader;
19 import java.io.InputStreamReader;
20 import java.util.Arrays;
21 import java.util.Comparator;
22 
23 public class Main implements Comparator<Main> {
24   // Whether to test local unwinding.
25   private boolean testLocal;
26 
27   // Unwinding another process, modelling debuggerd.
28   private boolean testRemote;
29 
30   // We fork ourself to create the secondary process for remote unwinding.
31   private boolean secondary;
32 
33   // Expect the symbols to contain full method signatures including parameters.
34   private boolean fullSignatures;
35 
36   private boolean passed;
37 
Main(String[] args)38   public Main(String[] args) throws Exception {
39       System.loadLibrary(args[0]);
40       for (String arg : args) {
41           if (arg.equals("--test-local")) {
42               testLocal = true;
43           }
44           if (arg.equals("--test-remote")) {
45               testRemote = true;
46           }
47           if (arg.equals("--secondary")) {
48               secondary = true;
49           }
50           if (arg.equals("--full-signatures")) {
51               fullSignatures = true;
52           }
53       }
54       if (!testLocal && !testRemote) {
55           System.out.println("No test selected.");
56       }
57   }
58 
main(String[] args)59   public static void main(String[] args) throws Exception {
60       new Main(args).run();
61   }
62 
run()63   private void run() {
64       if (secondary) {
65           if (!testRemote) {
66               throw new RuntimeException("Should not be running secondary!");
67           }
68           runSecondary();
69       } else {
70           runPrimary();
71       }
72   }
73 
runSecondary()74   private void runSecondary() {
75       foo();
76       throw new RuntimeException("Didn't expect to get back...");
77   }
78 
runPrimary()79   private void runPrimary() {
80       // First do the in-process unwinding.
81       if (testLocal && !foo()) {
82           System.out.println("Unwinding self failed.");
83       }
84 
85       if (!testRemote) {
86           // Skip the remote step.
87           return;
88       }
89 
90       // Fork the secondary.
91       String[] cmdline = getCmdLine();
92       String[] secCmdLine = new String[cmdline.length + 1];
93       System.arraycopy(cmdline, 0, secCmdLine, 0, cmdline.length);
94       secCmdLine[secCmdLine.length - 1] = "--secondary";
95       Process p = exec(secCmdLine);
96 
97       try {
98           int pid = getPid(p);
99           if (pid <= 0) {
100               throw new RuntimeException("Couldn't parse process");
101           }
102 
103           // Wait until the forked process had time to run until its sleep phase.
104           try {
105               InputStreamReader stdout = new InputStreamReader(p.getInputStream(), "UTF-8");
106               BufferedReader lineReader = new BufferedReader(stdout);
107               while (!lineReader.readLine().contains("Going to sleep")) {
108               }
109           } catch (Exception e) {
110               throw new RuntimeException(e);
111           }
112 
113           if (!unwindOtherProcess(fullSignatures, pid)) {
114               System.out.println("Unwinding other process failed.");
115           }
116       } finally {
117           // Kill the forked process if it is not already dead.
118           p.destroy();
119       }
120   }
121 
exec(String[] args)122   private static Process exec(String[] args) {
123       try {
124           return Runtime.getRuntime().exec(args);
125       } catch (Exception exc) {
126           throw new RuntimeException(exc);
127       }
128   }
129 
getPid(Process p)130   private static int getPid(Process p) {
131       // Could do reflection for the private pid field, but String parsing is easier.
132       String s = p.toString();
133       if (s.startsWith("Process[pid=")) {
134           return Integer.parseInt(s.substring("Process[pid=".length(), s.indexOf(",")));
135       } else {
136           return -1;
137       }
138   }
139 
140   // Read /proc/self/cmdline to find the invocation command line (so we can fork another runtime).
getCmdLine()141   private static String[] getCmdLine() {
142       try {
143           BufferedReader in = new BufferedReader(new FileReader("/proc/self/cmdline"));
144           String s = in.readLine();
145           in.close();
146           return s.split("\0");
147       } catch (Exception exc) {
148           throw new RuntimeException(exc);
149       }
150   }
151 
foo()152   public boolean foo() {
153       // Call bar via Arrays.binarySearch.
154       // This tests that we can unwind from framework code.
155       Main[] array = { this, this, this };
156       Arrays.binarySearch(array, 0, 3, this /* value */, this /* comparator */);
157       return passed;
158   }
159 
compare(Main lhs, Main rhs)160   public int compare(Main lhs, Main rhs) {
161       passed = bar(secondary);
162       // Returning "equal" ensures that we terminate search
163       // after first item and thus call bar() only once.
164       return 0;
165   }
166 
bar(boolean b)167   public boolean bar(boolean b) {
168       if (b) {
169           return sleep(2, b, 1.0);
170       } else {
171           return unwindInProcess(fullSignatures, 1, b);
172       }
173   }
174 
175   // Native functions. Note: to avoid deduping, they must all have different signatures.
176 
sleep(int i, boolean b, double dummy)177   public native boolean sleep(int i, boolean b, double dummy);
178 
unwindInProcess(boolean fullSignatures, int i, boolean b)179   public native boolean unwindInProcess(boolean fullSignatures, int i, boolean b);
unwindOtherProcess(boolean fullSignatures, int pid)180   public native boolean unwindOtherProcess(boolean fullSignatures, int pid);
181 }
182