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