Main.java revision 49e1fabc85480f01077f3cc10e8ba6ada6e4befa
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.InputStream;
20import java.io.OutputStream;
21import java.lang.reflect.InvocationTargetException;
22import java.lang.reflect.Method;
23import java.util.Arrays;
24import java.util.Comparator;
25
26public 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
39  public Main(boolean secondary) {
40      this.secondary = secondary;
41  }
42
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
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
66  private void runSecondary() {
67      foo();
68      throw new RuntimeException("Didn't expect to get back...");
69  }
70
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.
107          p.destroy();
108      }
109  }
110
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
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).
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
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
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
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
166  public native boolean sleep(int i, boolean b, double dummy);
167
168  public native boolean unwindInProcess(int i, boolean b);
169  public native boolean unwindOtherProcess(int pid);
170}
171