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 extends Base 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).runBase();
61   }
62 
runImpl()63   public void runImpl() {
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           BufferedReader lineReader;
105           try {
106               InputStreamReader stdout = new InputStreamReader(p.getInputStream(), "UTF-8");
107               lineReader = new BufferedReader(stdout);
108               while (!lineReader.readLine().contains("Going to sleep")) {
109               }
110           } catch (Exception e) {
111               throw new RuntimeException(e);
112           }
113 
114           if (!unwindOtherProcess(fullSignatures, pid)) {
115               System.out.println("Unwinding other process failed.");
116 
117               // In this case, log all the output.
118               // Note: this is potentially non-terminating code, if the secondary is totally stuck.
119               //       We rely on the run-test timeout infrastructure to terminate the primary in
120               //       such a case.
121               try {
122                   String tmp;
123                   System.out.println("Output from the secondary:");
124                   while ((tmp = lineReader.readLine()) != null) {
125                       System.out.println("Secondary: " + tmp);
126                   }
127               } catch (Exception e) {
128                   e.printStackTrace(System.out);
129               }
130           }
131 
132           try {
133               lineReader.close();
134           } catch (Exception e) {
135               e.printStackTrace(System.out);
136           }
137       } finally {
138           // Kill the forked process if it is not already dead.
139           p.destroy();
140       }
141   }
142 
exec(String[] args)143   private static Process exec(String[] args) {
144       try {
145           return Runtime.getRuntime().exec(args);
146       } catch (Exception exc) {
147           throw new RuntimeException(exc);
148       }
149   }
150 
getPid(Process p)151   private static int getPid(Process p) {
152       // Could do reflection for the private pid field, but String parsing is easier.
153       String s = p.toString();
154       if (s.startsWith("Process[pid=")) {
155           return Integer.parseInt(s.substring("Process[pid=".length(), s.indexOf(",")));
156       } else {
157           return -1;
158       }
159   }
160 
161   // Read /proc/self/cmdline to find the invocation command line (so we can fork another runtime).
getCmdLine()162   private static String[] getCmdLine() {
163       try {
164           BufferedReader in = new BufferedReader(new FileReader("/proc/self/cmdline"));
165           String s = in.readLine();
166           in.close();
167           return s.split("\0");
168       } catch (Exception exc) {
169           throw new RuntimeException(exc);
170       }
171   }
172 
foo()173   public boolean foo() {
174       // Call bar via Arrays.binarySearch.
175       // This tests that we can unwind from framework code.
176       Main[] array = { this, this, this };
177       Arrays.binarySearch(array, 0, 3, this /* value */, this /* comparator */);
178       return passed;
179   }
180 
compare(Main lhs, Main rhs)181   public int compare(Main lhs, Main rhs) {
182       passed = bar(secondary);
183       // Returning "equal" ensures that we terminate search
184       // after first item and thus call bar() only once.
185       return 0;
186   }
187 
bar(boolean b)188   public boolean bar(boolean b) {
189       if (b) {
190           return sleep(2, b, 1.0);
191       } else {
192           return unwindInProcess(fullSignatures, 1, b);
193       }
194   }
195 
196   // Native functions. Note: to avoid deduping, they must all have different signatures.
197 
sleep(int i, boolean b, double dummy)198   public native boolean sleep(int i, boolean b, double dummy);
199 
unwindInProcess(boolean fullSignatures, int i, boolean b)200   public native boolean unwindInProcess(boolean fullSignatures, int i, boolean b);
unwindOtherProcess(boolean fullSignatures, int pid)201   public native boolean unwindOtherProcess(boolean fullSignatures, int pid);
202 }
203