LockAgent: Add option to synthesize Java crash logging

Add the ability to dump a "crash" to logcat.

Test: m
Test: manual
Change-Id: I0692a91df995883e526a718fe95f0d3568ac9328
diff --git a/tools/lock_agent/agent.cpp b/tools/lock_agent/agent.cpp
index 5b1d52e..c639427 100644
--- a/tools/lock_agent/agent.cpp
+++ b/tools/lock_agent/agent.cpp
@@ -68,6 +68,7 @@
 
 JavaVM* gJavaVM = nullptr;
 bool gForkCrash = false;
+bool gJavaCrash = false;
 
 // Converts a class name to a type descriptor
 // (ex. "java.lang.String" to "Ljava/lang/String;")
@@ -394,6 +395,8 @@
     for (const std::string& c : config) {
         if (c == "native_crash") {
             gForkCrash = true;
+        } else if (c == "java_crash") {
+            gJavaCrash = true;
         }
     }
 
@@ -405,6 +408,11 @@
     return gForkCrash ? JNI_TRUE : JNI_FALSE;
 }
 
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_android_lock_1checker_LockHook_getSimulateCrashConfig(JNIEnv*, jclass) {
+    return gJavaCrash ? JNI_TRUE : JNI_FALSE;
+}
+
 extern "C" JNIEXPORT void JNICALL Java_com_android_lock_1checker_LockHook_nWtf(JNIEnv* env, jclass,
         jstring msg) {
     if (!gForkCrash || msg == nullptr) {
diff --git a/tools/lock_agent/java/com/android/lock_checker/LockHook.java b/tools/lock_agent/java/com/android/lock_checker/LockHook.java
index 86c97d3..35c75cb 100644
--- a/tools/lock_agent/java/com/android/lock_checker/LockHook.java
+++ b/tools/lock_agent/java/com/android/lock_checker/LockHook.java
@@ -16,6 +16,7 @@
 
 package com.android.lock_checker;
 
+import android.app.ActivityThread;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -24,6 +25,7 @@
 import android.util.Log;
 import android.util.LogWriter;
 
+import com.android.internal.os.RuntimeInit;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.StatLogger;
 
@@ -73,6 +75,7 @@
     private static final LockChecker[] sCheckers;
 
     private static boolean sNativeHandling = false;
+    private static boolean sSimulateCrash = false;
 
     static {
         sHandlerThread = new HandlerThread("LockHook:wtf", Process.THREAD_PRIORITY_BACKGROUND);
@@ -82,9 +85,11 @@
         sCheckers = new LockChecker[] { new OnThreadLockChecker() };
 
         sNativeHandling = getNativeHandlingConfig();
+        sSimulateCrash = getSimulateCrashConfig();
     }
 
     private static native boolean getNativeHandlingConfig();
+    private static native boolean getSimulateCrashConfig();
 
     static <T> boolean shouldDumpStacktrace(StacktraceHasher hasher, Map<String, T> dumpedSet,
             T val, AnnotatedStackTraceElement[] st, int from, int to) {
@@ -184,6 +189,12 @@
         if (sNativeHandling) {
             nWtf(msg);  // Also send to native.
         }
+        if (sSimulateCrash) {
+            RuntimeInit.logUncaught("LockAgent",
+                    ActivityThread.isSystem() ? "system_server"
+                            : ActivityThread.currentProcessName(),
+                    Process.myPid(), v.getException());
+        }
     }
 
     private static native void nWtf(String msg);
@@ -308,5 +319,6 @@
     }
 
     interface Violation {
+        Throwable getException();
     }
 }
diff --git a/tools/lock_agent/java/com/android/lock_checker/OnThreadLockChecker.java b/tools/lock_agent/java/com/android/lock_checker/OnThreadLockChecker.java
index e4e7211..e74ccf9 100644
--- a/tools/lock_agent/java/com/android/lock_checker/OnThreadLockChecker.java
+++ b/tools/lock_agent/java/com/android/lock_checker/OnThreadLockChecker.java
@@ -228,6 +228,8 @@
         AnnotatedStackTraceElement[] mStack;
         OrderData mOppositeData;
 
+        private static final int STACK_OFFSET = 4;
+
         Violation(Thread self, Object alreadyHeld, Object lock,
                 AnnotatedStackTraceElement[] stack, OrderData oppositeData) {
             this.mSelfTid = (int) self.getId();
@@ -284,6 +286,26 @@
                     lock.getClass().getName());
         }
 
+        // Synthesize an exception.
+        public Throwable getException() {
+            RuntimeException inner = new RuntimeException("Previously locked");
+            inner.setStackTrace(synthesizeStackTrace(mOppositeData.mStack));
+
+            RuntimeException outer = new RuntimeException(toString(), inner);
+            outer.setStackTrace(synthesizeStackTrace(mStack));
+
+            return outer;
+        }
+
+        private StackTraceElement[] synthesizeStackTrace(AnnotatedStackTraceElement[] stack) {
+
+            StackTraceElement[] out = new StackTraceElement[stack.length - STACK_OFFSET];
+            for (int i = 0; i < out.length; i++) {
+                out[i] = stack[i + STACK_OFFSET].getStackTraceElement();
+            }
+            return out;
+        }
+
         public String toString() {
             StringBuilder sb = new StringBuilder();
             sb.append("Lock inversion detected!\n");
@@ -294,7 +316,7 @@
             sb.append(" on thread ").append(mOppositeData.mTid).append(" (")
                     .append(mOppositeData.mThreadName).append(")");
             sb.append(" at:\n");
-            sb.append(getAnnotatedStackString(mOppositeData.mStack, 4,
+            sb.append(getAnnotatedStackString(mOppositeData.mStack, STACK_OFFSET,
                     describeLocking(mAlreadyHeld, "will lock"), getTo(mOppositeData.mStack, mLock)
                     + 1, "    | "));
             sb.append("  Locking ");
@@ -303,7 +325,8 @@
             sb.append(describeLock(mLock));
             sb.append(" on thread ").append(mSelfTid).append(" (").append(mSelfName).append(")");
             sb.append(" at:\n");
-            sb.append(getAnnotatedStackString(mStack, 4, describeLocking(mLock, "will lock"),
+            sb.append(getAnnotatedStackString(mStack, STACK_OFFSET,
+                    describeLocking(mLock, "will lock"),
                     getTo(mStack, mAlreadyHeld) + 1, "    | "));
 
             return sb.toString();