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 char cmdline[512]; | |
62 | |
63 tm = time(NULL); | |
64 read_proc_cmdline(cmdline, 512); | |
Primiano Tucci (use gerrit)
2014/07/16 16:36:18
s/512/sizeof(cmdline)/
Dai Mikurube (NOT FULLTIME)
2014/07/17 03:11:09
Done.
| |
54 | 65 |
55 // Look for the mmap which contains the HeapStats in the target process vmem. | 66 // 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 | 67 // On Linux/Android, the libheap_profiler mmaps explicitly /dev/zero. The |
57 // region furthermore starts with a magic marker to disambiguate. | 68 // region furthermore starts with a magic marker to disambiguate. |
58 bool stats_mmap_found = false; | 69 bool stats_mmap_found = false; |
59 for (;;) { | 70 for (;;) { |
60 char line[1024]; | 71 char line[1024]; |
61 if (fgets(line, sizeof(line), fmaps) == NULL) | 72 if (fgets(line, sizeof(line), fmaps) == NULL) |
62 break; | 73 break; |
63 | 74 |
(...skipping 18 matching lines...) Expand all Loading... | |
82 } | 93 } |
83 | 94 |
84 if (!stats_mmap_found) { | 95 if (!stats_mmap_found) { |
85 fprintf(stderr, "Could not find the HeapStats area. " | 96 fprintf(stderr, "Could not find the HeapStats area. " |
86 "It looks like libheap_profiler is not loaded.\n"); | 97 "It looks like libheap_profiler is not loaded.\n"); |
87 return -1; | 98 return -1; |
88 } | 99 } |
89 | 100 |
90 // Print JSON-formatted output. | 101 // Print JSON-formatted output. |
91 printf("{\n"); | 102 printf("{\n"); |
103 printf(" \"pid\": %d,\n", pid); | |
104 printf(" \"time\": %ld,\n", tm); | |
105 printf(" \"comment\": \"%s\",\n", comment); | |
106 printf(" \"cmdline\": \"%s\",\n", cmdline); | |
107 printf(" \"pagesize\": %d,\n", getpagesize()); | |
92 printf(" \"total_allocated\": %zu,\n", stats.total_alloc_bytes); | 108 printf(" \"total_allocated\": %zu,\n", stats.total_alloc_bytes); |
93 printf(" \"num_allocs\": %"PRIu32",\n", stats.num_allocs); | 109 printf(" \"num_allocs\": %"PRIu32",\n", stats.num_allocs); |
94 printf(" \"num_stacks\": %"PRIu32",\n", stats.num_stack_traces); | 110 printf(" \"num_stacks\": %"PRIu32",\n", stats.num_stack_traces); |
95 | 111 |
96 uint32_t dbg_counted_allocs = 0; | 112 uint32_t dbg_counted_allocs = 0; |
97 size_t dbg_counted_total_alloc_bytes = 0; | 113 size_t dbg_counted_total_alloc_bytes = 0; |
98 bool prepend_trailing_comma = false; // JSON syntax, I hate you. | 114 bool prepend_trailing_comma = false; // JSON syntax, I hate you. |
99 uint32_t i; | 115 uint32_t i; |
100 | 116 |
101 // Dump the optional allocation table. | 117 // Dump the optional allocation table. |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
241 char path[64]; | 257 char path[64]; |
242 snprintf(path, sizeof(path), "/proc/%d/maps", pid); | 258 snprintf(path, sizeof(path), "/proc/%d/maps", pid); |
243 FILE* fmaps = fopen(path, "r"); | 259 FILE* fmaps = fopen(path, "r"); |
244 if (fmaps == NULL) { | 260 if (fmaps == NULL) { |
245 fprintf(stderr, "Could not open %s.\n", path); | 261 fprintf(stderr, "Could not open %s.\n", path); |
246 perror("fopen"); | 262 perror("fopen"); |
247 } | 263 } |
248 return fmaps; | 264 return fmaps; |
249 } | 265 } |
250 | 266 |
267 static int read_proc_cmdline(char* cmdline, int size) { | |
Primiano Tucci (use gerrit)
2014/07/16 16:36:18
you don't seem to make any use of the return valu
Dai Mikurube (NOT FULLTIME)
2014/07/17 03:11:09
Done.
| |
268 char path[64]; | |
269 snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); | |
270 int cmdline_fd = open(path, O_RDONLY); | |
271 if (cmdline_fd < 0) { | |
272 fprintf(stderr, "Could not open %s.\n", path); | |
273 perror("open"); | |
274 cmdline[0] = '\0'; | |
275 return 0; | |
276 } | |
277 int length = read(cmdline_fd, cmdline, size); | |
278 if (length < 0) { | |
279 fprintf(stderr, "Could not read %s.\n", path); | |
280 perror("read"); | |
281 length = 0; | |
282 } | |
283 close(cmdline_fd); | |
284 cmdline[length] = '\0'; | |
285 return length; | |
286 } | |
287 | |
251 int main(int argc, char** argv) { | 288 int main(int argc, char** argv) { |
252 char c; | 289 char c; |
253 int ret = 0; | 290 int ret = 0; |
254 bool should_freeze_process = true; | 291 bool should_freeze_process = true; |
255 bool dump_also_allocs = false; | 292 bool dump_also_allocs = false; |
293 char comment[1024] = { '\0' }; | |
256 | 294 |
257 | 295 while (((c = getopt(argc, argv, "nxc:")) & 0x80) == 0) { |
258 while (((c = getopt(argc, argv, "nx")) & 0x80) == 0) { | |
259 switch (c) { | 296 switch (c) { |
260 case 'n': | 297 case 'n': |
261 should_freeze_process = false; | 298 should_freeze_process = false; |
262 break; | 299 break; |
263 case 'x': | 300 case 'x': |
264 dump_also_allocs = true; | 301 dump_also_allocs = true; |
265 break; | 302 break; |
303 case 'c': | |
304 strlcpy(comment, optarg, sizeof(comment)); | |
305 break; | |
266 } | 306 } |
267 } | 307 } |
268 | 308 |
269 if (optind >= argc) { | 309 if (optind >= argc) { |
270 printf("Usage: %s [-n] [-x] pid\n", argv[0]); | 310 printf("Usage: %s [-n] [-x] [-c comment] pid\n", argv[0]); |
271 return -1; | 311 return -1; |
272 } | 312 } |
273 | 313 |
274 pid = atoi(argv[optind]); | 314 pid = atoi(argv[optind]); |
275 | 315 |
276 if (should_freeze_process && !freeze_process()) | 316 if (should_freeze_process && !freeze_process()) |
277 return -1; | 317 return -1; |
278 | 318 |
279 // Wait for the process to actually freeze. | 319 // Wait for the process to actually freeze. |
280 waitpid(pid, NULL, 0); | 320 waitpid(pid, NULL, 0); |
281 | 321 |
282 int mem_fd = open_proc_mem_fd(); | 322 int mem_fd = open_proc_mem_fd(); |
283 if (mem_fd < 0) | 323 if (mem_fd < 0) |
284 ret = -1; | 324 ret = -1; |
285 | 325 |
286 FILE* fmaps = open_proc_maps(); | 326 FILE* fmaps = open_proc_maps(); |
287 if (fmaps == NULL) | 327 if (fmaps == NULL) |
288 ret = -1; | 328 ret = -1; |
289 | 329 |
290 if (ret == 0) | 330 if (ret == 0) |
291 ret = dump_process_heap(mem_fd, fmaps, dump_also_allocs); | 331 ret = dump_process_heap(mem_fd, fmaps, dump_also_allocs, comment); |
292 | 332 |
293 if (should_freeze_process) | 333 if (should_freeze_process) |
294 kill(pid, SIGCONT); | 334 kill(pid, SIGCONT); |
295 | 335 |
296 // Cleanup. | 336 // Cleanup. |
297 fflush(stdout); | 337 fflush(stdout); |
298 close(mem_fd); | 338 close(mem_fd); |
299 fclose(fmaps); | 339 fclose(fmaps); |
300 return ret; | 340 return ret; |
301 } | 341 } |
OLD | NEW |