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/Ddm.c b/vm/Ddm.c
index 774ed3f..3cd3a80 100644
--- a/vm/Ddm.c
+++ b/vm/Ddm.c
@@ -364,95 +364,6 @@
}
/*
- * 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.
- */
-static bool getThreadStats(pid_t pid, pid_t tid, unsigned long* pUtime,
- unsigned long* pStime)
-{
- /*
- 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);
- */
-
- 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/%d/task/%d/stat", (int) pid, (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;
- 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;
- }
- lineBuf[cc] = '\0';
-
- /*
- * Skip whitespace-separated tokens.
- */
- static const char* kWhitespace = " ";
- char* cp = lineBuf;
- for (i = 0; i < 13; i++) {
- cp += strcspn(cp, kWhitespace); // skip token
- cp += strspn(cp, kWhitespace); // skip whitespace
- }
-
- /*
- * Grab the values we want.
- */
- char* endp;
- *pUtime = strtoul(cp, &endp, 10);
- if (endp == cp)
- LOGI("Warning: strtoul failed on utime ('%.30s...')\n", cp);
-
- cp += strcspn(cp, kWhitespace);
- cp += strspn(cp, kWhitespace);
-
- *pStime = strtoul(cp, &endp, 10);
- if (endp == cp)
- LOGI("Warning: strtoul failed on stime ('%.30s...')\n", cp);
-
- close(fd);
- return true;
-}
-
-/*
* Generate the contents of a THST chunk. The data encompasses all known
* threads.
*
@@ -502,14 +413,13 @@
set2BE(buf+2, (u2) threadCount);
buf += kHeaderLen;
- pid_t pid = getpid();
for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
- unsigned long utime, stime;
bool isDaemon = false;
- if (!getThreadStats(pid, thread->systemTid, &utime, &stime)) {
- // failed; drop in empty values
- utime = stime = 0;
+ ProcStatData procStatData;
+ if (!dvmGetThreadStats(&procStatData, thread->systemTid)) {
+ /* failed; show zero */
+ memset(&procStatData, 0, sizeof(procStatData));
}
Object* threadObj = thread->threadObj;
@@ -521,8 +431,8 @@
set4BE(buf+0, thread->threadId);
set1(buf+4, thread->status);
set4BE(buf+5, thread->systemTid);
- set4BE(buf+9, utime);
- set4BE(buf+13, stime);
+ set4BE(buf+9, procStatData.utime);
+ set4BE(buf+13, procStatData.stime);
set1(buf+17, isDaemon);
buf += kBytesPerEntry;
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;
+}
diff --git a/vm/Misc.h b/vm/Misc.h
index 358a97b..e188705 100644
--- a/vm/Misc.h
+++ b/vm/Misc.h
@@ -306,4 +306,14 @@
*/
void *dvmAllocRegion(size_t size, int prot, const char *name);
+/*
+ * Get some per-thread stats from /proc/self/task/N/stat.
+ */
+typedef struct {
+ unsigned long utime; /* number of jiffies scheduled in user mode */
+ unsigned long stime; /* number of jiffies scheduled in kernel mode */
+ int processor; /* number of CPU that last executed thread */
+} ProcStatData;
+bool dvmGetThreadStats(ProcStatData* pData, pid_t tid);
+
#endif /*_DALVIK_MISC*/
diff --git a/vm/Thread.c b/vm/Thread.c
index 8fed98b..1836876 100644
--- a/vm/Thread.c
+++ b/vm/Thread.c
@@ -3508,7 +3508,6 @@
int policy; // pthread policy
struct sched_param sp; // pthread scheduling parameters
char schedstatBuf[64]; // contents of /proc/[pid]/task/[tid]/schedstat
- int schedstatFd;
/*
* Get the java.lang.Thread object. This function gets called from
@@ -3583,19 +3582,33 @@
thread->systemTid, getpriority(PRIO_PROCESS, thread->systemTid),
policy, sp.sched_priority, schedulerGroupBuf, (int)thread->handle);
- snprintf(schedstatBuf, sizeof(schedstatBuf), "/proc/%d/task/%d/schedstat",
- getpid(), thread->systemTid);
- schedstatFd = open(schedstatBuf, O_RDONLY);
+ /* get some bits from /proc/self/stat */
+ ProcStatData procStatData;
+ if (!dvmGetThreadStats(&procStatData, thread->systemTid)) {
+ /* failed, use zeroed values */
+ memset(&procStatData, 0, sizeof(procStatData));
+ }
+
+ /* grab the scheduler stats for this thread */
+ snprintf(schedstatBuf, sizeof(schedstatBuf), "/proc/self/task/%d/schedstat",
+ thread->systemTid);
+ int schedstatFd = open(schedstatBuf, O_RDONLY);
+ strcpy(schedstatBuf, "0 0 0"); /* show this if open/read fails */
if (schedstatFd >= 0) {
- int bytes;
+ ssize_t bytes;
bytes = read(schedstatFd, schedstatBuf, sizeof(schedstatBuf) - 1);
close(schedstatFd);
- if (bytes > 1) {
- schedstatBuf[bytes-1] = 0; // trailing newline
- dvmPrintDebugMessage(target, " | schedstat=( %s )\n", schedstatBuf);
+ if (bytes >= 1) {
+ schedstatBuf[bytes-1] = '\0'; /* remove trailing newline */
}
}
+ /* show what we got */
+ dvmPrintDebugMessage(target,
+ " | schedstat=( %s ) utm=%lu stm=%lu core=%d\n",
+ schedstatBuf, procStatData.utime, procStatData.stime,
+ procStatData.processor);
+
#ifdef WITH_MONITOR_TRACKING
if (!isRunning) {
LockedObjectData* lod = thread->pLockedObjects;