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