• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
is_library(const char * name)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 
parse_header(const char * line,const mapinfo * prev,mapinfo ** mi)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 
parse_field(mapinfo * mi,const char * line)87 static int parse_field(mapinfo* mi, const char* line) {
88     char field[64];
89     int size;
90 
91     if (sscanf(line, "%63s %d kB", field, &size) != 2) {
92         return -1;
93     }
94 
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 
order_before(const mapinfo * a,const mapinfo * b,int sort_by_address)114 static int order_before(const mapinfo *a, const mapinfo *b, int sort_by_address) {
115     if (sort_by_address) {
116         return a->start < b->start
117                 || (a->start == b->start && a->end < b->end);
118     } else {
119         return strcmp(a->name, b->name) < 0;
120     }
121 }
122 
enqueue_map(mapinfo ** head,mapinfo * map,int sort_by_address,int coalesce_by_name)123 static void enqueue_map(mapinfo **head, mapinfo *map, int sort_by_address, int coalesce_by_name) {
124     mapinfo *prev = NULL;
125     mapinfo *current = *head;
126 
127     if (!map) {
128         return;
129     }
130 
131     for (;;) {
132         if (current && coalesce_by_name && !strcmp(map->name, current->name)) {
133             current->size += map->size;
134             current->rss += map->rss;
135             current->pss += map->pss;
136             current->shared_clean += map->shared_clean;
137             current->shared_dirty += map->shared_dirty;
138             current->private_clean += map->private_clean;
139             current->private_dirty += map->private_dirty;
140             current->is_bss &= map->is_bss;
141             current->count++;
142             free(map);
143             break;
144         }
145 
146         if (!current || order_before(map, current, sort_by_address)) {
147             if (prev) {
148                 prev->next = map;
149             } else {
150                 *head = map;
151             }
152             map->next = current;
153             break;
154         }
155 
156         prev = current;
157         current = current->next;
158     }
159 }
160 
load_maps(int pid,int sort_by_address,int coalesce_by_name)161 static mapinfo *load_maps(int pid, int sort_by_address, int coalesce_by_name)
162 {
163     char fn[128];
164     FILE *fp;
165     char line[1024];
166     mapinfo *head = NULL;
167     mapinfo *current = NULL;
168     int len;
169 
170     snprintf(fn, sizeof(fn), "/proc/%d/smaps", pid);
171     fp = fopen(fn, "r");
172     if (fp == 0) {
173         fprintf(stderr, "cannot open /proc/%d/smaps: %s\n", pid, strerror(errno));
174         return NULL;
175     }
176 
177     while (fgets(line, sizeof(line), fp) != 0) {
178         len = strlen(line);
179         if (line[len - 1] == '\n') {
180             line[--len] = 0;
181         }
182 
183         if (current != NULL && !parse_field(current, line)) {
184             continue;
185         }
186 
187         mapinfo *next;
188         if (!parse_header(line, current, &next)) {
189             enqueue_map(&head, current, sort_by_address, coalesce_by_name);
190             current = next;
191             continue;
192         }
193 
194         fprintf(stderr, "warning: could not parse map info line: %s\n", line);
195     }
196 
197     enqueue_map(&head, current, sort_by_address, coalesce_by_name);
198 
199     fclose(fp);
200 
201     if (!head) {
202         fprintf(stderr, "could not read /proc/%d/smaps\n", pid);
203         return NULL;
204     }
205 
206     return head;
207 }
208 
209 static int verbose = 0;
210 static int terse = 0;
211 static int addresses = 0;
212 
print_header()213 static void print_header()
214 {
215     if (addresses) {
216         printf("   start      end ");
217     }
218     printf(" virtual                     shared   shared  private  private\n");
219 
220     if (addresses) {
221         printf("    addr     addr ");
222     }
223     printf("    size      RSS      PSS    clean    dirty    clean    dirty ");
224     if (!verbose && !addresses) {
225         printf("   # ");
226     }
227     printf("object\n");
228 }
229 
print_divider()230 static void print_divider()
231 {
232     if (addresses) {
233         printf("-------- -------- ");
234     }
235     printf("-------- -------- -------- -------- -------- -------- -------- ");
236     if (!verbose && !addresses) {
237         printf("---- ");
238     }
239     printf("------------------------------\n");
240 }
241 
show_map(int pid)242 static int show_map(int pid)
243 {
244     mapinfo *milist;
245     mapinfo *mi;
246     unsigned shared_dirty = 0;
247     unsigned shared_clean = 0;
248     unsigned private_dirty = 0;
249     unsigned private_clean = 0;
250     unsigned rss = 0;
251     unsigned pss = 0;
252     unsigned size = 0;
253     unsigned count = 0;
254 
255     milist = load_maps(pid, addresses, !verbose && !addresses);
256     if (milist == NULL) {
257         return 1;
258     }
259 
260     print_header();
261     print_divider();
262 
263     for (mi = milist; mi;) {
264         mapinfo* last = mi;
265 
266         shared_clean += mi->shared_clean;
267         shared_dirty += mi->shared_dirty;
268         private_clean += mi->private_clean;
269         private_dirty += mi->private_dirty;
270         rss += mi->rss;
271         pss += mi->pss;
272         size += mi->size;
273         count += mi->count;
274 
275         if (terse && !mi->private_dirty) {
276             goto out;
277         }
278 
279         if (addresses) {
280             printf("%08x %08x ", mi->start, mi->end);
281         }
282         printf("%8d %8d %8d %8d %8d %8d %8d ", mi->size,
283                mi->rss,
284                mi->pss,
285                mi->shared_clean, mi->shared_dirty,
286                mi->private_clean, mi->private_dirty);
287         if (!verbose && !addresses) {
288             printf("%4d ", mi->count);
289         }
290         printf("%s%s\n", mi->name, mi->is_bss ? " [bss]" : "");
291 
292 out:
293         mi = mi->next;
294         free(last);
295     }
296 
297     print_divider();
298     print_header();
299     print_divider();
300 
301     if (addresses) {
302         printf("                  ");
303     }
304     printf("%8d %8d %8d %8d %8d %8d %8d ", size,
305             rss, pss,
306             shared_clean, shared_dirty,
307             private_clean, private_dirty);
308     if (!verbose && !addresses) {
309         printf("%4d ", count);
310     }
311     printf("TOTAL\n");
312 
313     return 0;
314 }
315 
main(int argc,char * argv[])316 int main(int argc, char *argv[])
317 {
318     int usage = 1;
319     int result = 0;
320     int pid;
321     char *arg;
322     char *argend;
323 
324     signal(SIGPIPE, SIG_IGN);
325     for (argc--, argv++; argc > 0; argc--, argv++) {
326         arg = argv[0];
327         if (!strcmp(arg,"-v")) {
328             verbose = 1;
329             continue;
330         }
331         if (!strcmp(arg,"-t")) {
332             terse = 1;
333             continue;
334         }
335         if (!strcmp(arg,"-a")) {
336             addresses = 1;
337             continue;
338         }
339         if (argc != 1) {
340             fprintf(stderr, "too many arguments\n");
341             break;
342         }
343         pid = strtol(arg, &argend, 10);
344         if (*arg && !*argend) {
345             usage = 0;
346             if (show_map(pid)) {
347                 result = 1;
348             }
349             break;
350         }
351         fprintf(stderr, "unrecognized argument: %s\n", arg);
352         break;
353     }
354 
355     if (usage) {
356         fprintf(stderr,
357                 "showmap [-t] [-v] [-c] <pid>\n"
358                 "        -t = terse (show only items with private pages)\n"
359                 "        -v = verbose (don't coalesce maps with the same name)\n"
360                 "        -a = addresses (show virtual memory map)\n"
361                 );
362         result = 1;
363     }
364 
365     return result;
366 }
367