Main.java revision 03bf174e41488879ad5a7a74896669b7431ad31a
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 17import java.io.BufferedReader; 18import java.io.FileReader; 19import java.io.InputStreamReader; 20import java.util.Arrays; 21import java.util.Comparator; 22 23public class Main implements Comparator<Main> { 24 // Whether to test local unwinding. Libunwind uses linker info to find executables. As we do 25 // not dlopen at the moment, this doesn't work, so keep it off for now. 26 public final static boolean TEST_LOCAL_UNWINDING = true; 27 28 // Unwinding another process, modelling debuggerd. This doesn't use the linker, so should work 29 // no matter whether we're using dlopen or not. 30 public final static boolean TEST_REMOTE_UNWINDING = true; 31 32 private boolean secondary; 33 34 private boolean full_signatures; 35 36 private boolean passed; 37 38 public Main(boolean secondary, boolean full_signatures) { 39 this.secondary = secondary; 40 this.full_signatures = full_signatures; 41 } 42 43 public static void main(String[] args) throws Exception { 44 System.loadLibrary(args[0]); 45 boolean secondary = false; 46 boolean full_signatures = false; 47 for (String arg : args) { 48 if (arg.equals("--secondary")) { 49 secondary = true; 50 } 51 if (arg.equals("--full-signatures")) { 52 full_signatures = true; 53 } 54 } 55 new Main(secondary, full_signatures).run(); 56 } 57 58 private void run() { 59 if (secondary) { 60 if (!TEST_REMOTE_UNWINDING) { 61 throw new RuntimeException("Should not be running secondary!"); 62 } 63 runSecondary(); 64 } else { 65 runPrimary(); 66 } 67 } 68 69 private void runSecondary() { 70 foo(); 71 throw new RuntimeException("Didn't expect to get back..."); 72 } 73 74 private void runPrimary() { 75 // First do the in-process unwinding. 76 if (TEST_LOCAL_UNWINDING && !foo()) { 77 System.out.println("Unwinding self failed."); 78 } 79 80 if (!TEST_REMOTE_UNWINDING) { 81 // Skip the remote step. 82 return; 83 } 84 85 // Fork the secondary. 86 String[] cmdline = getCmdLine(); 87 String[] secCmdLine = new String[cmdline.length + 1]; 88 System.arraycopy(cmdline, 0, secCmdLine, 0, cmdline.length); 89 secCmdLine[secCmdLine.length - 1] = "--secondary"; 90 Process p = exec(secCmdLine); 91 92 try { 93 int pid = getPid(p); 94 if (pid <= 0) { 95 throw new RuntimeException("Couldn't parse process"); 96 } 97 98 // Wait until the forked process had time to run until its sleep phase. 99 try { 100 InputStreamReader stdout = new InputStreamReader(p.getInputStream(), "UTF-8"); 101 BufferedReader lineReader = new BufferedReader(stdout); 102 while (!lineReader.readLine().contains("Going to sleep")) { 103 } 104 } catch (Exception e) { 105 throw new RuntimeException(e); 106 } 107 108 if (!unwindOtherProcess(full_signatures, pid)) { 109 System.out.println("Unwinding other process failed."); 110 } 111 } finally { 112 // Kill the forked process if it is not already dead. 113 p.destroy(); 114 } 115 } 116 117 private static Process exec(String[] args) { 118 try { 119 return Runtime.getRuntime().exec(args); 120 } catch (Exception exc) { 121 throw new RuntimeException(exc); 122 } 123 } 124 125 private static int getPid(Process p) { 126 // Could do reflection for the private pid field, but String parsing is easier. 127 String s = p.toString(); 128 if (s.startsWith("Process[pid=")) { 129 return Integer.parseInt(s.substring("Process[pid=".length(), s.indexOf(","))); 130 } else { 131 return -1; 132 } 133 } 134 135 // Read /proc/self/cmdline to find the invocation command line (so we can fork another runtime). 136 private static String[] getCmdLine() { 137 try { 138 BufferedReader in = new BufferedReader(new FileReader("/proc/self/cmdline")); 139 String s = in.readLine(); 140 in.close(); 141 return s.split("\0"); 142 } catch (Exception exc) { 143 throw new RuntimeException(exc); 144 } 145 } 146 147 public boolean foo() { 148 // Call bar via Arrays.binarySearch. 149 // This tests that we can unwind from framework code. 150 Main[] array = { this, this, this }; 151 Arrays.binarySearch(array, 0, 3, this /* value */, this /* comparator */); 152 return passed; 153 } 154 155 public int compare(Main lhs, Main rhs) { 156 passed = bar(secondary); 157 // Returning "equal" ensures that we terminate search 158 // after first item and thus call bar() only once. 159 return 0; 160 } 161 162 public boolean bar(boolean b) { 163 if (b) { 164 return sleep(2, b, 1.0); 165 } else { 166 return unwindInProcess(full_signatures, 1, b); 167 } 168 } 169 170 // Native functions. Note: to avoid deduping, they must all have different signatures. 171 172 public native boolean sleep(int i, boolean b, double dummy); 173 174 public native boolean unwindInProcess(boolean full_signatures, int i, boolean b); 175 public native boolean unwindOtherProcess(boolean full_signatures, int pid); 176} 177