Show CPU# and utime/stime in the Dalvik thread dump.

This adds the "CPU number last executed on" from /proc/stat to
the Dalvik thread output.  This may come in handy when looking at
thread dumps.  While I was at it I added the utime/stime values,
which may not be all that useful since they represent lifetime usage
rather than recent usage.

Output appears on the schedstat line:
  | schedstat=( 2930542006 9197204583 1284 ) ut=287 st=6 core=0

The routine that parses /proc/stat, previously only used for DDMS, has
been generalized.  This also fixes a problem with parsing threads
whose name includes a space.

I also changed this and the /proc/schedstat code to use /proc/self
instead of /proc/%d + getpid().

Bug 2884342.

Change-Id: Iec85bc929005044427ebbb468bfa0c9693444bca
diff --git a/vm/Misc.c b/vm/Misc.c
index 2a1e244..b4c8a6a 100644
--- a/vm/Misc.c
+++ b/vm/Misc.c
@@ -715,3 +715,120 @@
     }
     return base;
 }
+
+
+/*
+ * Get some per-thread stats.
+ *
+ * This is currently generated by opening the appropriate "stat" file
+ * in /proc and reading the pile of stuff that comes out.
+ */
+bool dvmGetThreadStats(ProcStatData* pData, pid_t tid)
+{
+    /*
+    int pid;
+    char comm[128];
+    char state;
+    int ppid, pgrp, session, tty_nr, tpgid;
+    unsigned long flags, minflt, cminflt, majflt, cmajflt, utime, stime;
+    long cutime, cstime, priority, nice, zero, itrealvalue;
+    unsigned long starttime, vsize;
+    long rss;
+    unsigned long rlim, startcode, endcode, startstack, kstkesp, kstkeip;
+    unsigned long signal, blocked, sigignore, sigcatch, wchan, nswap, cnswap;
+    int exit_signal, processor;
+    unsigned long rt_priority, policy;
+
+    scanf("%d %s %c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld "
+          "%ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu "
+          "%lu %lu %lu %d %d %lu %lu",
+        &pid, comm, &state, &ppid, &pgrp, &session, &tty_nr, &tpgid,
+        &flags, &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime,
+        &cutime, &cstime, &priority, &nice, &zero, &itrealvalue,
+        &starttime, &vsize, &rss, &rlim, &startcode, &endcode,
+        &startstack, &kstkesp, &kstkeip, &signal, &blocked, &sigignore,
+        &sigcatch, &wchan, &nswap, &cnswap, &exit_signal, &processor,
+        &rt_priority, &policy);
+
+        (new: delayacct_blkio_ticks %llu (since Linux 2.6.18))
+    */
+
+    char nameBuf[64];
+    int i, fd;
+
+    /*
+     * Open and read the appropriate file.  This is expected to work on
+     * Linux but will fail on other platforms (e.g. Mac sim).
+     */
+    sprintf(nameBuf, "/proc/self/task/%d/stat", (int) tid);
+    fd = open(nameBuf, O_RDONLY);
+    if (fd < 0) {
+        LOGV("Unable to open '%s': %s\n", nameBuf, strerror(errno));
+        return false;
+    }
+
+    char lineBuf[512];      /* > 2x typical */
+    int cc = read(fd, lineBuf, sizeof(lineBuf)-1);
+    if (cc <= 0) {
+        const char* msg = (cc == 0) ? "unexpected EOF" : strerror(errno);
+        LOGI("Unable to read '%s': %s\n", nameBuf, msg);
+        close(fd);
+        return false;
+    }
+    close(fd);
+    lineBuf[cc] = '\0';
+
+    /*
+     * Skip whitespace-separated tokens.  For the most part we can assume
+     * that tokens do not contain spaces, and are separated by exactly one
+     * space character.  The only exception is the second field ("comm")
+     * which may contain spaces but is surrounded by parenthesis.
+     */
+    char* cp = strchr(lineBuf, ')');
+    if (cp == NULL)
+        goto parse_fail;
+    cp++;
+    for (i = 2; i < 13; i++) {
+        cp = strchr(cp+1, ' ');
+        if (cp == NULL)
+            goto parse_fail;
+    }
+
+    /*
+     * Grab utime/stime.
+     */
+    char* endp;
+    pData->utime = strtoul(cp+1, &endp, 10);
+    if (endp == cp+1)
+        LOGI("Warning: strtoul failed on utime ('%.30s...')\n", cp);
+
+    cp = strchr(cp+1, ' ');
+    if (cp == NULL)
+        goto parse_fail;
+
+    pData->stime = strtoul(cp+1, &endp, 10);
+    if (endp == cp+1)
+        LOGI("Warning: strtoul failed on stime ('%.30s...')\n", cp);
+
+    /*
+     * Skip more stuff we don't care about.
+     */
+    for (i = 14; i < 38; i++) {
+        cp = strchr(cp+1, ' ');
+        if (cp == NULL)
+            goto parse_fail;
+    }
+
+    /*
+     * Grab processor number.
+     */
+    pData->processor = strtol(cp+1, &endp, 10);
+    if (endp == cp+1)
+        LOGI("Warning: strtoul failed on processor ('%.30s...')\n", cp);
+
+    return true;
+
+parse_fail:
+    LOGI("stat parse failed (%s)\n", lineBuf);
+    return false;
+}