LockAgent: Add option to synthesize Java crash logging
Add the ability to dump a "crash" to logcat. Test: m Test: manual Change-Id: I0692a91df995883e526a718fe95f0d3568ac9328
This commit is contained in:
@@ -64,6 +64,19 @@ public class RuntimeInit {
|
||||
return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr);
|
||||
}
|
||||
|
||||
public static void logUncaught(String threadName, String processName, int pid, Throwable e) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
// The "FATAL EXCEPTION" string is still used on Android even though
|
||||
// apps can set a custom UncaughtExceptionHandler that renders uncaught
|
||||
// exceptions non-fatal.
|
||||
message.append("FATAL EXCEPTION: ").append(threadName).append("\n");
|
||||
if (processName != null) {
|
||||
message.append("Process: ").append(processName).append(", ");
|
||||
}
|
||||
message.append("PID: ").append(pid);
|
||||
Clog_e(TAG, message.toString(), e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message when a thread encounters an uncaught exception. By
|
||||
* default, {@link KillApplicationHandler} will terminate this process later,
|
||||
@@ -85,17 +98,7 @@ public class RuntimeInit {
|
||||
if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
|
||||
Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
|
||||
} else {
|
||||
StringBuilder message = new StringBuilder();
|
||||
// The "FATAL EXCEPTION" string is still used on Android even though
|
||||
// apps can set a custom UncaughtExceptionHandler that renders uncaught
|
||||
// exceptions non-fatal.
|
||||
message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");
|
||||
final String processName = ActivityThread.currentProcessName();
|
||||
if (processName != null) {
|
||||
message.append("Process: ").append(processName).append(", ");
|
||||
}
|
||||
message.append("PID: ").append(Process.myPid());
|
||||
Clog_e(TAG, message.toString(), e);
|
||||
logUncaught(t.getName(), ActivityThread.currentProcessName(), Process.myPid(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ namespace {
|
||||
|
||||
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 @@ jint attach(JavaVM* vm, char* options, void* reserved ATTRIBUTE_UNUSED) {
|
||||
for (const std::string& c : config) {
|
||||
if (c == "native_crash") {
|
||||
gForkCrash = true;
|
||||
} else if (c == "java_crash") {
|
||||
gJavaCrash = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,6 +408,11 @@ jboolean JNICALL Java_com_android_lock_1checker_LockHook_getNativeHandlingConfig
|
||||
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) {
|
||||
|
||||
@@ -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.os.Process;
|
||||
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 @@ public class LockHook {
|
||||
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 @@ public class LockHook {
|
||||
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 @@ public class LockHook {
|
||||
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 @@ public class LockHook {
|
||||
}
|
||||
|
||||
interface Violation {
|
||||
Throwable getException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,6 +228,8 @@ class OnThreadLockChecker implements LockHook.LockChecker {
|
||||
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 @@ class OnThreadLockChecker implements LockHook.LockChecker {
|
||||
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 @@ class OnThreadLockChecker implements LockHook.LockChecker {
|
||||
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 @@ class OnThreadLockChecker implements LockHook.LockChecker {
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user