summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java25
-rw-r--r--tools/lock_agent/Android.bp4
-rw-r--r--tools/lock_agent/agent.cpp67
-rw-r--r--tools/lock_agent/java/com/android/lock_checker/LockHook.java23
-rw-r--r--tools/lock_agent/java/com/android/lock_checker/OnThreadLockChecker.java27
-rwxr-xr-xtools/lock_agent/start_with_lockagent.sh10
6 files changed, 141 insertions, 15 deletions
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index eac150dbd320..1de2e7272f4d 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -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);
}
}
}
diff --git a/tools/lock_agent/Android.bp b/tools/lock_agent/Android.bp
index 2125ad682bb6..408946b28836 100644
--- a/tools/lock_agent/Android.bp
+++ b/tools/lock_agent/Android.bp
@@ -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",
@@ -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",
diff --git a/tools/lock_agent/agent.cpp b/tools/lock_agent/agent.cpp
index 59bfa2bf849b..c639427e645e 100644
--- a/tools/lock_agent/agent.cpp
+++ b/tools/lock_agent/agent.cpp
@@ -19,6 +19,8 @@
#include <memory>
#include <sstream>
+#include <unistd.h>
+
#include <jni.h>
#include <jvmti.h>
@@ -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)
@@ -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;")
@@ -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;
@@ -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);
}
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 efab1d217225..35c75cbbae7c 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.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;
@@ -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);
@@ -175,8 +186,19 @@ 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}.
*/
@@ -297,5 +319,6 @@ public class LockHook {
}
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 e4e721156b2b..e74ccf9d5069 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 @@ 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();
diff --git a/tools/lock_agent/start_with_lockagent.sh b/tools/lock_agent/start_with_lockagent.sh
index 953922230a11..70ed5c5e1dd7 100755
--- a/tools/lock_agent/start_with_lockagent.sh
+++ b/tools/lock_agent/start_with_lockagent.sh
@@ -1,5 +1,13 @@
#!/system/bin/sh
+
+AGENT_OPTIONS=
+if [[ "$1" == --agent-options ]] ; then
+ shift
+ AGENT_OPTIONS="=$1"
+ shift
+fi
+
APP=$1
shift
-$APP -Xplugin:libopenjdkjvmti.so -agentpath:liblockagent.so $@
+$APP -Xplugin:libopenjdkjvmti.so "-agentpath:liblockagent.so$AGENT_OPTIONS" $@