Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4507201d authored by Andreas Gampe's avatar Andreas Gampe Committed by Gerrit Code Review
Browse files

Merge changes Ie70d0155,I0692a91d,I8c5041a4

* changes:
  LockAgent: Add agent parameters to start_lock_agent script
  LockAgent: Add option to synthesize Java crash logging
  LockAgent: Add ability to generate a native crash
parents 3fdedfab f52b970c
Loading
Loading
Loading
Loading
+14 −11
Original line number Original line Diff line number Diff line
@@ -64,6 +64,19 @@ public class RuntimeInit {
        return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr);
        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
     * Logs a message when a thread encounters an uncaught exception. By
     * default, {@link KillApplicationHandler} will terminate this process later,
     * default, {@link KillApplicationHandler} will terminate this process later,
@@ -85,17 +98,7 @@ public class RuntimeInit {
            if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
            if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
                Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
                Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
            } else {
            } else {
                StringBuilder message = new StringBuilder();
                logUncaught(t.getName(), ActivityThread.currentProcessName(), Process.myPid(), e);
                // 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);
            }
            }
        }
        }
    }
    }
+4 −0
Original line number Original line Diff line number Diff line
@@ -15,6 +15,8 @@ cc_library {
    include_dirs: [
    include_dirs: [
        // NDK headers aren't available in platform NDK builds.
        // NDK headers aren't available in platform NDK builds.
        "libnativehelper/include_jni",
        "libnativehelper/include_jni",
        // Use ScopedUtfChars.
        "libnativehelper/header_only_include",
    ],
    ],
    header_libs: [
    header_libs: [
        "libopenjdkjvmti_headers",
        "libopenjdkjvmti_headers",
@@ -33,6 +35,8 @@ cc_binary_host {
    include_dirs: [
    include_dirs: [
        // NDK headers aren't available in platform NDK builds.
        // NDK headers aren't available in platform NDK builds.
        "libnativehelper/include_jni",
        "libnativehelper/include_jni",
        // Use ScopedUtfChars.
        "libnativehelper/header_only_include",
    ],
    ],
    header_libs: [
    header_libs: [
        "libopenjdkjvmti_headers",
        "libopenjdkjvmti_headers",
+66 −1
Original line number Original line Diff line number Diff line
@@ -19,6 +19,8 @@
#include <memory>
#include <memory>
#include <sstream>
#include <sstream>


#include <unistd.h>

#include <jni.h>
#include <jni.h>


#include <jvmti.h>
#include <jvmti.h>
@@ -26,10 +28,14 @@
#include <android-base/file.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/macros.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <android-base/unique_fd.h>


#include <fcntl.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/stat.h>
#include <sys/wait.h>

#include <nativehelper/scoped_utf_chars.h>


// We need dladdr.
// We need dladdr.
#if !defined(__APPLE__) && !defined(_WIN32)
#if !defined(__APPLE__) && !defined(_WIN32)
@@ -61,6 +67,8 @@
namespace {
namespace {


JavaVM* gJavaVM = nullptr;
JavaVM* gJavaVM = nullptr;
bool gForkCrash = false;
bool gJavaCrash = false;


// Converts a class name to a type descriptor
// Converts a class name to a type descriptor
// (ex. "java.lang.String" to "Ljava/lang/String;")
// (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;
    gJavaVM = vm;


    jvmtiEnv* env;
    jvmtiEnv* env;
@@ -383,9 +391,66 @@ jint attach(JavaVM* vm, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE


    prepareHook(env);
    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;
    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) {
extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
    return attach(vm, options, reserved);
    return attach(vm, options, reserved);
}
}
+23 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.lock_checker;
package com.android.lock_checker;


import android.app.ActivityThread;
import android.os.Handler;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Looper;
@@ -24,6 +25,7 @@ import android.os.Process;
import android.util.Log;
import android.util.Log;
import android.util.LogWriter;
import android.util.LogWriter;


import com.android.internal.os.RuntimeInit;
import com.android.internal.os.SomeArgs;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.StatLogger;
import com.android.internal.util.StatLogger;


@@ -72,14 +74,23 @@ public class LockHook {


    private static final LockChecker[] sCheckers;
    private static final LockChecker[] sCheckers;


    private static boolean sNativeHandling = false;
    private static boolean sSimulateCrash = false;

    static {
    static {
        sHandlerThread = new HandlerThread("LockHook:wtf", Process.THREAD_PRIORITY_BACKGROUND);
        sHandlerThread = new HandlerThread("LockHook:wtf", Process.THREAD_PRIORITY_BACKGROUND);
        sHandlerThread.start();
        sHandlerThread.start();
        sHandler = new WtfHandler(sHandlerThread.getLooper());
        sHandler = new WtfHandler(sHandlerThread.getLooper());


        sCheckers = new LockChecker[] { new OnThreadLockChecker() };
        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,
    static <T> boolean shouldDumpStacktrace(StacktraceHasher hasher, Map<String, T> dumpedSet,
            T val, AnnotatedStackTraceElement[] st, int from, int to) {
            T val, AnnotatedStackTraceElement[] st, int from, int to) {
        final String stacktraceHash = hasher.stacktraceHash(st, from, to);
        final String stacktraceHash = hasher.stacktraceHash(st, from, to);
@@ -175,7 +186,18 @@ public class LockHook {
    private static void handleViolation(Violation v) {
    private static void handleViolation(Violation v) {
        String msg = v.toString();
        String msg = v.toString();
        Log.wtf(TAG, msg);
        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}.
     * Generates a hash for a given stacktrace of a {@link Throwable}.
@@ -297,5 +319,6 @@ public class LockHook {
    }
    }


    interface Violation {
    interface Violation {
        Throwable getException();
    }
    }
}
}
+25 −2
Original line number Original line Diff line number Diff line
@@ -228,6 +228,8 @@ class OnThreadLockChecker implements LockHook.LockChecker {
        AnnotatedStackTraceElement[] mStack;
        AnnotatedStackTraceElement[] mStack;
        OrderData mOppositeData;
        OrderData mOppositeData;


        private static final int STACK_OFFSET = 4;

        Violation(Thread self, Object alreadyHeld, Object lock,
        Violation(Thread self, Object alreadyHeld, Object lock,
                AnnotatedStackTraceElement[] stack, OrderData oppositeData) {
                AnnotatedStackTraceElement[] stack, OrderData oppositeData) {
            this.mSelfTid = (int) self.getId();
            this.mSelfTid = (int) self.getId();
@@ -284,6 +286,26 @@ class OnThreadLockChecker implements LockHook.LockChecker {
                    lock.getClass().getName());
                    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() {
        public String toString() {
            StringBuilder sb = new StringBuilder();
            StringBuilder sb = new StringBuilder();
            sb.append("Lock inversion detected!\n");
            sb.append("Lock inversion detected!\n");
@@ -294,7 +316,7 @@ class OnThreadLockChecker implements LockHook.LockChecker {
            sb.append(" on thread ").append(mOppositeData.mTid).append(" (")
            sb.append(" on thread ").append(mOppositeData.mTid).append(" (")
                    .append(mOppositeData.mThreadName).append(")");
                    .append(mOppositeData.mThreadName).append(")");
            sb.append(" at:\n");
            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)
                    describeLocking(mAlreadyHeld, "will lock"), getTo(mOppositeData.mStack, mLock)
                    + 1, "    | "));
                    + 1, "    | "));
            sb.append("  Locking ");
            sb.append("  Locking ");
@@ -303,7 +325,8 @@ class OnThreadLockChecker implements LockHook.LockChecker {
            sb.append(describeLock(mLock));
            sb.append(describeLock(mLock));
            sb.append(" on thread ").append(mSelfTid).append(" (").append(mSelfName).append(")");
            sb.append(" on thread ").append(mSelfTid).append(" (").append(mSelfName).append(")");
            sb.append(" at:\n");
            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, "    | "));
                    getTo(mStack, mAlreadyHeld) + 1, "    | "));


            return sb.toString();
            return sb.toString();
Loading