Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Side by Side Diff: tools/android/heap_profiler/heap_dump.c

Issue 2105873005: Remove tools/android/heap_profiler (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove heap_profiler_unittests from all targets Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/android/heap_profiler/DEPS ('k') | tools/android/heap_profiler/heap_profiler.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
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.
7 // The JSON output looks like this:
8 // {
9 // "total_allocated": 908748493, # Total bytes allocated and not freed.
10 // "num_allocs": 37542, # Number of allocations.
11 // "num_stacks": 3723, # Number of allocation call-sites.
12 // "allocs": # Optional. Printed only with the -x arg.
13 // {
14 // "beef1234": {"l": 17, "f": 1, "s": "1a"},
15 // ^ ^ ^ ^ Index of the corresponding entry in the
16 // | | | next "stacks" section. Essentially a ref
17 // | | | to the call site that created the alloc.
18 // | | |
19 // | | +-------> Flags (last arg of heap_profiler_alloc).
20 // | +----------------> Length of the Alloc.
21 // +-----------------------------> Start address of the Alloc (hex).
22 // },
23 // "stacks":
24 // {
25 // "1a": {"l": 17, "f": [1074792772, 1100849864, 1100850688, ...]},
26 // ^ ^ ^
27 // | | +-----> Stack frames (absolute virtual addresses).
28 // | +--------------> Bytes allocated and not freed by the call site.
29 // +---------------------> Index of the entry (as for "allocs" xref).
30 // Indexes are hex and might not be monotonic.
31
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <inttypes.h>
35 #include <stdbool.h>
36 #include <stddef.h>
37 #include <stdint.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/ptrace.h>
42 #include <sys/stat.h>
43 #include <sys/wait.h>
44 #include <time.h>
45 #include <unistd.h>
46
47 #include "tools/android/heap_profiler/heap_profiler.h"
48
49 static void lseek_abs(int fd, size_t off);
50 static void read_proc_cmdline(char* cmdline, int size);
51 static ssize_t read_safe(int fd, void* buf, size_t count);
52
53 static int pid;
54
55
56 static int dump_process_heap(
57 int mem_fd,
58 FILE* fmaps,
59 bool dump_also_allocs,
60 bool pedantic, // Enable pedantic consistency checks on memory counters.
61 char* comment) {
62 HeapStats stats;
63 time_t tm;
64 char cmdline[512];
65
66 tm = time(NULL);
67 read_proc_cmdline(cmdline, sizeof(cmdline));
68
69 // Look for the mmap which contains the HeapStats in the target process vmem.
70 // On Linux/Android, the libheap_profiler mmaps explicitly /dev/zero. The
71 // region furthermore starts with a magic marker to disambiguate.
72 bool stats_mmap_found = false;
73 for (;;) {
74 char line[1024];
75 if (fgets(line, sizeof(line), fmaps) == NULL)
76 break;
77
78 uintptr_t start;
79 uintptr_t end;
80 char map_file[32];
81 int ret = sscanf(line, "%"SCNxPTR"-%"SCNxPTR" rw-p %*s %*s %*s %31s",
82 &start, &end, map_file);
83 const size_t size = end - start + 1;
84 if (ret != 3 || strcmp(map_file, "/dev/zero") != 0 || size < sizeof(stats))
85 continue;
86
87 // The mmap looks promising. Let's check for the magic marker.
88 lseek_abs(mem_fd, start);
89 ssize_t rsize = read_safe(mem_fd, &stats, sizeof(stats));
90
91 if (rsize == -1) {
92 perror("read");
93 return -1;
94 }
95
96 if (rsize < sizeof(stats))
97 continue;
98
99 if (stats.magic_start == HEAP_PROFILER_MAGIC_MARKER) {
100 stats_mmap_found = true;
101 break;
102 }
103 }
104
105 if (!stats_mmap_found) {
106 fprintf(stderr, "Could not find the HeapStats area. "
107 "It looks like libheap_profiler is not loaded.\n");
108 return -1;
109 }
110
111 // Print JSON-formatted output.
112 printf("{\n");
113 printf(" \"pid\": %d,\n", pid);
114 printf(" \"time\": %ld,\n", tm);
115 printf(" \"comment\": \"%s\",\n", comment);
116 printf(" \"cmdline\": \"%s\",\n", cmdline);
117 printf(" \"pagesize\": %d,\n", getpagesize());
118 printf(" \"total_allocated\": %zu,\n", stats.total_alloc_bytes);
119 printf(" \"num_allocs\": %"PRIu32",\n", stats.num_allocs);
120 printf(" \"num_stacks\": %"PRIu32",\n", stats.num_stack_traces);
121
122 uint32_t dbg_counted_allocs = 0;
123 size_t dbg_counted_total_alloc_bytes = 0;
124 bool prepend_trailing_comma = false; // JSON syntax, I hate you.
125 uint32_t i;
126
127 // Dump the optional allocation table.
128 if (dump_also_allocs) {
129 printf(" \"allocs\": {");
130 lseek_abs(mem_fd, (uintptr_t) stats.allocs);
131 for (i = 0; i < stats.max_allocs; ++i) {
132 Alloc alloc;
133 if (read_safe(mem_fd, &alloc, sizeof(alloc)) != sizeof(alloc)) {
134 fprintf(stderr, "ERROR: cannot read allocation table\n");
135 perror("read");
136 return -1;
137 }
138
139 // Skip empty (i.e. freed) entries.
140 if (alloc.start == 0 && alloc.end == 0)
141 continue;
142
143 if (alloc.end < alloc.start) {
144 fprintf(stderr, "ERROR: found inconsistent alloc.\n");
145 return -1;
146 }
147
148 size_t alloc_size = alloc.end - alloc.start + 1;
149 size_t stack_idx = (
150 (uintptr_t) alloc.st - (uintptr_t) stats.stack_traces) /
151 sizeof(StacktraceEntry);
152 dbg_counted_total_alloc_bytes += alloc_size;
153 ++dbg_counted_allocs;
154
155 if (prepend_trailing_comma)
156 printf(",");
157 prepend_trailing_comma = true;
158 printf("\"%"PRIxPTR"\": {\"l\": %zu, \"f\": %"PRIu32", \"s\": \"%zx\"}",
159 alloc.start, alloc_size, alloc.flags, stack_idx);
160 }
161 printf("},\n");
162
163 if (pedantic && dbg_counted_allocs != stats.num_allocs) {
164 fprintf(stderr,
165 "ERROR: inconsistent alloc count (%"PRIu32" vs %"PRIu32").\n",
166 dbg_counted_allocs, stats.num_allocs);
167 return -1;
168 }
169
170 if (pedantic && dbg_counted_total_alloc_bytes != stats.total_alloc_bytes) {
171 fprintf(stderr, "ERROR: inconsistent alloc totals (%zu vs %zu).\n",
172 dbg_counted_total_alloc_bytes, stats.total_alloc_bytes);
173 return -1;
174 }
175 }
176
177 // Dump the distinct stack traces.
178 printf(" \"stacks\": {");
179 prepend_trailing_comma = false;
180 dbg_counted_total_alloc_bytes = 0;
181 lseek_abs(mem_fd, (uintptr_t) stats.stack_traces);
182 for (i = 0; i < stats.max_stack_traces; ++i) {
183 StacktraceEntry st;
184 if (read_safe(mem_fd, &st, sizeof(st)) != sizeof(st)) {
185 fprintf(stderr, "ERROR: cannot read stack trace table\n");
186 perror("read");
187 return -1;
188 }
189
190 // Skip empty (i.e. freed) entries.
191 if (st.alloc_bytes == 0)
192 continue;
193
194 dbg_counted_total_alloc_bytes += st.alloc_bytes;
195
196 if (prepend_trailing_comma)
197 printf(",");
198 prepend_trailing_comma = true;
199
200 printf("\"%"PRIx32"\":{\"l\": %zu, \"f\": [", i, st.alloc_bytes);
201 size_t n = 0;
202 for (;;) {
203 printf("%" PRIuPTR, st.frames[n]);
204 ++n;
205 if (n == HEAP_PROFILER_MAX_DEPTH || st.frames[n] == 0)
206 break;
207 else
208 printf(",");
209 }
210 printf("]}");
211 }
212 printf("}\n}\n");
213
214 if (pedantic && dbg_counted_total_alloc_bytes != stats.total_alloc_bytes) {
215 fprintf(stderr, "ERROR: inconsistent stacks totals (%zu vs %zu).\n",
216 dbg_counted_total_alloc_bytes, stats.total_alloc_bytes);
217 return -1;
218 }
219
220 fflush(stdout);
221 return 0;
222 }
223
224 // Unfortunately lseek takes a *signed* offset, which is unsuitable for large
225 // files like /proc/X/mem on 64-bit.
226 static void lseek_abs(int fd, size_t off) {
227 #define OFF_T_MAX ((off_t) ~(((uint64_t) 1) << (8 * sizeof(off_t) - 1)))
228 if (off <= OFF_T_MAX) {
229 lseek(fd, (off_t) off, SEEK_SET);
230 return;
231 }
232 lseek(fd, (off_t) OFF_T_MAX, SEEK_SET);
233 lseek(fd, (off_t) (off - OFF_T_MAX), SEEK_CUR);
234 }
235
236 static ssize_t read_safe(int fd, void* buf, size_t count) {
237 ssize_t res;
238 size_t bytes_read = 0;
239 do {
240 do {
241 res = read(fd, buf + bytes_read, count - bytes_read);
242 } while (res == -1 && errno == EINTR);
243 if (res <= 0)
244 break;
245 bytes_read += res;
246 } while (bytes_read < count);
247 return bytes_read ? bytes_read : res;
248 }
249
250 static int open_proc_mem_fd() {
251 char path[64];
252 snprintf(path, sizeof(path), "/proc/%d/mem", pid);
253 int mem_fd = open(path, O_RDONLY);
254 if (mem_fd < 0) {
255 fprintf(stderr, "Could not attach to target process virtual memory.\n");
256 perror("open");
257 }
258 return mem_fd;
259 }
260
261 static FILE* open_proc_maps() {
262 char path[64];
263 snprintf(path, sizeof(path), "/proc/%d/maps", pid);
264 FILE* fmaps = fopen(path, "r");
265 if (fmaps == NULL) {
266 fprintf(stderr, "Could not open %s.\n", path);
267 perror("fopen");
268 }
269 return fmaps;
270 }
271
272 static void read_proc_cmdline(char* cmdline, int size) {
273 char path[64];
274 snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
275 int cmdline_fd = open(path, O_RDONLY);
276 if (cmdline_fd < 0) {
277 fprintf(stderr, "Could not open %s.\n", path);
278 perror("open");
279 cmdline[0] = '\0';
280 return;
281 }
282 int length = read_safe(cmdline_fd, cmdline, size);
283 if (length < 0) {
284 fprintf(stderr, "Could not read %s.\n", path);
285 perror("read");
286 length = 0;
287 }
288 close(cmdline_fd);
289 cmdline[length] = '\0';
290 }
291
292 int main(int argc, char** argv) {
293 char c;
294 int ret = 0;
295 bool dump_also_allocs = false;
296 bool pedantic = true;
297 char comment[1024] = { '\0' };
298
299 while (((c = getopt(argc, argv, "xnc:")) & 0x80) == 0) {
300 switch (c) {
301 case 'x':
302 dump_also_allocs = true;
303 break;
304 case 'n':
305 pedantic = false;
306 break;
307 case 'c':
308 strlcpy(comment, optarg, sizeof(comment));
309 break;
310 }
311 }
312
313 if (optind >= argc) {
314 printf("Usage: %s [-n] [-x] [-c comment] pid\n"
315 " -n: Skip pedantic checks on dump consistency.\n"
316 " -x: Extended dump, includes individual allocations.\n"
317 " -c: Appends the given comment to the JSON dump.\n",
318 argv[0]);
319 return -1;
320 }
321
322 pid = atoi(argv[optind]);
323
324 if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) {
325 perror("ptrace");
326 return -1;
327 }
328
329 // Wait for the process to actually freeze.
330 waitpid(pid, NULL, 0);
331
332 int mem_fd = open_proc_mem_fd();
333 if (mem_fd < 0)
334 ret = -1;
335
336 FILE* fmaps = open_proc_maps();
337 if (fmaps == NULL)
338 ret = -1;
339
340 if (ret == 0)
341 ret = dump_process_heap(mem_fd, fmaps, dump_also_allocs, pedantic, comment);
342
343 ptrace(PTRACE_DETACH, pid, NULL, NULL);
344
345 // Cleanup.
346 fflush(stdout);
347 close(mem_fd);
348 fclose(fmaps);
349 return ret;
350 }
OLDNEW
« no previous file with comments | « tools/android/heap_profiler/DEPS ('k') | tools/android/heap_profiler/heap_profiler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698