showmap.c revision f95601708a46c098582eb836fe25889866858ad9
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
12typedef struct mapinfo mapinfo;
13
14struct 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    char name[1];
27};
28
29static int is_library(const char *name) {
30    int len = strlen(name);
31    return len >= 4 && name[0] == '/'
32            && name[len - 3] == '.' && name[len - 2] == 's' && name[len - 1] == 'o';
33}
34
35// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /android/lib/libcomposer.so
36// 012345678901234567890123456789012345678901234567890123456789
37// 0         1         2         3         4         5
38
39static int parse_header(const char* line, const mapinfo* prev, mapinfo** mi) {
40    unsigned long start;
41    unsigned long end;
42    char name[128];
43    int name_pos;
44    int is_bss = 0;
45
46    if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
47        *mi = NULL;
48        return -1;
49    }
50
51    while (isspace(line[name_pos])) {
52        name_pos += 1;
53    }
54
55    if (line[name_pos]) {
56        strlcpy(name, line + name_pos, sizeof(name));
57    } else {
58        if ((start >= 0x10000000) && (start < 0x40000000)) {
59            strlcpy(name, "[stack]", sizeof(name));
60        } else 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    strlcpy(info->name, name, name_size);
82
83    *mi = info;
84    return 0;
85}
86
87static 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
114static 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
123static 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            free(map);
142            break;
143        }
144
145        if (!current || order_before(map, current, sort_by_address)) {
146            if (prev) {
147                prev->next = map;
148            } else {
149                *head = map;
150            }
151            map->next = current;
152            break;
153        }
154
155        prev = current;
156        current = current->next;
157    }
158}
159
160static mapinfo *load_maps(int pid, int sort_by_address, int coalesce_by_name)
161{
162    char fn[128];
163    FILE *fp;
164    char line[1024];
165    mapinfo *head = NULL;
166    mapinfo *current = NULL;
167    int len;
168
169    snprintf(fn, sizeof(fn), "/proc/%d/smaps", pid);
170    fp = fopen(fn, "r");
171    if (fp == 0) {
172        fprintf(stderr, "cannot open /proc/%d/smaps: %s\n", pid, strerror(errno));
173        return NULL;
174    }
175
176    while (fgets(line, sizeof(line), fp) != 0) {
177        len = strlen(line);
178        if (line[len - 1] == '\n') {
179            line[--len] = 0;
180        }
181
182        if (current != NULL && !parse_field(current, line)) {
183            continue;
184        }
185
186        mapinfo *next;
187        if (!parse_header(line, current, &next)) {
188            enqueue_map(&head, current, sort_by_address, coalesce_by_name);
189            current = next;
190            continue;
191        }
192
193        fprintf(stderr, "warning: could not parse map info line: %s\n", line);
194    }
195
196    enqueue_map(&head, current, sort_by_address, coalesce_by_name);
197
198    fclose(fp);
199
200    if (!head) {
201        fprintf(stderr, "could not read /proc/%d/smaps\n", pid);
202        return NULL;
203    }
204
205    return head;
206}
207
208static int verbose = 0;
209static int terse = 0;
210static int addresses = 0;
211
212static int show_map(int pid)
213{
214    mapinfo *milist;
215    mapinfo *mi;
216    unsigned shared_dirty = 0;
217    unsigned shared_clean = 0;
218    unsigned private_dirty = 0;
219    unsigned private_clean = 0;
220    unsigned rss = 0;
221    unsigned pss = 0;
222    unsigned size = 0;
223
224    milist = load_maps(pid, addresses, !verbose && !addresses);
225    if (milist == NULL) {
226        return 1;
227    }
228
229    if (addresses) {
230        printf("start    end      ");
231    }
232    printf("virtual                    shared   shared   private  private\n");
233
234    if (addresses) {
235        printf("addr     addr     ");
236    }
237    printf("size     RSS      PSS      clean    dirty    clean    dirty    object\n");
238
239    if (addresses) {
240        printf("-------- -------- ");
241    }
242    printf("-------- -------- -------- -------- -------- -------- -------- ------------------------------\n");
243
244    for (mi = milist; mi;) {
245        mapinfo* last = mi;
246
247        shared_clean += mi->shared_clean;
248        shared_dirty += mi->shared_dirty;
249        private_clean += mi->private_clean;
250        private_dirty += mi->private_dirty;
251        rss += mi->rss;
252        pss += mi->pss;
253        size += mi->size;
254
255        if (terse && !mi->private_dirty) {
256            goto out;
257        }
258
259        if (addresses) {
260            printf("%08x %08x ", mi->start, mi->end);
261        }
262        printf("%8d %8d %8d %8d %8d %8d %8d %s%s\n", mi->size,
263               mi->rss,
264               mi->pss,
265               mi->shared_clean, mi->shared_dirty,
266               mi->private_clean, mi->private_dirty,
267               mi->name, mi->is_bss ? " [bss]" : "");
268
269out:
270        mi = mi->next;
271        free(last);
272    }
273
274    if (addresses) {
275        printf("-------- -------- ");
276    }
277    printf("-------- -------- -------- -------- -------- -------- -------- ------------------------------\n");
278
279    if (addresses) {
280        printf("                  ");
281    }
282    printf("%8d %8d %8d %8d %8d %8d %8d TOTAL\n", size,
283            rss, pss,
284            shared_clean, shared_dirty,
285            private_clean, private_dirty);
286
287    return 0;
288}
289
290int main(int argc, char *argv[])
291{
292    int usage = 1;
293    int result = 0;
294    int pid;
295    char *arg;
296    char *argend;
297
298    for (argc--, argv++; argc > 0; argc--, argv++) {
299        arg = argv[0];
300        if (!strcmp(arg,"-v")) {
301            verbose = 1;
302            continue;
303        }
304        if (!strcmp(arg,"-t")) {
305            terse = 1;
306            continue;
307        }
308        if (!strcmp(arg,"-a")) {
309            addresses = 1;
310            continue;
311        }
312        if (argc != 1) {
313            fprintf(stderr, "too many arguments\n");
314            break;
315        }
316        pid = strtol(arg, &argend, 10);
317        if (*arg && !*argend) {
318            usage = 0;
319            if (show_map(pid)) {
320                result = 1;
321            }
322            break;
323        }
324        fprintf(stderr, "unrecognized argument: %s\n", arg);
325        break;
326    }
327
328    if (usage) {
329        fprintf(stderr,
330                "showmap [-t] [-v] [-c] <pid>\n"
331                "        -t = terse (show only items with private pages)\n"
332                "        -v = verbose (don't coalesce maps with the same name)\n"
333                "        -a = addresses (show virtual memory map)\n"
334                );
335        result = 1;
336    }
337
338    return result;
339}
340