Loading core/java/com/android/internal/os/RuntimeInit.java +14 −11 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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); } } } Loading tools/lock_agent/Android.bp +4 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ cc_library { include_dirs: [ // NDK headers aren't available in platform NDK builds. "libnativehelper/include_jni", // Use ScopedUtfChars. "libnativehelper/header_only_include", ], header_libs: [ "libopenjdkjvmti_headers", Loading @@ -33,6 +35,8 @@ cc_binary_host { include_dirs: [ // NDK headers aren't available in platform NDK builds. "libnativehelper/include_jni", // Use ScopedUtfChars. "libnativehelper/header_only_include", ], header_libs: [ "libopenjdkjvmti_headers", Loading tools/lock_agent/agent.cpp +66 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ #include <memory> #include <sstream> #include <unistd.h> #include <jni.h> #include <jvmti.h> Loading @@ -26,10 +28,14 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/macros.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/wait.h> #include <nativehelper/scoped_utf_chars.h> // We need dladdr. #if !defined(__APPLE__) && !defined(_WIN32) Loading Loading @@ -61,6 +67,8 @@ 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;") Loading Loading @@ -372,7 +380,7 @@ void prepareHook(jvmtiEnv* env) { } } jint attach(JavaVM* vm, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE_UNUSED) { jint attach(JavaVM* vm, char* options, void* reserved ATTRIBUTE_UNUSED) { gJavaVM = vm; jvmtiEnv* env; Loading @@ -383,9 +391,66 @@ jint attach(JavaVM* vm, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE prepareHook(env); std::vector<std::string> config = android::base::Split(options, ","); for (const std::string& c : config) { if (c == "native_crash") { gForkCrash = true; } else if (c == "java_crash") { gJavaCrash = true; } } return JVMTI_ERROR_NONE; } extern "C" JNIEXPORT jboolean JNICALL Java_com_android_lock_1checker_LockHook_getNativeHandlingConfig(JNIEnv*, jclass) { 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) { return; } // Create a native crash with the given message. Decouple from the current crash to create a // tombstone but continue on. // // TODO: Once there are not so many reports, consider making this fatal for the calling process. ScopedUtfChars utf(env, msg); if (utf.c_str() == nullptr) { return; } const char* args[] = { "/system/bin/lockagent_crasher", utf.c_str(), nullptr }; pid_t pid = fork(); if (pid < 0) { return; } if (pid == 0) { // Double fork so we return quickly. Leave init to deal with the zombie. pid_t pid2 = fork(); if (pid2 == 0) { execv(args[0], const_cast<char* const*>(args)); _exit(1); __builtin_unreachable(); } _exit(0); __builtin_unreachable(); } int status; waitpid(pid, &status, 0); // Ignore any results. } extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) { return attach(vm, options, reserved); } Loading tools/lock_agent/java/com/android/lock_checker/LockHook.java +23 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -72,14 +74,23 @@ 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); sHandlerThread.start(); sHandler = new WtfHandler(sHandlerThread.getLooper()); 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) { final String stacktraceHash = hasher.stacktraceHash(st, from, to); Loading Loading @@ -175,7 +186,18 @@ public class LockHook { private static void handleViolation(Violation v) { String msg = v.toString(); Log.wtf(TAG, msg); 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); /** * Generates a hash for a given stacktrace of a {@link Throwable}. Loading Loading @@ -297,5 +319,6 @@ public class LockHook { } interface Violation { Throwable getException(); } } tools/lock_agent/java/com/android/lock_checker/OnThreadLockChecker.java +25 −2 Original line number Diff line number Diff line Loading @@ -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(); Loading Loading @@ -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"); Loading @@ -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 "); Loading @@ -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(); Loading Loading
core/java/com/android/internal/os/RuntimeInit.java +14 −11 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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); } } } Loading
tools/lock_agent/Android.bp +4 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ cc_library { include_dirs: [ // NDK headers aren't available in platform NDK builds. "libnativehelper/include_jni", // Use ScopedUtfChars. "libnativehelper/header_only_include", ], header_libs: [ "libopenjdkjvmti_headers", Loading @@ -33,6 +35,8 @@ cc_binary_host { include_dirs: [ // NDK headers aren't available in platform NDK builds. "libnativehelper/include_jni", // Use ScopedUtfChars. "libnativehelper/header_only_include", ], header_libs: [ "libopenjdkjvmti_headers", Loading
tools/lock_agent/agent.cpp +66 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ #include <memory> #include <sstream> #include <unistd.h> #include <jni.h> #include <jvmti.h> Loading @@ -26,10 +28,14 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/macros.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/wait.h> #include <nativehelper/scoped_utf_chars.h> // We need dladdr. #if !defined(__APPLE__) && !defined(_WIN32) Loading Loading @@ -61,6 +67,8 @@ 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;") Loading Loading @@ -372,7 +380,7 @@ void prepareHook(jvmtiEnv* env) { } } jint attach(JavaVM* vm, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE_UNUSED) { jint attach(JavaVM* vm, char* options, void* reserved ATTRIBUTE_UNUSED) { gJavaVM = vm; jvmtiEnv* env; Loading @@ -383,9 +391,66 @@ jint attach(JavaVM* vm, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE prepareHook(env); std::vector<std::string> config = android::base::Split(options, ","); for (const std::string& c : config) { if (c == "native_crash") { gForkCrash = true; } else if (c == "java_crash") { gJavaCrash = true; } } return JVMTI_ERROR_NONE; } extern "C" JNIEXPORT jboolean JNICALL Java_com_android_lock_1checker_LockHook_getNativeHandlingConfig(JNIEnv*, jclass) { 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) { return; } // Create a native crash with the given message. Decouple from the current crash to create a // tombstone but continue on. // // TODO: Once there are not so many reports, consider making this fatal for the calling process. ScopedUtfChars utf(env, msg); if (utf.c_str() == nullptr) { return; } const char* args[] = { "/system/bin/lockagent_crasher", utf.c_str(), nullptr }; pid_t pid = fork(); if (pid < 0) { return; } if (pid == 0) { // Double fork so we return quickly. Leave init to deal with the zombie. pid_t pid2 = fork(); if (pid2 == 0) { execv(args[0], const_cast<char* const*>(args)); _exit(1); __builtin_unreachable(); } _exit(0); __builtin_unreachable(); } int status; waitpid(pid, &status, 0); // Ignore any results. } extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) { return attach(vm, options, reserved); } Loading
tools/lock_agent/java/com/android/lock_checker/LockHook.java +23 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -72,14 +74,23 @@ 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); sHandlerThread.start(); sHandler = new WtfHandler(sHandlerThread.getLooper()); 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) { final String stacktraceHash = hasher.stacktraceHash(st, from, to); Loading Loading @@ -175,7 +186,18 @@ public class LockHook { private static void handleViolation(Violation v) { String msg = v.toString(); Log.wtf(TAG, msg); 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); /** * Generates a hash for a given stacktrace of a {@link Throwable}. Loading Loading @@ -297,5 +319,6 @@ public class LockHook { } interface Violation { Throwable getException(); } }
tools/lock_agent/java/com/android/lock_checker/OnThreadLockChecker.java +25 −2 Original line number Diff line number Diff line Loading @@ -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(); Loading Loading @@ -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"); Loading @@ -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 "); Loading @@ -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(); Loading