Simplify the 137-cfi test.
Test: ./test.py --host -b -t 137
Change-Id: I4307ab28d942b2baa110ab26035ef0f095d1b3c7
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index a91d348..985d273 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -54,15 +54,38 @@
#endif
}
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_sleep(JNIEnv*, jobject, jint, jboolean, jdouble) {
- // Keep pausing.
- struct timespec ts = { .tv_sec = 100, .tv_nsec = 0 };
- printf("Going to sleep\n");
- for (;;) {
- // Use nanosleep since it gets to the system call quickly and doesn't
- // have any points at which an unwind will fail.
- nanosleep(&ts, nullptr);
+extern "C" JNIEXPORT jint JNICALL Java_Main_startSecondaryProcess(JNIEnv*, jclass) {
+#if __linux__
+ // Get our command line so that we can use it to start identical process.
+ std::string cmdline; // null-separated and null-terminated arguments.
+ ReadFileToString("/proc/self/cmdline", &cmdline);
+ cmdline = cmdline + "--secondary" + '\0'; // Let the child know it is a helper.
+
+ // Split the string into individual arguments suitable for execv.
+ std::vector<char*> argv;
+ for (size_t i = 0; i < cmdline.size(); i += strlen(&cmdline[i]) + 1) {
+ argv.push_back(&cmdline[i]);
}
+ argv.push_back(nullptr); // Terminate the list.
+
+ pid_t pid = fork();
+ if (pid < 0) {
+ LOG(FATAL) << "Fork failed";
+ } else if (pid == 0) {
+ execv(argv[0], argv.data());
+ exit(1);
+ }
+ return pid;
+#else
+ return 0;
+#endif
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_sigstop(JNIEnv*, jclass) {
+#if __linux__
+ raise(SIGSTOP);
+#endif
+ return true; // Prevent the compiler from tail-call optimizing this method away.
}
// Helper to look for a sequence in the stack trace.
@@ -107,12 +130,7 @@
}
#endif
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(
- JNIEnv*,
- jobject,
- jboolean,
- jint,
- jboolean) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(JNIEnv*, jclass) {
#if __linux__
std::unique_ptr<Backtrace> bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid()));
if (!bt->Unwind(0, nullptr)) {
@@ -128,10 +146,10 @@
// only unique functions are being expected.
// "mini-debug-info" does not include parameters to save space.
std::vector<std::string> seq = {
- "Java_Main_unwindInProcess", // This function.
- "java.util.Arrays.binarySearch0", // Framework method.
- "Base.runBase", // Method in other dex file.
- "Main.main" // The Java entry method.
+ "Java_Main_unwindInProcess", // This function.
+ "java.util.Arrays.binarySearch0", // Framework method.
+ "Base.runTest", // Method in other dex file.
+ "Main.main" // The Java entry method.
};
bool result = CheckStack(bt.get(), seq);
@@ -150,8 +168,8 @@
}
#if __linux__
-static constexpr int kSleepTimeMicroseconds = 50000; // 0.05 seconds
-static constexpr int kMaxTotalSleepTimeMicroseconds = 1000000; // 1 second
+static constexpr int kSleepTimeMicroseconds = 50000; // 0.05 seconds
+static constexpr int kMaxTotalSleepTimeMicroseconds = 10000000; // 10 seconds
// Wait for a sigstop. This code is copied from libbacktrace.
int wait_for_sigstop(pid_t tid, int* total_sleep_time_usec, bool* detach_failed ATTRIBUTE_UNUSED) {
@@ -183,17 +201,12 @@
}
#endif
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(
- JNIEnv*,
- jobject,
- jboolean,
- jint pid_int) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(JNIEnv*, jclass, jint pid_int) {
#if __linux__
pid_t pid = static_cast<pid_t>(pid_int);
- // OK, this is painful. debuggerd uses ptrace to unwind other processes.
-
- if (ptrace(PTRACE_ATTACH, pid, 0, 0)) {
+ // SEIZE is like ATTACH, but it does not stop the process (we let it stop itself).
+ if (ptrace(PTRACE_SEIZE, pid, 0, 0)) {
// Were not able to attach, bad.
printf("Failed to attach to other process.\n");
PLOG(ERROR) << "Failed to attach.";
@@ -201,13 +214,12 @@
return JNI_FALSE;
}
- kill(pid, SIGSTOP);
-
bool detach_failed = false;
int total_sleep_time_usec = 0;
int signal = wait_for_sigstop(pid, &total_sleep_time_usec, &detach_failed);
- if (signal == -1) {
+ if (signal != SIGSTOP) {
LOG(WARNING) << "wait_for_sigstop failed.";
+ return JNI_FALSE;
}
std::unique_ptr<Backtrace> bt(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
@@ -224,10 +236,10 @@
// See comment in unwindInProcess for non-exact stack matching.
// "mini-debug-info" does not include parameters to save space.
std::vector<std::string> seq = {
- "Java_Main_sleep", // The sleep function in the other process.
- "java.util.Arrays.binarySearch0", // Framework method.
- "Base.runBase", // Method in other dex file.
- "Main.main" // The Java entry method.
+ "Java_Main_sigstop", // The stop function in the other process.
+ "java.util.Arrays.binarySearch0", // Framework method.
+ "Base.runTest", // Method in other dex file.
+ "Main.main" // The Java entry method.
};
result = CheckStack(bt.get(), seq);
diff --git a/test/137-cfi/expected.txt b/test/137-cfi/expected.txt
index 8db7853..eedae8f 100644
--- a/test/137-cfi/expected.txt
+++ b/test/137-cfi/expected.txt
@@ -1,2 +1,7 @@
JNI_OnLoad called
+Unwind in process: PASS
JNI_OnLoad called
+Unwind other process: PASS
+JNI_OnLoad called
+JNI_OnLoad called
+Unwind other process: PASS
diff --git a/test/137-cfi/run b/test/137-cfi/run
index 9190b1c..4096b89 100755
--- a/test/137-cfi/run
+++ b/test/137-cfi/run
@@ -20,7 +20,7 @@
# there will be JITed frames on the callstack (it synchronously JITs on first use).
${RUN} "$@" -Xcompiler-option --generate-debug-info \
--runtime-option -Xjitthreshold:0 \
- --args --full-signatures --args --test-local --args --test-remote
+ --args --test-local --args --test-remote
return_status1=$?
# Test with minimal compressed debugging information.
diff --git a/test/137-cfi/src-multidex/Base.java b/test/137-cfi/src-multidex/Base.java
index d3f8a56..986a3c2 100644
--- a/test/137-cfi/src-multidex/Base.java
+++ b/test/137-cfi/src-multidex/Base.java
@@ -15,8 +15,12 @@
*/
public abstract class Base {
- abstract public void runImpl();
- public void runBase() {
- runImpl();
+ public void runTest() throws Exception {
+ // Conditionally throw exception to prevent the compiler from inlining the code.
+ if (!this.getClass().getName().equals("Main")) {
+ throw new Exception("Who is calling?");
+ }
+ test();
}
+ abstract public void test();
}
diff --git a/test/137-cfi/src/Main.java b/test/137-cfi/src/Main.java
index 9a2e352..5b32d8e 100644
--- a/test/137-cfi/src/Main.java
+++ b/test/137-cfi/src/Main.java
@@ -22,181 +22,68 @@
public class Main extends Base implements Comparator<Main> {
// Whether to test local unwinding.
- private boolean testLocal;
+ private static boolean testLocal;
// Unwinding another process, modelling debuggerd.
- private boolean testRemote;
+ private static boolean testRemote;
// We fork ourself to create the secondary process for remote unwinding.
- private boolean secondary;
-
- // Expect the symbols to contain full method signatures including parameters.
- private boolean fullSignatures;
-
- private boolean passed;
-
- public Main(String[] args) throws Exception {
- System.loadLibrary(args[0]);
- for (String arg : args) {
- if (arg.equals("--test-local")) {
- testLocal = true;
- }
- if (arg.equals("--test-remote")) {
- testRemote = true;
- }
- if (arg.equals("--secondary")) {
- secondary = true;
- }
- if (arg.equals("--full-signatures")) {
- fullSignatures = true;
- }
- }
- if (!testLocal && !testRemote) {
- System.out.println("No test selected.");
- }
- }
+ private static boolean secondary;
public static void main(String[] args) throws Exception {
- new Main(args).runBase();
- }
-
- public void runImpl() {
- if (secondary) {
- if (!testRemote) {
- throw new RuntimeException("Should not be running secondary!");
+ System.loadLibrary(args[0]);
+ for (int i = 1; i < args.length; i++) {
+ if (args[i].equals("--test-local")) {
+ testLocal = true;
+ } else if (args[i].equals("--test-remote")) {
+ testRemote = true;
+ } else if (args[i].equals("--secondary")) {
+ secondary = true;
+ } else {
+ System.out.println("Unknown argument: " + args[i]);
+ System.exit(1);
}
- runSecondary();
- } else {
- runPrimary();
}
+
+ // Call test() via base class to test unwinding through multidex.
+ new Main().runTest();
}
- private void runSecondary() {
- foo();
- throw new RuntimeException("Didn't expect to get back...");
- }
-
- private void runPrimary() {
- // First do the in-process unwinding.
- if (testLocal && !foo()) {
- System.out.println("Unwinding self failed.");
- }
-
- if (!testRemote) {
- // Skip the remote step.
- return;
- }
-
- // Fork the secondary.
- String[] cmdline = getCmdLine();
- String[] secCmdLine = new String[cmdline.length + 1];
- System.arraycopy(cmdline, 0, secCmdLine, 0, cmdline.length);
- secCmdLine[secCmdLine.length - 1] = "--secondary";
- Process p = exec(secCmdLine);
-
- try {
- int pid = getPid(p);
- if (pid <= 0) {
- throw new RuntimeException("Couldn't parse process");
- }
-
- // Wait until the forked process had time to run until its sleep phase.
- BufferedReader lineReader;
- try {
- InputStreamReader stdout = new InputStreamReader(p.getInputStream(), "UTF-8");
- lineReader = new BufferedReader(stdout);
- while (!lineReader.readLine().contains("Going to sleep")) {
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
-
- if (!unwindOtherProcess(fullSignatures, pid)) {
- System.out.println("Unwinding other process failed.");
-
- // In this case, log all the output.
- // Note: this is potentially non-terminating code, if the secondary is totally stuck.
- // We rely on the run-test timeout infrastructure to terminate the primary in
- // such a case.
- try {
- String tmp;
- System.out.println("Output from the secondary:");
- while ((tmp = lineReader.readLine()) != null) {
- System.out.println("Secondary: " + tmp);
- }
- } catch (Exception e) {
- e.printStackTrace(System.out);
- }
- }
-
- try {
- lineReader.close();
- } catch (Exception e) {
- e.printStackTrace(System.out);
- }
- } finally {
- // Kill the forked process if it is not already dead.
- p.destroy();
- }
- }
-
- private static Process exec(String[] args) {
- try {
- return Runtime.getRuntime().exec(args);
- } catch (Exception exc) {
- throw new RuntimeException(exc);
- }
- }
-
- private static int getPid(Process p) {
- // Could do reflection for the private pid field, but String parsing is easier.
- String s = p.toString();
- if (s.startsWith("Process[pid=")) {
- return Integer.parseInt(s.substring("Process[pid=".length(), s.indexOf(",")));
- } else {
- return -1;
- }
- }
-
- // Read /proc/self/cmdline to find the invocation command line (so we can fork another runtime).
- private static String[] getCmdLine() {
- try {
- BufferedReader in = new BufferedReader(new FileReader("/proc/self/cmdline"));
- String s = in.readLine();
- in.close();
- return s.split("\0");
- } catch (Exception exc) {
- throw new RuntimeException(exc);
- }
- }
-
- public boolean foo() {
- // Call bar via Arrays.binarySearch.
- // This tests that we can unwind from framework code.
+ public void test() {
+ // Call unwind() via Arrays.binarySearch to test unwinding through framework.
Main[] array = { this, this, this };
Arrays.binarySearch(array, 0, 3, this /* value */, this /* comparator */);
- return passed;
}
public int compare(Main lhs, Main rhs) {
- passed = bar(secondary);
+ unwind();
// Returning "equal" ensures that we terminate search
- // after first item and thus call bar() only once.
+ // after first item and thus call unwind() only once.
return 0;
}
- public boolean bar(boolean b) {
- if (b) {
- return sleep(2, b, 1.0);
- } else {
- return unwindInProcess(fullSignatures, 1, b);
+ public void unwind() {
+ if (secondary) {
+ sigstop(); // This is helper child process. Stop and wait for unwinding.
+ return; // Don't run the tests again in the secondary helper process.
+ }
+
+ if (testLocal) {
+ String result = unwindInProcess() ? "PASS" : "FAIL";
+ System.out.println("Unwind in process: " + result);
+ }
+
+ if (testRemote) {
+ // Start a secondary helper process. It will stop itself when it is ready.
+ int pid = startSecondaryProcess();
+ // Wait for the secondary process to stop and then unwind it remotely.
+ String result = unwindOtherProcess(pid) ? "PASS" : "FAIL";
+ System.out.println("Unwind other process: " + result);
}
}
- // Native functions. Note: to avoid deduping, they must all have different signatures.
-
- public native boolean sleep(int i, boolean b, double dummy);
-
- public native boolean unwindInProcess(boolean fullSignatures, int i, boolean b);
- public native boolean unwindOtherProcess(boolean fullSignatures, int pid);
+ public static native int startSecondaryProcess();
+ public static native boolean sigstop();
+ public static native boolean unwindInProcess();
+ public static native boolean unwindOtherProcess(int pid);
}