diff options
author | Mark Salyzyn <salyzyn@google.com> | 2016-02-05 15:33:17 -0800 |
---|---|---|
committer | Mark Salyzyn <salyzyn@google.com> | 2016-02-09 07:38:11 -0800 |
commit | 0751efa64cddfab69749236193e776058c9db595 (patch) | |
tree | 0b52f09c101c1964a7f24da718ad53d88b32a3ae | |
parent | 68116161c74676ae6f8577e5154aca774657ec60 (diff) | |
download | native-0751efa64cddfab69749236193e776058c9db595.tar.gz |
dumpstate: report process times
- Fix per-pid helper to pick up comm if cmdline is empty
and assume kernel thread.
- Add per pid report command line user time if not kernel thread,
system time and I/O time, plus percentage if non zero.
Bug: 26966375
Change-Id: I6b499f535cc3687eb468b32ae39e7d151dffb11f
-rw-r--r-- | cmds/dumpstate/Android.mk | 2 | ||||
-rw-r--r-- | cmds/dumpstate/dumpstate.cpp | 1 | ||||
-rw-r--r-- | cmds/dumpstate/dumpstate.h | 3 | ||||
-rw-r--r-- | cmds/dumpstate/utils.cpp | 141 |
4 files changed, 135 insertions, 12 deletions
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk index 6442701c29..b0cecdd9de 100644 --- a/cmds/dumpstate/Android.mk +++ b/cmds/dumpstate/Android.mk @@ -16,7 +16,7 @@ LOCAL_MODULE := dumpstate LOCAL_SHARED_LIBRARIES := libcutils liblog libselinux LOCAL_HAL_STATIC_LIBRARIES := libdumpstate -LOCAL_CFLAGS += -Wall -Wno-unused-parameter -std=gnu99 +LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter -std=gnu99 LOCAL_INIT_RC := dumpstate.rc include $(BUILD_EXECUTABLE) diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 808d0c27df..792d7e8ca7 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -437,6 +437,7 @@ static void dumpstate() { run_command("LIST OF OPEN FILES", 10, SU_PATH, "root", "lsof", NULL); for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES"); for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS"); + for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)"); if (screenshot_path[0]) { ALOGI("taking screenshot\n"); diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index fb66065446..3063ec2867 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -90,6 +90,9 @@ void for_each_tid(for_each_tid_func func, const char *header); /* Displays a blocked processes in-kernel wait channel */ void show_wchan(int pid, int tid, const char *name); +/* Displays a processes times */ +void show_showtime(int pid, const char *name); + /* Runs "showmap" for a process */ void do_showmap(int pid, const char *name); diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp index 006b14b56a..0b1fa43fa4 100644 --- a/cmds/dumpstate/utils.cpp +++ b/cmds/dumpstate/utils.cpp @@ -26,6 +26,7 @@ #include <string.h> #include <sys/inotify.h> #include <sys/stat.h> +#include <sys/sysconf.h> #include <sys/time.h> #include <sys/wait.h> #include <sys/klog.h> @@ -103,13 +104,32 @@ static void __for_each_pid(void (*helper)(int, const char *, void *), const char continue; } - sprintf(cmdpath,"/proc/%d/cmdline", pid); memset(cmdline, 0, sizeof(cmdline)); - if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) < 0) { - strcpy(cmdline, "N/A"); - } else { - read(fd, cmdline, sizeof(cmdline) - 1); + + snprintf(cmdpath, sizeof(cmdpath), "/proc/%d/cmdline", pid); + if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) >= 0) { + TEMP_FAILURE_RETRY(read(fd, cmdline, sizeof(cmdline) - 2)); + close(fd); + if (cmdline[0]) { + helper(pid, cmdline, arg); + continue; + } + } + + // if no cmdline, a kernel thread has comm + snprintf(cmdpath, sizeof(cmdpath), "/proc/%d/comm", pid); + if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) >= 0) { + TEMP_FAILURE_RETRY(read(fd, cmdline + 1, sizeof(cmdline) - 4)); close(fd); + if (cmdline[1]) { + cmdline[0] = '['; + size_t len = strcspn(cmdline, "\f\b\r\n"); + cmdline[len] = ']'; + cmdline[len+1] = '\0'; + } + } + if (!cmdline[0]) { + strcpy(cmdline, "N/A"); } helper(pid, cmdline, arg); } @@ -161,7 +181,7 @@ static void for_each_tid_helper(int pid, const char *cmdline, void *arg) { strcpy(comm, "N/A"); } else { char *c; - read(fd, comm, sizeof(comm) - 1); + TEMP_FAILURE_RETRY(read(fd, comm, sizeof(comm) - 2)); close(fd); c = strrchr(comm, '\n'); @@ -184,7 +204,7 @@ void show_wchan(int pid, int tid, const char *name) { ON_DRY_RUN_RETURN(); char path[255]; char buffer[255]; - int fd; + int fd, ret, save_errno; char name_buffer[255]; memset(buffer, 0, sizeof(buffer)); @@ -195,9 +215,13 @@ void show_wchan(int pid, int tid, const char *name) { return; } - if (read(fd, buffer, sizeof(buffer)) < 0) { - printf("Failed to read '%s' (%s)\n", path, strerror(errno)); - goto out_close; + ret = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); + save_errno = errno; + close(fd); + + if (ret < 0) { + printf("Failed to read '%s' (%s)\n", path, strerror(save_errno)); + return; } snprintf(name_buffer, sizeof(name_buffer), "%*s%s", @@ -205,8 +229,103 @@ void show_wchan(int pid, int tid, const char *name) { printf("%-7d %-32s %s\n", tid, name_buffer, buffer); -out_close: + return; +} + +// print time in centiseconds +static void snprcent(char *buffer, size_t len, size_t spc, + unsigned long long time) { + static long hz; // cache discovered hz + + if (hz <= 0) { + hz = sysconf(_SC_CLK_TCK); + if (hz <= 0) { + hz = 1000; + } + } + + // convert to centiseconds + time = (time * 100 + (hz / 2)) / hz; + + char str[16]; + + snprintf(str, sizeof(str), " %llu.%02u", + time / 100, (unsigned)(time % 100)); + size_t offset = strlen(buffer); + snprintf(buffer + offset, (len > offset) ? len - offset : 0, + "%*s", (spc > offset) ? (int)(spc - offset) : 0, str); +} + +// print permille as a percent +static void snprdec(char *buffer, size_t len, size_t spc, unsigned permille) { + char str[16]; + + snprintf(str, sizeof(str), " %u.%u%%", permille / 10, permille % 10); + size_t offset = strlen(buffer); + snprintf(buffer + offset, (len > offset) ? len - offset : 0, + "%*s", (spc > offset) ? (int)(spc - offset) : 0, str); +} + +void show_showtime(int pid, const char *name) { + ON_DRY_RUN_RETURN(); + char path[255]; + char buffer[1023]; + int fd, ret, save_errno; + + memset(buffer, 0, sizeof(buffer)); + + sprintf(path, "/proc/%d/stat", pid); + if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))) < 0) { + printf("Failed to open '%s' (%s)\n", path, strerror(errno)); + return; + } + + ret = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); + save_errno = errno; close(fd); + + if (ret < 0) { + printf("Failed to read '%s' (%s)\n", path, strerror(save_errno)); + return; + } + + // field 14 is utime + // field 15 is stime + // field 42 is iotime + unsigned long long utime = 0, stime = 0, iotime = 0; + if (sscanf(buffer, + "%*llu %*s %*s %*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld " + "%*lld %*lld %llu %llu %*lld %*lld %*lld %*lld %*lld %*lld " + "%*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld " + "%*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld %llu ", + &utime, &stime, &iotime) != 3) { + return; + } + + unsigned long long total = utime + stime; + if (!total) { + return; + } + + unsigned permille = (iotime * 1000 + (total / 2)) / total; + if (permille > 1000) { + permille = 1000; + } + + // try to beautify and stabilize columns at <80 characters + snprintf(buffer, sizeof(buffer), "%-6d%s", pid, name); + if ((name[0] != '[') || utime) { + snprcent(buffer, sizeof(buffer), 57, utime); + } + snprcent(buffer, sizeof(buffer), 65, stime); + if ((name[0] != '[') || iotime) { + snprcent(buffer, sizeof(buffer), 73, iotime); + } + if (iotime) { + snprdec(buffer, sizeof(buffer), 79, permille); + } + puts(buffer); // adds a trailing newline + return; } |