Chromium Code Reviews| Index: tools/android/heap_profiler/heap_dump.c |
| diff --git a/tools/android/heap_profiler/heap_dump.c b/tools/android/heap_profiler/heap_dump.c |
| index 731833ec13710e3d2df3b27a68dd1192e6d861e1..018a5d4c3d4531c67109296a95f18a4ba39ca571 100644 |
| --- a/tools/android/heap_profiler/heap_dump.c |
| +++ b/tools/android/heap_profiler/heap_dump.c |
| @@ -4,8 +4,6 @@ |
| // The client dump tool for libheap_profiler. It attaches to a process (given |
| // its pid) and dumps all the libheap_profiler tracking information in JSON. |
| -// The target process is frozen (SIGSTOP) while dumping, unless the -n opt. |
| -// is passed (in which case the caller is responsible for un/freezing). |
| // The JSON output looks like this: |
| // { |
| // "total_allocated": 908748493, # Total bytes allocated and not freed. |
| @@ -31,15 +29,16 @@ |
| // +---------------------> Index of the entry (as for "allocs" xref). |
| // Indexes are hex and might not be monotonic. |
| +#include <errno.h> |
| #include <fcntl.h> |
| #include <inttypes.h> |
| -#include <signal.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <time.h> |
| #include <unistd.h> |
| +#include <sys/ptrace.h> |
| #include <sys/stat.h> |
| #include "tools/android/heap_profiler/heap_profiler.h" |
| @@ -47,6 +46,7 @@ |
| static void lseek_abs(int fd, size_t off); |
| static void read_proc_cmdline(char* cmdline, int size); |
| +static ssize_t read_safe(int fd, void* buf, size_t count); |
| static int pid; |
| @@ -55,6 +55,7 @@ static int dump_process_heap( |
| int mem_fd, |
| FILE* fmaps, |
| bool dump_also_allocs, |
| + bool pedantic, // Enable pedantic consistency checks on memory counters. |
| char* comment) { |
| HeapStats stats; |
| time_t tm; |
| @@ -83,7 +84,14 @@ static int dump_process_heap( |
| // The mmap looks promising. Let's check for the magic marker. |
| lseek_abs(mem_fd, start); |
| - if (read(mem_fd, &stats, sizeof(stats)) < sizeof(stats)) |
| + ssize_t rsize = read_safe(mem_fd, &stats, sizeof(stats)); |
| + |
| + if (rsize == -1) { |
| + perror("read"); |
| + return -1; |
| + } |
| + |
| + if (rsize < sizeof(stats)) |
| continue; |
| if (stats.magic_start == HEAP_PROFILER_MAGIC_MARKER) { |
| @@ -120,7 +128,7 @@ static int dump_process_heap( |
| lseek_abs(mem_fd, (uintptr_t) stats.allocs); |
| for (i = 0; i < stats.max_allocs; ++i) { |
| Alloc alloc; |
| - if (read(mem_fd, &alloc, sizeof(alloc)) != sizeof(alloc)) { |
| + if (read_safe(mem_fd, &alloc, sizeof(alloc)) != sizeof(alloc)) { |
| fprintf(stderr, "ERROR: cannot read allocation table\n"); |
| perror("read"); |
| return -1; |
| @@ -150,14 +158,14 @@ static int dump_process_heap( |
| } |
| printf("},\n"); |
| - if (dbg_counted_allocs != stats.num_allocs) { |
| + if (pedantic && dbg_counted_allocs != stats.num_allocs) { |
| fprintf(stderr, |
| "ERROR: inconsistent alloc count (%"PRIu32" vs %"PRIu32").\n", |
| dbg_counted_allocs, stats.num_allocs); |
| return -1; |
| } |
| - if (dbg_counted_total_alloc_bytes != stats.total_alloc_bytes) { |
| + if (pedantic && dbg_counted_total_alloc_bytes != stats.total_alloc_bytes) { |
| fprintf(stderr, "ERROR: inconsistent alloc totals (%zu vs %zu).\n", |
| dbg_counted_total_alloc_bytes, stats.total_alloc_bytes); |
| return -1; |
| @@ -171,7 +179,7 @@ static int dump_process_heap( |
| lseek_abs(mem_fd, (uintptr_t) stats.stack_traces); |
| for (i = 0; i < stats.max_stack_traces; ++i) { |
| StacktraceEntry st; |
| - if (read(mem_fd, &st, sizeof(st)) != sizeof(st)) { |
| + if (read_safe(mem_fd, &st, sizeof(st)) != sizeof(st)) { |
| fprintf(stderr, "ERROR: cannot read stack trace table\n"); |
| perror("read"); |
| return -1; |
| @@ -201,7 +209,7 @@ static int dump_process_heap( |
| } |
| printf("}\n}\n"); |
| - if (dbg_counted_total_alloc_bytes != stats.total_alloc_bytes) { |
| + if (pedantic && dbg_counted_total_alloc_bytes != stats.total_alloc_bytes) { |
| fprintf(stderr, "ERROR: inconsistent stacks totals (%zu vs %zu).\n", |
| dbg_counted_total_alloc_bytes, stats.total_alloc_bytes); |
| return -1; |
| @@ -211,25 +219,6 @@ static int dump_process_heap( |
| return 0; |
| } |
| -// If the dump is interrupted, resume the target process before exiting. |
| -static void exit_handler() { |
| - kill(pid, SIGCONT); |
| - waitpid(pid, NULL, 0); |
| - exit(-1); |
| -} |
| - |
| -static bool freeze_process() { |
| - if (kill(pid, SIGSTOP) != 0) { |
| - fprintf(stderr, "Could not freeze the target process.\n"); |
| - perror("kill"); |
| - return false; |
| - } |
| - |
| - signal(SIGPIPE, exit_handler); |
| - signal(SIGINT, exit_handler); |
| - return true; |
| -} |
| - |
| // Unfortunately lseek takes a *signed* offset, which is unsuitable for large |
| // files like /proc/X/mem on 64-bit. |
| static void lseek_abs(int fd, size_t off) { |
| @@ -242,6 +231,22 @@ static void lseek_abs(int fd, size_t off) { |
| lseek(fd, (off_t) (off - OFF_T_MAX), SEEK_CUR); |
| } |
| +static ssize_t read_safe(int fd, void* buf, size_t count) { |
| + ssize_t res; |
| + size_t bytes_read = 0; |
| + if (count < 0) |
| + return -1; |
| + do { |
| + do { |
| + res = read(fd, buf, count); |
| + } while (res == -1 && errno == EINTR); |
| + if (res <= 0) |
| + break; |
| + bytes_read += res; |
|
pasko
2014/09/10 16:09:27
buf += res
Primiano Tucci (use gerrit)
2014/09/10 16:36:46
Yeah, Apparently I am not able to copy and paste.
|
| + } while (bytes_read < count); |
| + return bytes_read ? bytes_read : res; |
| +} |
| + |
| static int open_proc_mem_fd() { |
| char path[64]; |
| snprintf(path, sizeof(path), "/proc/%d/mem", pid); |
| @@ -274,7 +279,7 @@ static void read_proc_cmdline(char* cmdline, int size) { |
| cmdline[0] = '\0'; |
| return; |
| } |
| - int length = read(cmdline_fd, cmdline, size); |
| + int length = read_safe(cmdline_fd, cmdline, size); |
| if (length < 0) { |
| fprintf(stderr, "Could not read %s.\n", path); |
| perror("read"); |
| @@ -287,18 +292,18 @@ static void read_proc_cmdline(char* cmdline, int size) { |
| int main(int argc, char** argv) { |
| char c; |
| int ret = 0; |
| - bool should_freeze_process = true; |
| bool dump_also_allocs = false; |
| + bool pedantic = true; |
| char comment[1024] = { '\0' }; |
| - while (((c = getopt(argc, argv, "nxc:")) & 0x80) == 0) { |
| + while (((c = getopt(argc, argv, "xnc:")) & 0x80) == 0) { |
| switch (c) { |
| - case 'n': |
| - should_freeze_process = false; |
| - break; |
| case 'x': |
| dump_also_allocs = true; |
| break; |
| + case 'n': |
| + pedantic = false; |
| + break; |
| case 'c': |
| strlcpy(comment, optarg, sizeof(comment)); |
| break; |
| @@ -306,14 +311,20 @@ int main(int argc, char** argv) { |
| } |
| if (optind >= argc) { |
| - printf("Usage: %s [-n] [-x] [-c comment] pid\n", argv[0]); |
| + printf("Usage: %s [-n] [-x] [-c comment] pid\n" |
| + " -n: Skip pedantic checks on dump consistency.\n" |
| + " -x: Extended dump, includes individual allocations.\n" |
| + " -c: Appends the given comment to the JSON dump.\n", |
| + argv[0]); |
| return -1; |
| } |
| pid = atoi(argv[optind]); |
| - if (should_freeze_process && !freeze_process()) |
| + if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) { |
| + perror("ptrace"); |
| return -1; |
| + } |
| // Wait for the process to actually freeze. |
| waitpid(pid, NULL, 0); |
| @@ -327,10 +338,9 @@ int main(int argc, char** argv) { |
| ret = -1; |
| if (ret == 0) |
| - ret = dump_process_heap(mem_fd, fmaps, dump_also_allocs, comment); |
| + ret = dump_process_heap(mem_fd, fmaps, dump_also_allocs, pedantic, comment); |
| - if (should_freeze_process) |
| - kill(pid, SIGCONT); |
| + ptrace(PTRACE_DETACH, pid, NULL, NULL); |
| // Cleanup. |
| fflush(stdout); |