diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 98fe06a20a68c..591cd0efbecd7 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -1325,6 +1325,13 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo } } + /** + * Have the stack traces of the given native process dumped to the + * specified file. Will be appended to the file. + * @hide + */ + public static native void dumpNativeBacktraceToFile(int pid, String file); + /** * Return a String describing the calling method and location at a particular stack depth. * @param callStack the Thread stack diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 18fd3cbad52ac..8eaeb1d5f9d04 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -933,6 +933,9 @@ public class Process { public static final native boolean parseProcLine(byte[] buffer, int startIndex, int endIndex, int[] format, String[] outStrings, long[] outLongs, float[] outFloats); + /** @hide */ + public static final native int[] getPidsForCommands(String[] cmds); + /** * Gets the total Pss value for a given process, in bytes. * diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 9586717218a0a..6724f367faef8 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -17,8 +17,11 @@ #define LOG_TAG "android.os.Debug" #include "JNIHelp.h" #include "jni.h" +#include #include "utils/misc.h" +#include "cutils/debugger.h" +#include #include #include #include @@ -538,6 +541,35 @@ static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz, } +static void android_os_Debug_dumpNativeBacktraceToFile(JNIEnv* env, jobject clazz, + jint pid, jstring fileName) +{ + if (fileName == NULL) { + jniThrowNullPointerException(env, NULL); + return; + } + const jchar* str = env->GetStringCritical(fileName, 0); + String8 fileName8; + if (str) { + fileName8 = String8(str, env->GetStringLength(fileName)); + env->ReleaseStringCritical(fileName, str); + } + + int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW, 0666); /* -rw-rw-rw- */ + if (fd < 0) { + fprintf(stderr, "Can't open %s: %s\n", fileName8.string(), strerror(errno)); + return; + } + + if (lseek(fd, 0, SEEK_END) < 0) { + fprintf(stderr, "lseek: %s\n", strerror(errno)); + } else { + dump_backtrace_to_file(pid, fd); + } + + close(fd); +} + /* * JNI registration. */ @@ -569,6 +601,8 @@ static JNINativeMethod gMethods[] = { (void*)android_os_Debug_getProxyObjectCount }, { "getBinderDeathObjectCount", "()I", (void*)android_os_Debug_getDeathObjectCount }, + { "dumpNativeBacktraceToFile", "(ILjava/lang/String;)V", + (void*)android_os_Debug_dumpNativeBacktraceToFile }, }; int register_android_os_Debug(JNIEnv *env) diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 027ed16381002..0290857c97617 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -892,6 +892,93 @@ static jlong android_os_Process_getPss(JNIEnv* env, jobject clazz, jint pid) return pss * 1024; } +jintArray android_os_Process_getPidsForCommands(JNIEnv* env, jobject clazz, + jobjectArray commandNames) +{ + if (commandNames == NULL) { + jniThrowNullPointerException(env, NULL); + return NULL; + } + + Vector commands; + + jsize count = env->GetArrayLength(commandNames); + + for (int i=0; iGetObjectArrayElement(commandNames, i); + if (obj != NULL) { + const char* str8 = env->GetStringUTFChars((jstring)obj, NULL); + if (str8 == NULL) { + jniThrowNullPointerException(env, "Element in commandNames"); + return NULL; + } + commands.add(String8(str8)); + env->ReleaseStringUTFChars((jstring)obj, str8); + } else { + jniThrowNullPointerException(env, "Element in commandNames"); + return NULL; + } + } + + Vector pids; + + DIR *proc = opendir("/proc"); + if (proc == NULL) { + fprintf(stderr, "/proc: %s\n", strerror(errno)); + return NULL; + } + + struct dirent *d; + while ((d = readdir(proc))) { + int pid = atoi(d->d_name); + if (pid <= 0) continue; + + char path[PATH_MAX]; + char data[PATH_MAX]; + snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); + + int fd = open(path, O_RDONLY); + if (fd < 0) { + continue; + } + const int len = read(fd, data, sizeof(data)-1); + close(fd); + + if (len < 0) { + continue; + } + data[len] = 0; + + for (int i=0; iNewIntArray(pids.size()); + if (pidArray == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return NULL; + } + + if (pids.size() > 0) { + env->SetIntArrayRegion(pidArray, 0, pids.size(), pids.array()); + } + + return pidArray; +} + static const JNINativeMethod methods[] = { {"myPid", "()I", (void*)android_os_Process_myPid}, {"myTid", "()I", (void*)android_os_Process_myTid}, @@ -919,6 +1006,7 @@ static const JNINativeMethod methods[] = { {"parseProcLine", "([BII[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_parseProcLine}, {"getElapsedCpuTime", "()J", (void*)android_os_Process_getElapsedCpuTime}, {"getPss", "(I)J", (void*)android_os_Process_getPss}, + {"getPidsForCommands", "([Ljava/lang/String;)[I", (void*)android_os_Process_getPidsForCommands}, //{"setApplicationObject", "(Landroid/os/IBinder;)V", (void*)android_os_Process_setApplicationObject}, }; diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java index 728fb26895896..c2393823bf719 100644 --- a/services/java/com/android/server/Watchdog.java +++ b/services/java/com/android/server/Watchdog.java @@ -67,6 +67,12 @@ public class Watchdog extends Thread { static final String REBOOT_ACTION = "com.android.service.Watchdog.REBOOT"; + static final String[] NATIVE_STACKS_OF_INTEREST = new String[] { + "/system/bin/mediaserver", + "/system/bin/sdcard", + "/system/bin/surfaceflinger" + }; + static Watchdog sWatchdog; /* This handler will be used to post message back onto the main thread */ @@ -414,7 +420,8 @@ public class Watchdog extends Thread { // trace and wait another half. ArrayList pids = new ArrayList(); pids.add(Process.myPid()); - ActivityManagerService.dumpStackTraces(true, pids, null, null); + ActivityManagerService.dumpStackTraces(true, pids, null, null, + NATIVE_STACKS_OF_INTEREST); waitedHalf = true; continue; } @@ -434,7 +441,7 @@ public class Watchdog extends Thread { // Pass !waitedHalf so that just in case we somehow wind up here without having // dumped the halfway stacks, we properly re-initialize the trace file. final File stack = ActivityManagerService.dumpStackTraces( - !waitedHalf, pids, null, null); + !waitedHalf, pids, null, null, NATIVE_STACKS_OF_INTEREST); // Give some extra time to make sure the stack traces get written. // The system's been hanging for a minute, another second or two won't hurt much. diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 63d6fa3b783d8..63455ee0163c6 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -3019,10 +3019,11 @@ public final class ActivityManagerService extends ActivityManagerNative * appended to any existing file content. * @param firstPids of dalvik VM processes to dump stack traces for first * @param lastPids of dalvik VM processes to dump stack traces for last + * @param nativeProcs optional list of native process names to dump stack crawls * @return file containing stack traces, or null if no dump file is configured */ public static File dumpStackTraces(boolean clearTraces, ArrayList firstPids, - ProcessStats processStats, SparseArray lastPids) { + ProcessStats processStats, SparseArray lastPids, String[] nativeProcs) { String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null); if (tracesPath == null || tracesPath.length() == 0) { return null; @@ -3042,12 +3043,12 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } - dumpStackTraces(tracesPath, firstPids, processStats, lastPids); + dumpStackTraces(tracesPath, firstPids, processStats, lastPids, nativeProcs); return tracesFile; } private static void dumpStackTraces(String tracesPath, ArrayList firstPids, - ProcessStats processStats, SparseArray lastPids) { + ProcessStats processStats, SparseArray lastPids, String[] nativeProcs) { // Use a FileObserver to detect when traces finish writing. // The order of traces is considered important to maintain for legibility. FileObserver observer = new FileObserver(tracesPath, FileObserver.CLOSE_WRITE) { @@ -3108,6 +3109,15 @@ public final class ActivityManagerService extends ActivityManagerNative } finally { observer.stopWatching(); } + + if (nativeProcs != null) { + int[] pids = Process.getPidsForCommands(nativeProcs); + if (pids != null) { + for (int pid : pids) { + Debug.dumpNativeBacktraceToFile(pid, tracesPath); + } + } + } } final void logAppTooSlow(ProcessRecord app, long startTime, String msg) { @@ -3156,7 +3166,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (app != null) { ArrayList firstPids = new ArrayList(); firstPids.add(app.pid); - dumpStackTraces(tracesPath, firstPids, null, null); + dumpStackTraces(tracesPath, firstPids, null, null, null); } File lastTracesFile = null; @@ -3264,7 +3274,7 @@ public final class ActivityManagerService extends ActivityManagerNative final ProcessStats processStats = new ProcessStats(true); - File tracesFile = dumpStackTraces(true, firstPids, processStats, lastPids); + File tracesFile = dumpStackTraces(true, firstPids, processStats, lastPids, null); String cpuInfo = null; if (MONITOR_CPU_USAGE) {