Include important native processes in watchdog stacks.

Helps us track down deadlocks involving native service processes.

Bug: 6615693
Change-Id: I580047550772e29586195a8cf440141574e3f40c
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 98fe06a..591cd0e 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1326,6 +1326,13 @@
     }
 
     /**
+     * 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 
      * @param depth the depth of stack to return information for.
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 18fd3cb..8eaeb1d 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -933,6 +933,9 @@
     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 9586717..6724f36 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 <utils/String8.h>
 #include "utils/misc.h"
+#include "cutils/debugger.h"
 
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -538,6 +541,35 @@
 }
 
 
+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 @@
             (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 027ed16..0290857 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -892,6 +892,93 @@
     return pss * 1024;
 }
 
+jintArray android_os_Process_getPidsForCommands(JNIEnv* env, jobject clazz,
+        jobjectArray commandNames)
+{
+    if (commandNames == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return NULL;
+    }
+
+    Vector<String8> commands;
+
+    jsize count = env->GetArrayLength(commandNames);
+
+    for (int i=0; i<count; i++) {
+        jobject obj = env->GetObjectArrayElement(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<jint> 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; i<len; i++) {
+            if (data[i] == ' ') {
+                data[i] = 0;
+                break;
+            }
+        }
+
+        for (size_t i=0; i<commands.size(); i++) {
+            if (commands[i] == data) {
+                pids.add(pid);
+                break;
+            }
+        }
+    }
+
+    closedir(proc);
+
+    jintArray pidArray = env->NewIntArray(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 @@
     {"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 728fb26..c239382 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -67,6 +67,12 @@
 
     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 @@
                     // trace and wait another half.
                     ArrayList<Integer> pids = new ArrayList<Integer>();
                     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 @@
             // 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 63d6fa3..63455ee 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3019,10 +3019,11 @@
      *    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<Integer> firstPids,
-            ProcessStats processStats, SparseArray<Boolean> lastPids) {
+            ProcessStats processStats, SparseArray<Boolean> 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 @@
             return null;
         }
 
-        dumpStackTraces(tracesPath, firstPids, processStats, lastPids);
+        dumpStackTraces(tracesPath, firstPids, processStats, lastPids, nativeProcs);
         return tracesFile;
     }
 
     private static void dumpStackTraces(String tracesPath, ArrayList<Integer> firstPids,
-            ProcessStats processStats, SparseArray<Boolean> lastPids) {
+            ProcessStats processStats, SparseArray<Boolean> 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 @@
         } 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 @@
             if (app != null) {
                 ArrayList<Integer> firstPids = new ArrayList<Integer>();
                 firstPids.add(app.pid);
-                dumpStackTraces(tracesPath, firstPids, null, null);
+                dumpStackTraces(tracesPath, firstPids, null, null, null);
             }
 
             File lastTracesFile = null;
@@ -3264,7 +3274,7 @@
 
         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) {