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

Commit ecaa7b41 authored by Christopher Tate's avatar Christopher Tate
Browse files

Watchdog now records kernel stacks when it fires

The kernel threads are appended to the usual /data/anr/traces.txt file
and dropboxed along with the usual Dalvik stack dumps.

Change-Id: I120f1f5ee54c965efe9ac0c7f40fdef56385f1fa
NOTE: this change depends on the kernel publishing /proc/$PID/stack
parent de56c27d
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -120,6 +120,7 @@ LOCAL_SRC_FILES:= \
	android_server_BluetoothService.cpp \
	android_server_BluetoothService.cpp \
	android_server_BluetoothEventLoop.cpp \
	android_server_BluetoothEventLoop.cpp \
	android_server_BluetoothA2dpService.cpp \
	android_server_BluetoothA2dpService.cpp \
	android_server_Watchdog.cpp \
	android_message_digest_sha1.cpp \
	android_message_digest_sha1.cpp \
	android_ddm_DdmHandleNativeHeap.cpp \
	android_ddm_DdmHandleNativeHeap.cpp \
	android_location_GpsLocationProvider.cpp \
	android_location_GpsLocationProvider.cpp \
+2 −0
Original line number Original line Diff line number Diff line
@@ -153,6 +153,7 @@ extern int register_android_bluetooth_ScoSocket(JNIEnv *env);
extern int register_android_server_BluetoothService(JNIEnv* env);
extern int register_android_server_BluetoothService(JNIEnv* env);
extern int register_android_server_BluetoothEventLoop(JNIEnv *env);
extern int register_android_server_BluetoothEventLoop(JNIEnv *env);
extern int register_android_server_BluetoothA2dpService(JNIEnv* env);
extern int register_android_server_BluetoothA2dpService(JNIEnv* env);
extern int register_android_server_Watchdog(JNIEnv* env);
extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env);
extern int register_android_location_GpsLocationProvider(JNIEnv* env);
extern int register_android_location_GpsLocationProvider(JNIEnv* env);
@@ -1276,6 +1277,7 @@ static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_android_server_BluetoothService),
    REG_JNI(register_android_server_BluetoothService),
    REG_JNI(register_android_server_BluetoothEventLoop),
    REG_JNI(register_android_server_BluetoothEventLoop),
    REG_JNI(register_android_server_BluetoothA2dpService),
    REG_JNI(register_android_server_BluetoothA2dpService),
    REG_JNI(register_android_server_Watchdog),
    REG_JNI(register_android_message_digest_sha1),
    REG_JNI(register_android_message_digest_sha1),
    REG_JNI(register_android_ddm_DdmHandleNativeHeap),
    REG_JNI(register_android_ddm_DdmHandleNativeHeap),
    REG_JNI(register_android_location_GpsLocationProvider),
    REG_JNI(register_android_location_GpsLocationProvider),
+111 −0
Original line number Original line Diff line number Diff line
/*
 ** Copyright 2010, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
 ** You may obtain a copy of the License at
 **
 **     http://www.apache.org/licenses/LICENSE-2.0
 **
 ** Unless required by applicable law or agreed to in writing, software
 ** distributed under the License is distributed on an "AS IS" BASIS,
 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */

#define LOG_TAG "Watchdog_N"
#include <utils/Log.h>

#include <sys/types.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>

#include "jni.h"
#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>

static void dumpOneStack(int tid, int outFd) {
    char buf[64];

    snprintf(buf, sizeof(buf), "/proc/%d/stack", tid);
    int stackFd = open(buf, O_RDONLY);
    if (stackFd >= 0) {
        // header for readability
        strncat(buf, ":\n", sizeof(buf) - strlen(buf) - 1);
        write(outFd, buf, strlen(buf));

        // copy the stack dump text
        int nBytes;
        while ((nBytes = read(stackFd, buf, sizeof(buf))) > 0) {
            write(outFd, buf, nBytes);
        }

        // footer and done
        write(outFd, "\n", 1);
        close(stackFd);
    } else {
        LOGE("Unable to open stack of tid %d : %d (%s)", tid, errno, strerror(errno));
    }
}

