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