OSDN Git Service

ANRdaemon: move trace result from /sdcard to /data
[android-x86/system-extras.git] / showmap / showmap.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <math.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8
9 #include <ctype.h>
10 #include <stddef.h>
11
12 typedef struct mapinfo mapinfo;
13
14 struct mapinfo {
15     mapinfo *next;
16     unsigned start;
17     unsigned end;
18     unsigned size;
19     unsigned rss;
20     unsigned pss;
21     unsigned shared_clean;
22     unsigned shared_dirty;
23     unsigned private_clean;
24     unsigned private_dirty;
25     unsigned swap;
26     int is_bss;
27     int count;
28     char name[1];
29 };
30
31 static int is_library(const char *name) {
32     int len = strlen(name);
33     return len >= 4 && name[0] == '/'
34             && name[len - 3] == '.' && name[len - 2] == 's' && name[len - 1] == 'o';
35 }
36
37 // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /android/lib/libcomposer.so
38 // 012345678901234567890123456789012345678901234567890123456789
39 // 0         1         2         3         4         5
40
41 static int parse_header(const char* line, const mapinfo* prev, mapinfo** mi) {
42     unsigned long start;
43     unsigned long end;
44     char name[128];
45     int name_pos;
46     int is_bss = 0;
47
48     if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
49         *mi = NULL;
50         return -1;
51     }
52
53     while (isspace(line[name_pos])) {
54         name_pos += 1;
55     }
56
57     if (line[name_pos]) {
58         strlcpy(name, line + name_pos, sizeof(name));
59     } else {
60         if (prev && start == prev->end && is_library(prev->name)) {
61             // anonymous mappings immediately adjacent to shared libraries
62             // usually correspond to the library BSS segment, so we use the
63             // library's own name
64             strlcpy(name, prev->name, sizeof(name));
65             is_bss = 1;
66         } else {
67             strlcpy(name, "[anon]", sizeof(name));
68         }
69     }
70
71     const int name_size = strlen(name) + 1;
72     struct mapinfo* info = calloc(1, sizeof(mapinfo) + name_size);
73     if (info == NULL) {
74         fprintf(stderr, "out of memory\n");
75         exit(1);
76     }
77
78     info->start = start;
79     info->end = end;
80     info->is_bss = is_bss;
81     info->count = 1;
82     strlcpy(info->name, name, name_size);
83
84     *mi = info;
85     return 0;
86 }
87
88 static int parse_field(mapinfo* mi, const char* line) {
89     char field[64];
90     int len;
91
92     if (sscanf(line, "%63s %n", field, &len) == 1
93             && *field && field[strlen(field) - 1] == ':') {
94         int size;
95         if (sscanf(line + len, "%d kB", &size) == 1) {
96             if (!strcmp(field, "Size:")) {
97                 mi->size = size;
98             } else if (!strcmp(field, "Rss:")) {
99                 mi->rss = size;
100             } else if (!strcmp(field, "Pss:")) {
101                 mi->pss = size;
102             } else if (!strcmp(field, "Shared_Clean:")) {
103                 mi->shared_clean = size;
104             } else if (!strcmp(field, "Shared_Dirty:")) {
105                 mi->shared_dirty = size;
106             } else if (!strcmp(field, "Private_Clean:")) {
107                 mi->private_clean = size;
108             } else if (!strcmp(field, "Private_Dirty:")) {
109                 mi->private_dirty = size;
110             } else if (!strcmp(field, "Swap:")) {
111                 mi->swap = size;
112             }
113         }
114         return 0;
115     }
116     return -1;
117 }
118
119 static int order_before(const mapinfo *a, const mapinfo *b, int sort_by_address) {
120     if (sort_by_address) {
121         return a->start < b->start
122                 || (a->start == b->start && a->end < b->end);
123     } else {
124         return strcmp(a->name, b->name) < 0;
125     }
126 }
127
128 static void enqueue_map(mapinfo **head, mapinfo *map, int sort_by_address, int coalesce_by_name) {
129     mapinfo *prev = NULL;
130     mapinfo *current = *head;
131
132     if (!map) {
133         return;
134     }
135
136     for (;;) {
137         if (current && coalesce_by_name && !strcmp(map->name, current->name)) {
138             current->size += map->size;
139             current->rss += map->rss;
140             current->pss += map->pss;
141             current->shared_clean += map->shared_clean;
142             current->shared_dirty += map->shared_dirty;
143             current->private_clean += map->private_clean;
144             current->private_dirty += map->private_dirty;
145             current->swap += map->swap;
146             current->is_bss &= map->is_bss;
147             current->count++;
148             free(map);
149             break;
150         }
151
152         if (!current || order_before(map, current, sort_by_address)) {
153             if (prev) {
154                 prev->next = map;
155             } else {
156                 *head = map;
157             }
158             map->next = current;
159             break;
160         }
161
162         prev = current;
163         current = current->next;
164     }
165 }
166
167 static mapinfo *load_maps(int pid, int sort_by_address, int coalesce_by_name)
168 {
169     char fn[128];
170     FILE *fp;
171     char line[1024];
172     mapinfo *head = NULL;
173     mapinfo *current = NULL;
174     int len;
175
176     snprintf(fn, sizeof(fn), "/proc/%d/smaps", pid);
177     fp = fopen(fn, "r");
178     if (fp == 0) {
179         fprintf(stderr, "cannot open /proc/%d/smaps: %s\n", pid, strerror(errno));
180         return NULL;
181     }
182
183     while (fgets(line, sizeof(line), fp) != 0) {
184         len = strlen(line);
185         if (line[len - 1] == '\n') {
186             line[--len] = 0;
187         }
188
189         if (current != NULL && !parse_field(current, line)) {
190             continue;
191         }
192
193         mapinfo *next;
194         if (!parse_header(line, current, &next)) {
195             enqueue_map(&head, current, sort_by_address, coalesce_by_name);
196             current = next;
197             continue;
198         }
199
200         fprintf(stderr, "warning: could not parse map info line: %s\n", line);
201     }
202
203     enqueue_map(&head, current, sort_by_address, coalesce_by_name);
204
205     fclose(fp);
206
207     if (!head) {
208         fprintf(stderr, "could not read /proc/%d/smaps\n", pid);
209         return NULL;
210     }
211
212     return head;
213 }
214
215 static int verbose = 0;
216 static int terse = 0;
217 static int addresses = 0;
218
219 static void print_header()
220 {
221     if (addresses) {
222         printf("   start      end ");
223     }
224     printf(" virtual                     shared   shared  private  private\n");
225
226     if (addresses) {
227         printf("    addr     addr ");
228     }
229     printf("    size      RSS      PSS    clean    dirty    clean    dirty    swap ");
230     if (!verbose && !addresses) {
231         printf("   # ");
232     }
233     printf("object\n");
234 }
235
236 static void print_divider()
237 {
238     if (addresses) {
239         printf("-------- -------- ");
240     }
241     printf("-------- -------- -------- -------- -------- -------- -------- -------- ");
242     if (!verbose && !addresses) {
243         printf("---- ");
244     }
245     printf("------------------------------\n");
246 }
247
248 static int show_map(int pid)
249 {
250     mapinfo *milist;
251     mapinfo *mi;
252     unsigned shared_dirty = 0;
253     unsigned shared_clean = 0;
254     unsigned private_dirty = 0;
255     unsigned private_clean = 0;
256     unsigned swap = 0;
257     unsigned rss = 0;
258     unsigned pss = 0;
259     unsigned size = 0;
260     unsigned count = 0;
261
262     milist = load_maps(pid, addresses, !verbose && !addresses);
263     if (milist == NULL) {
264         return 1;
265     }
266
267     print_header();
268     print_divider();
269
270     for (mi = milist; mi;) {
271         mapinfo* last = mi;
272
273         shared_clean += mi->shared_clean;
274         shared_dirty += mi->shared_dirty;
275         private_clean += mi->private_clean;
276         private_dirty += mi->private_dirty;
277         swap += mi->swap;
278         rss += mi->rss;
279         pss += mi->pss;
280         size += mi->size;
281         count += mi->count;
282         
283         if (terse && !mi->private_dirty) {
284             goto out;
285         }
286
287         if (addresses) {
288             printf("%08x %08x ", mi->start, mi->end);
289         }
290         printf("%8d %8d %8d %8d %8d %8d %8d %8d", mi->size,
291                mi->rss,
292                mi->pss,
293                mi->shared_clean, mi->shared_dirty,
294                mi->private_clean, mi->private_dirty, mi->swap);
295         if (!verbose && !addresses) {
296             printf("%4d ", mi->count);
297         }
298         printf("%s%s\n", mi->name, mi->is_bss ? " [bss]" : "");
299
300 out:
301         mi = mi->next;
302         free(last);
303     }
304
305     print_divider();
306     print_header();
307     print_divider();
308
309     if (addresses) {
310         printf("                  ");
311     }
312     printf("%8d %8d %8d %8d %8d %8d %8d %8d", size,
313             rss, pss,
314             shared_clean, shared_dirty,
315             private_clean, private_dirty, swap);
316     if (!verbose && !addresses) {
317         printf("%4d ", count);
318     }
319     printf("TOTAL\n");
320
321     return 0;
322 }
323
324 int main(int argc, char *argv[])
325 {
326     int usage = 1;
327     int result = 0;
328     int pid;
329     char *arg;
330     char *argend;
331
332     signal(SIGPIPE, SIG_IGN);
333     for (argc--, argv++; argc > 0; argc--, argv++) {
334         arg = argv[0];
335         if (!strcmp(arg,"-v")) {
336             verbose = 1;
337             continue;
338         }
339         if (!strcmp(arg,"-t")) {
340             terse = 1;
341             continue;
342         }
343         if (!strcmp(arg,"-a")) {
344             addresses = 1;
345             continue;
346         }
347         if (argc != 1) {
348             fprintf(stderr, "too many arguments\n");
349             break;
350         }
351         pid = strtol(arg, &argend, 10);
352         if (*arg && !*argend) {
353             usage = 0;
354             if (show_map(pid)) {
355                 result = 1;
356             }
357             break;
358         }
359         fprintf(stderr, "unrecognized argument: %s\n", arg);
360         break;
361     }
362
363     if (usage) {
364         fprintf(stderr,
365                 "showmap [-t] [-v] [-c] <pid>\n"
366                 "        -t = terse (show only items with private pages)\n"
367                 "        -v = verbose (don't coalesce maps with the same name)\n"
368                 "        -a = addresses (show virtual memory map)\n"
369                 );
370         result = 1;
371     }
372
373     return result;
374 }