static void dumpKernelStacks(JNIEnv* env, jobject clazz, jstring pathStr) {
    char buf[128];
    DIR* taskdir;

    LOGI("dumpKernelStacks");
    if (!pathStr) {
        jniThrowException(env, "java/lang/IllegalArgumentException", "Null path");
        return;
    }

    const char *path = env->GetStringUTFChars(pathStr, NULL);

    int outFd = open(path, O_WRONLY | O_APPEND | O_CREAT);
    if (outFd < 0) {
        LOGE("Unable to open stack dump file: %d (%s)", errno, strerror(errno));
        goto done;
    }

    snprintf(buf, sizeof(buf), "\n----- begin pid %d kernel stacks -----\n", getpid());
    write(outFd, buf, strlen(buf));

    // look up the list of all threads in this process
    snprintf(buf, sizeof(buf), "/proc/%d/task", getpid());
    taskdir = opendir(buf);
    if (taskdir != NULL) {
        struct dirent * ent;
        while ((ent = readdir(taskdir)) != NULL) {
            int tid = atoi(ent->d_name);
            if (tid > 0 && tid <= 65535) {
                // dump each stack trace
                dumpOneStack(tid, outFd);
            }
        }
        closedir(taskdir);
    }

    snprintf(buf, sizeof(buf), "----- end pid %d kernel stacks -----\n", getpid());
    write(outFd, buf, strlen(buf));

    close(outFd);
done:
    env->ReleaseStringUTFChars(pathStr, path);
}

// ----------------------------------------

namespace android {

static const JNINativeMethod g_methods[] = {
    { "native_dumpKernelStacks", "(Ljava/lang/String;)V", (void*)dumpKernelStacks },
};

int register_android_server_Watchdog(JNIEnv* env) {
    return AndroidRuntime::registerNativeMethods(env, "com/android/server/Watchdog",
                                                 g_methods, NELEM(g_methods));
}

}
+22 −0
Original line number Original line Diff line number Diff line
@@ -39,6 +39,8 @@ import android.util.Log;
import android.util.Slog;
import android.util.Slog;


import java.io.File;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Calendar;
@@ -51,6 +53,9 @@ public class Watchdog extends Thread {
    // Set this to true to use debug default values.
    // Set this to true to use debug default values.
    static final boolean DB = false;
    static final boolean DB = false;


    // Set this to true to have the watchdog record kernel thread stacks when it fires
    static final boolean RECORD_KERNEL_THREADS = true;

    static final int MONITOR = 2718;
    static final int MONITOR = 2718;
    static final int GLOBAL_PSS = 2719;
    static final int GLOBAL_PSS = 2719;


@@ -850,6 +855,11 @@ public class Watchdog extends Thread {
            // The system's been hanging for a minute, another second or two won't hurt much.
            // The system's been hanging for a minute, another second or two won't hurt much.
            SystemClock.sleep(2000);
            SystemClock.sleep(2000);


            // Pull our own kernel thread stacks as well if we're configured for that
            if (RECORD_KERNEL_THREADS) {
                dumpKernelStackTraces();
            }

            mActivity.addErrorToDropBox("watchdog", null, null, null, name, null, stack, null);
            mActivity.addErrorToDropBox("watchdog", null, null, null, name, null, stack, null);


            // Only kill the process if the debugger is not attached.
            // Only kill the process if the debugger is not attached.
@@ -864,4 +874,16 @@ public class Watchdog extends Thread {
            waitedHalf = false;
            waitedHalf = false;
        }
        }
    }
    }

    private File dumpKernelStackTraces() {
        String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
        if (tracesPath == null || tracesPath.length() == 0) {
            return null;
        }

        native_dumpKernelStacks(tracesPath);
        return new File(tracesPath);
    }

    private native void native_dumpKernelStacks(String tracesPath);
}
}