dumpstate: dump all threads in show_wchan

for_each_pid only finds processes and not their threads.  Add
for_each_tid and call it for show_wchan so we can see where all
threads are blocked in the kernel.

Change-Id: Iffb59f7c2933cecf51cdd358a36e19932c2f24c7
diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c
index 8f132d5..c85690d 100644
--- a/cmds/dumpstate/utils.c
+++ b/cmds/dumpstate/utils.c
@@ -48,7 +48,7 @@
         NULL,
 };
 
-void for_each_pid(void (*func)(int, const char *), const char *header) {
+static void __for_each_pid(void (*helper)(int, const char *, void *), const char *header, void *arg) {
     DIR *d;
     struct dirent *de;
 
@@ -73,23 +73,85 @@
         if ((fd = open(cmdpath, O_RDONLY)) < 0) {
             strcpy(cmdline, "N/A");
         } else {
-            read(fd, cmdline, sizeof(cmdline));
+            read(fd, cmdline, sizeof(cmdline) - 1);
             close(fd);
         }
-        func(pid, cmdline);
+        helper(pid, cmdline, arg);
     }
 
     closedir(d);
 }
 
-void show_wchan(int pid, const char *name) {
+static void for_each_pid_helper(int pid, const char *cmdline, void *arg) {
+    for_each_pid_func *func = arg;
+    func(pid, cmdline);
+}
+
+void for_each_pid(for_each_pid_func func, const char *header) {
+    __for_each_pid(for_each_pid_helper, header, func);
+}
+
+static void for_each_tid_helper(int pid, const char *cmdline, void *arg) {
+    DIR *d;
+    struct dirent *de;
+    char taskpath[255];
+    for_each_tid_func *func = arg;
+
+    sprintf(taskpath, "/proc/%d/task", pid);
+
+    if (!(d = opendir(taskpath))) {
+        printf("Failed to open %s (%s)\n", taskpath, strerror(errno));
+        return;
+    }
+
+    func(pid, pid, cmdline);
+
+    while ((de = readdir(d))) {
+        int tid;
+        int fd;
+        char commpath[255];
+        char comm[255];
+
+        if (!(tid = atoi(de->d_name))) {
+            continue;
+        }
+
+        if (tid == pid)
+            continue;
+
+        sprintf(commpath,"/proc/%d/comm", tid);
+        memset(comm, 0, sizeof(cmdline));
+        if ((fd = open(commpath, O_RDONLY)) < 0) {
+            strcpy(comm, "N/A");
+        } else {
+            char *c;
+            read(fd, comm, sizeof(comm) - 1);
+            close(fd);
+
+            c = strrchr(comm, '\n');
+            if (c) {
+                *c = '\0';
+            }
+        }
+        func(pid, tid, comm);
+    }
+
+    closedir(d);
+}
+
+void for_each_tid(for_each_tid_func func, const char *header) {
+    __for_each_pid(for_each_tid_helper, header, func);
+}
+
+void show_wchan(int pid, int tid, const char *name) {
     char path[255];
     char buffer[255];
     int fd;
+    char name_buffer[255];
 
     memset(buffer, 0, sizeof(buffer));
 
-    sprintf(path, "/proc/%d/wchan", pid);
+    sprintf(path, "/proc/%d/wchan", tid);
     if ((fd = open(path, O_RDONLY)) < 0) {
         printf("Failed to open '%s' (%s)\n", path, strerror(errno));
         return;
@@ -100,7 +162,10 @@
         goto out_close;
     }
 
-    printf("%-7d %-32s %s\n", pid, name, buffer);
+    snprintf(name_buffer, sizeof(name_buffer), "%*s%s",
+             pid == tid ? 0 : 3, "", name);
+
+    printf("%-7d %-32s %s\n", tid, name_buffer, buffer);
 
 out_close:
     close(fd);