OSDN Git Service

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