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();