blob: dc3ef7eb070dae305c9e0ec6b4fb8d05e0b8402b [file] [log] [blame]
Andreas Gampe73810102015-04-22 18:57:06 -07001/*
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.InputStream;
20import java.io.OutputStream;
21import java.lang.reflect.InvocationTargetException;
22import java.lang.reflect.Method;
David Srbecky3da76082015-06-10 21:52:06 +000023import java.util.Arrays;
24import java.util.Comparator;
Andreas Gampe73810102015-04-22 18:57:06 -070025
David Srbecky3da76082015-06-10 21:52:06 +000026public class Main implements Comparator<Main> {
Andreas Gampe73810102015-04-22 18:57:06 -070027 // 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.
David Srbecky1baabf02015-06-16 17:12:34 +000029 public final static boolean TEST_LOCAL_UNWINDING = true;
Andreas Gampe73810102015-04-22 18:57:06 -070030
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
David Srbecky3da76082015-06-10 21:52:06 +000037 private boolean passed;
38
Andreas Gampe73810102015-04-22 18:57:06 -070039 public Main(boolean secondary) {
40 this.secondary = secondary;
41 }
42
43 public static void main(String[] args) throws Exception {
Mathieu Chartier031768a2015-08-27 10:25:02 -070044 System.loadLibrary(args[0]);
Andreas Gampe73810102015-04-22 18:57:06 -070045 boolean secondary = false;
46 if (args.length > 0 && args[args.length - 1].equals("--secondary")) {
47 secondary = true;
48 }
49 new Main(secondary).run();
50 }
51
Andreas Gampe73810102015-04-22 18:57:06 -070052 private void run() {
53 if (secondary) {
54 if (!TEST_REMOTE_UNWINDING) {
55 throw new RuntimeException("Should not be running secondary!");
56 }
57 runSecondary();
58 } else {
59 runPrimary();
60 }
61 }
62
63 private void runSecondary() {
David Srbecky3da76082015-06-10 21:52:06 +000064 foo();
Andreas Gampe73810102015-04-22 18:57:06 -070065 throw new RuntimeException("Didn't expect to get back...");
66 }
67
68 private void runPrimary() {
69 // First do the in-process unwinding.
David Srbecky3da76082015-06-10 21:52:06 +000070 if (TEST_LOCAL_UNWINDING && !foo()) {
Andreas Gampe73810102015-04-22 18:57:06 -070071 System.out.println("Unwinding self failed.");
72 }
73
74 if (!TEST_REMOTE_UNWINDING) {
75 // Skip the remote step.
76 return;
77 }
78
79 // Fork the secondary.
80 String[] cmdline = getCmdLine();
81 String[] secCmdLine = new String[cmdline.length + 1];
82 System.arraycopy(cmdline, 0, secCmdLine, 0, cmdline.length);
83 secCmdLine[secCmdLine.length - 1] = "--secondary";
84 Process p = exec(secCmdLine);
85
86 try {
87 int pid = getPid(p);
88 if (pid <= 0) {
89 throw new RuntimeException("Couldn't parse process");
90 }
91
92 // Wait a bit, so the forked process has time to run until its sleep phase.
93 try {
94 Thread.sleep(5000);
95 } catch (Exception e) {
96 throw new RuntimeException(e);
97 }
98
99 if (!unwindOtherProcess(pid)) {
100 System.out.println("Unwinding other process failed.");
101 }
102 } finally {
David Srbeckya70e5b92015-06-17 03:52:54 +0100103 // Kill the forked process if it is not already dead.
Andreas Gampe73810102015-04-22 18:57:06 -0700104 p.destroy();
105 }
106 }
107
108 private static Process exec(String[] args) {
109 try {
110 return Runtime.getRuntime().exec(args);
111 } catch (Exception exc) {
112 throw new RuntimeException(exc);
113 }
114 }
115
116 private static int getPid(Process p) {
117 // Could do reflection for the private pid field, but String parsing is easier.
118 String s = p.toString();
119 if (s.startsWith("Process[pid=")) {
120 return Integer.parseInt(s.substring("Process[pid=".length(), s.length() - 1));
121 } else {
122 return -1;
123 }
124 }
125
126 // Read /proc/self/cmdline to find the invocation command line (so we can fork another runtime).
127 private static String[] getCmdLine() {
128 try {
129 BufferedReader in = new BufferedReader(new FileReader("/proc/self/cmdline"));
130 String s = in.readLine();
131 in.close();
132 return s.split("\0");
133 } catch (Exception exc) {
134 throw new RuntimeException(exc);
135 }
136 }
137
David Srbecky3da76082015-06-10 21:52:06 +0000138 public boolean foo() {
139 // Call bar via Arrays.binarySearch.
140 // This tests that we can unwind from framework code.
141 Main[] array = { this, this, this };
142 Arrays.binarySearch(array, 0, 3, this /* value */, this /* comparator */);
143 return passed;
144 }
145
146 public int compare(Main lhs, Main rhs) {
147 passed = bar(secondary);
148 // Returning "equal" ensures that we terminate search
149 // after first item and thus call bar() only once.
150 return 0;
Andreas Gampe73810102015-04-22 18:57:06 -0700151 }
152
153 public boolean bar(boolean b) {
154 if (b) {
155 return sleep(2, b, 1.0);
156 } else {
157 return unwindInProcess(1, b);
158 }
159 }
160
161 // Native functions. Note: to avoid deduping, they must all have different signatures.
162
163 public native boolean sleep(int i, boolean b, double dummy);
164
165 public native boolean unwindInProcess(int i, boolean b);
166 public native boolean unwindOtherProcess(int pid);
167}