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 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 59 public static void main(String[] args) throws Exception { 60 new Main(args).run(); 61 } 62 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 74 private void runSecondary() { 75 foo(); 76 throw new RuntimeException("Didn't expect to get back..."); 77 } 78 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 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 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). 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 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 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 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 198 public native boolean sleep(int i, boolean b, double dummy); 199 200 public native boolean unwindInProcess(boolean fullSignatures, int i, boolean b); 201 public native boolean unwindOtherProcess(boolean fullSignatures, int pid); 202 } 203