Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // The client dump tool for libheap_profiler. It attaches to a process (given | 5 // The client dump tool for libheap_profiler. It attaches to a process (given |
| 6 // its pid) and dumps all the libheap_profiler tracking information in JSON. | 6 // its pid) and dumps all the libheap_profiler tracking information in JSON. |
| 7 // The target process is frozen (SIGSTOP) while dumping, unless the -n opt. | 7 // The target process is frozen (SIGSTOP) while dumping, unless the -n opt. |
| 8 // is passed (in which case the caller is responsible for un/freezing). | 8 // is passed (in which case the caller is responsible for un/freezing). |
| 9 // The JSON output looks like this: | 9 // The JSON output looks like this: |
| 10 // { | 10 // { |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 31 // +---------------------> Index of the entry (as for "allocs" xref). | 31 // +---------------------> Index of the entry (as for "allocs" xref). |
| 32 // Indexes are hex and might not be monotonic. | 32 // Indexes are hex and might not be monotonic. |
| 33 | 33 |
| 34 #include <fcntl.h> | 34 #include <fcntl.h> |
| 35 #include <inttypes.h> | 35 #include <inttypes.h> |
| 36 #include <signal.h> | 36 #include <signal.h> |
| 37 #include <stdbool.h> | 37 #include <stdbool.h> |
| 38 #include <stdio.h> | 38 #include <stdio.h> |
| 39 #include <stdlib.h> | 39 #include <stdlib.h> |
| 40 #include <string.h> | 40 #include <string.h> |
| 41 #include <time.h> | |
| 41 #include <unistd.h> | 42 #include <unistd.h> |
| 42 #include <sys/stat.h> | 43 #include <sys/stat.h> |
| 43 | 44 |
| 44 #include "tools/android/heap_profiler/heap_profiler.h" | 45 #include "tools/android/heap_profiler/heap_profiler.h" |
| 45 | 46 |
| 46 | 47 |
| 47 static void lseek_abs(int fd, size_t off); | 48 static void lseek_abs(int fd, size_t off); |
| 49 static int read_proc_cmdline(char* cmdline, int size); | |
| 48 | 50 |
| 49 static int pid; | 51 static int pid; |
| 50 | 52 |
| 51 | 53 |
| 52 static int dump_process_heap(int mem_fd, FILE* fmaps, bool dump_also_allocs) { | 54 static int dump_process_heap( |
| 55 int mem_fd, | |
| 56 FILE* fmaps, | |
| 57 bool dump_also_allocs, | |
| 58 char* comment) { | |
| 53 HeapStats stats; | 59 HeapStats stats; |
| 60 time_t tm; | |
| 61 struct tm local_tm; | |
| 62 char asc_tm[64]; | |
| 63 char cmdline[512]; | |
| 64 | |
| 65 tm = time(NULL); | |
| 66 localtime_r(&tm, &local_tm); | |
| 67 strftime(asc_tm, 64, "%Y-%m-%d %H:%M:%S%z", &local_tm); | |
|
Primiano Tucci (use gerrit)
2014/07/15 23:42:34
Can we avoid extracting the time on the device and
Dai Mikurube (NOT FULLTIME)
2014/07/16 06:24:57
Makes sense especially for the latter. Changed it
| |
| 68 | |
| 69 read_proc_cmdline(cmdline, 512); | |
| 54 | 70 |
| 55 // Look for the mmap which contains the HeapStats in the target process vmem. | 71 // Look for the mmap which contains the HeapStats in the target process vmem. |
| 56 // On Linux/Android, the libheap_profiler mmaps explicitly /dev/zero. The | 72 // On Linux/Android, the libheap_profiler mmaps explicitly /dev/zero. The |
| 57 // region furthermore starts with a magic marker to disambiguate. | 73 // region furthermore starts with a magic marker to disambiguate. |
| 58 bool stats_mmap_found = false; | 74 bool stats_mmap_found = false; |
| 59 for (;;) { | 75 for (;;) { |
| 60 char line[1024]; | 76 char line[1024]; |
| 61 if (fgets(line, sizeof(line), fmaps) == NULL) | 77 if (fgets(line, sizeof(line), fmaps) == NULL) |
| 62 break; | 78 break; |
| 63 | 79 |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 82 } | 98 } |
| 83 | 99 |
| 84 if (!stats_mmap_found) { | 100 if (!stats_mmap_found) { |
| 85 fprintf(stderr, "Could not find the HeapStats area. " | 101 fprintf(stderr, "Could not find the HeapStats area. " |
| 86 "It looks like libheap_profiler is not loaded.\n"); | 102 "It looks like libheap_profiler is not loaded.\n"); |
| 87 return -1; | 103 return -1; |
| 88 } | 104 } |
| 89 | 105 |
| 90 // Print JSON-formatted output. | 106 // Print JSON-formatted output. |
| 91 printf("{\n"); | 107 printf("{\n"); |
| 108 printf(" \"pid\": %d,\n", pid); | |
| 109 printf(" \"time\": \"%s\",\n", asc_tm); | |
| 110 printf(" \"comment\": \"%s\",\n", comment); | |
| 111 printf(" \"cmdline\": \"%s\",\n", cmdline); | |
| 112 printf(" \"pagesize\": %d,\n", getpagesize()); | |
|
Primiano Tucci (use gerrit)
2014/07/15 23:42:34
I'm curious, Is there any case in which this is ex
Dai Mikurube (NOT FULLTIME)
2014/07/16 06:24:57
Ah, it may not be required here. I used it in dmpr
| |
| 92 printf(" \"total_allocated\": %zu,\n", stats.total_alloc_bytes); | 113 printf(" \"total_allocated\": %zu,\n", stats.total_alloc_bytes); |
| 93 printf(" \"num_allocs\": %"PRIu32",\n", stats.num_allocs); | 114 printf(" \"num_allocs\": %"PRIu32",\n", stats.num_allocs); |
| 94 printf(" \"num_stacks\": %"PRIu32",\n", stats.num_stack_traces); | 115 printf(" \"num_stacks\": %"PRIu32",\n", stats.num_stack_traces); |
| 95 | 116 |
| 96 uint32_t dbg_counted_allocs = 0; | 117 uint32_t dbg_counted_allocs = 0; |
| 97 size_t dbg_counted_total_alloc_bytes = 0; | 118 size_t dbg_counted_total_alloc_bytes = 0; |
| 98 bool prepend_trailing_comma = false; // JSON syntax, I hate you. | 119 bool prepend_trailing_comma = false; // JSON syntax, I hate you. |
| 99 uint32_t i; | 120 uint32_t i; |
| 100 | 121 |
| 101 // Dump the optional allocation table. | 122 // Dump the optional allocation table. |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 241 char path[64]; | 262 char path[64]; |
| 242 snprintf(path, sizeof(path), "/proc/%d/maps", pid); | 263 snprintf(path, sizeof(path), "/proc/%d/maps", pid); |
| 243 FILE* fmaps = fopen(path, "r"); | 264 FILE* fmaps = fopen(path, "r"); |
| 244 if (fmaps == NULL) { | 265 if (fmaps == NULL) { |
| 245 fprintf(stderr, "Could not open %s.\n", path); | 266 fprintf(stderr, "Could not open %s.\n", path); |
| 246 perror("fopen"); | 267 perror("fopen"); |
| 247 } | 268 } |
| 248 return fmaps; | 269 return fmaps; |
| 249 } | 270 } |
| 250 | 271 |
| 272 static int read_proc_cmdline(char* cmdline, int size) { | |
| 273 char path[64]; | |
| 274 snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); | |
| 275 FILE* fcmdline = fopen(path, "r"); | |
|
Primiano Tucci (use gerrit)
2014/07/15 23:42:34
I'd personally just use raw open and read in these
Dai Mikurube (NOT FULLTIME)
2014/07/16 06:24:57
Okay, switched to open/read.
Primiano Tucci (use gerrit)
2014/07/16 16:36:18
Thanks :)
| |
| 276 if (fcmdline == NULL) { | |
| 277 fprintf(stderr, "Could not open %s.\n", path); | |
| 278 perror("fopen"); | |
| 279 return -1; | |
| 280 } | |
| 281 if (NULL == fgets(cmdline, size, fcmdline)) { | |
| 282 fprintf(stderr, "Could not read %s.\n", path); | |
| 283 perror("fgets"); | |
| 284 return -1; | |
| 285 } | |
| 286 fclose(fcmdline); | |
| 287 return 0; | |
| 288 } | |
| 289 | |
| 251 int main(int argc, char** argv) { | 290 int main(int argc, char** argv) { |
| 252 char c; | 291 char c; |
| 253 int ret = 0; | 292 int ret = 0; |
| 254 bool should_freeze_process = true; | 293 bool should_freeze_process = true; |
| 255 bool dump_also_allocs = false; | 294 bool dump_also_allocs = false; |
| 295 char comment[1024]; | |
|
Primiano Tucci (use gerrit)
2014/07/15 23:42:33
What about just:
char comment[1024] = { '\0' };
a
Dai Mikurube (NOT FULLTIME)
2014/07/16 06:24:57
Done.
| |
| 256 | 296 |
| 257 | 297 comment[0] = '\0'; |
| 258 while (((c = getopt(argc, argv, "nx")) & 0x80) == 0) { | 298 while (((c = getopt(argc, argv, "nxc:")) & 0x80) == 0) { |
| 259 switch (c) { | 299 switch (c) { |
| 260 case 'n': | 300 case 'n': |
| 261 should_freeze_process = false; | 301 should_freeze_process = false; |
| 262 break; | 302 break; |
| 263 case 'x': | 303 case 'x': |
| 264 dump_also_allocs = true; | 304 dump_also_allocs = true; |
| 265 break; | 305 break; |
| 306 case 'c': | |
| 307 strncpy(comment, optarg, 1024); | |
|
Primiano Tucci (use gerrit)
2014/07/15 23:42:34
s/strncpy/strlcpy/.
strlcpy is safer and nowadays
Dai Mikurube (NOT FULLTIME)
2014/07/16 06:24:57
Done.
| |
| 308 comment[1023] = '\0'; | |
|
Primiano Tucci (use gerrit)
2014/07/15 23:42:34
Precisely. By using strlcpy you don't need this an
Dai Mikurube (NOT FULLTIME)
2014/07/16 06:24:57
Acknowledged.
| |
| 309 break; | |
| 266 } | 310 } |
| 267 } | 311 } |
| 268 | 312 |
| 269 if (optind >= argc) { | 313 if (optind >= argc) { |
| 270 printf("Usage: %s [-n] [-x] pid\n", argv[0]); | 314 printf("Usage: %s [-n] [-x] [-c comment] pid\n", argv[0]); |
| 271 return -1; | 315 return -1; |
| 272 } | 316 } |
| 273 | 317 |
| 274 pid = atoi(argv[optind]); | 318 pid = atoi(argv[optind]); |
| 275 | 319 |
| 276 if (should_freeze_process && !freeze_process()) | 320 if (should_freeze_process && !freeze_process()) |
| 277 return -1; | 321 return -1; |
| 278 | 322 |
| 279 // Wait for the process to actually freeze. | 323 // Wait for the process to actually freeze. |
| 280 waitpid(pid, NULL, 0); | 324 waitpid(pid, NULL, 0); |
| 281 | 325 |
| 282 int mem_fd = open_proc_mem_fd(); | 326 int mem_fd = open_proc_mem_fd(); |
| 283 if (mem_fd < 0) | 327 if (mem_fd < 0) |
| 284 ret = -1; | 328 ret = -1; |
| 285 | 329 |
| 286 FILE* fmaps = open_proc_maps(); | 330 FILE* fmaps = open_proc_maps(); |
| 287 if (fmaps == NULL) | 331 if (fmaps == NULL) |
| 288 ret = -1; | 332 ret = -1; |
| 289 | 333 |
| 290 if (ret == 0) | 334 if (ret == 0) |
| 291 ret = dump_process_heap(mem_fd, fmaps, dump_also_allocs); | 335 ret = dump_process_heap(mem_fd, fmaps, dump_also_allocs, comment); |
| 292 | 336 |
| 293 if (should_freeze_process) | 337 if (should_freeze_process) |
| 294 kill(pid, SIGCONT); | 338 kill(pid, SIGCONT); |
| 295 | 339 |
| 296 // Cleanup. | 340 // Cleanup. |
| 297 fflush(stdout); | 341 fflush(stdout); |
| 298 close(mem_fd); | 342 close(mem_fd); |
| 299 fclose(fmaps); | 343 fclose(fmaps); |
| 300 return ret; | 344 return ret; |
| 301 } | 345 } |
| OLD | NEW |