showmap.c revision fda77ea946fa4c58775d5ff63895cf9d41d3b568
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    char name[1];
26};
27
28// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /android/lib/libcomposer.so
29// 012345678901234567890123456789012345678901234567890123456789
30// 0         1         2         3         4         5
31
32int parse_header(char* line, int len, mapinfo** mi) {
33    unsigned long start;
34    unsigned long end;
35    char name[128];
36
37    name[0] = '\0';
38
39    // Sometimes the name is missing.
40    if (sscanf(line, "%lx-%lx %*s %*lx %*x:%*x %*ld %127s", &start, &end, name) < 2) {
41        return 0;
42    }
43
44    if (name[0] == '\0') {
45        if ((start >= 0x10000000) && (start < 0x40000000)) {
46            strlcpy(name, "[stack]", sizeof(name));
47        } else if (start > 0x50000000) {
48            strlcpy(name, "[lib_bss]", sizeof(name));
49        } else {
50            strlcpy(name, "[anon]", sizeof(name));
51        }
52    }
53
54    const int name_size = strlen(name) + 1;
55    struct mapinfo* info = calloc(1, sizeof(mapinfo) + name_size);
56    if (info == NULL) {
57        return -1;
58    }
59
60    info->start = start;
61    info->end = end;
62    strlcpy(info->name, name, name_size);
63
64    *mi = info;
65
66    return 0;
67}
68
69int parse_field(mapinfo* mi, char* line) {
70    char field[64];
71    int size;
72
73    if (sscanf(line, "%63s %d kB", field, &size) != 2) {
74        return -1;
75    }
76
77    if (!strcmp(field, "Size:")) {
78        mi->size = size;
79    } else if (!strcmp(field, "Rss:")) {
80        mi->rss = size;
81    } else if (!strcmp(field, "Pss:")) {
82        mi->pss = size;
83    } else if (!strcmp(field, "Shared_Clean:")) {
84        mi->shared_clean = size;
85    } else if (!strcmp(field, "Shared_Dirty:")) {
86        mi->shared_dirty = size;
87    } else if (!strcmp(field, "Private_Clean:")) {
88        mi->private_clean = size;
89    } else if (!strcmp(field, "Private_Dirty:")) {
90        mi->private_dirty = size;
91    }
92
93    return 0;
94}
95
96mapinfo *read_mapinfo(FILE *fp)
97{
98    char line[1024];
99    mapinfo *current = NULL;
100    int len;
101    int skip;
102
103    while (fgets(line, sizeof(line), fp) != 0) {
104        if (current != NULL) {
105            parse_field(current, line);
106        }
107
108        len = strlen(line);
109        if (len < 1) {
110            return NULL;
111        }
112        line[--len] = 0;
113
114        mapinfo *next = NULL;
115        if (parse_header(line, len, &next) < 0) {
116            goto err;
117        } else if (next != NULL) {
118            next->next = current;
119            current = next;
120            continue;
121        }
122    }
123
124    return current;
125
126err:
127    while (current != NULL) {
128        mapinfo* next = current->next;
129        free(current);
130        current = next;
131    }
132
133    return NULL;
134}
135
136
137mapinfo *load_maps(int pid, int verbose)
138{
139    char tmp[128];
140    FILE *fp;
141    mapinfo *milist = 0;
142    mapinfo *mi;
143
144    snprintf(tmp, sizeof(tmp), "/proc/%d/smaps", pid);
145    fp = fopen(tmp, "r");
146    if (fp == 0) {
147        fprintf(stderr, "cannot open /proc/%d/smaps: %s\n", pid, strerror(errno));
148        return NULL;
149    }
150
151    milist = read_mapinfo(fp);
152    fclose(fp);
153
154    if (!milist) {
155        fprintf(stderr, "could not read /proc/%d/smaps\n", pid);
156        return NULL;
157    }
158
159    /* if not verbose, coalesce mappings from the same entity */
160    if (!verbose) {
161        mapinfo* current = milist;
162        mapinfo* last = NULL;
163
164        while (current != NULL) {
165            mapinfo* next = current->next;
166
167            if (last != NULL
168                    && ((current->name[0] != '[' && !strcmp(last->name, current->name))
169                        || !strcmp(current->name, "[lib_bss]"))) {
170                last->size += current->size;
171                last->rss += current->rss;
172                last->pss += current->pss;
173                last->shared_clean += current->shared_clean;
174                last->shared_dirty += current->shared_dirty;
175                last->private_clean += current->private_clean;
176                last->private_dirty += current->private_dirty;
177                last->end = current->end;
178
179                last->next = next;
180                free(current);
181            } else {
182                last = current;
183            }
184
185            current = next;
186        }
187    }
188
189    return milist;
190}
191
192static int verbose = 0;
193static int terse = 0;
194static int addresses = 0;
195
196int show_map(int pid)
197{
198    mapinfo *milist;
199    mapinfo *mi;
200    unsigned shared_dirty = 0;
201    unsigned shared_clean = 0;
202    unsigned private_dirty = 0;
203    unsigned private_clean = 0;
204    unsigned rss = 0;
205    unsigned pss = 0;
206    unsigned size = 0;
207
208    milist = load_maps(pid, verbose);
209    if (milist == NULL) {
210        return 1;
211    }
212
213    if (addresses) {
214        printf("start    end      shared   private  object\n");
215        printf("-------- -------- -------- -------- ------------------------------\n");
216    } else {
217        printf("virtual                    shared   shared   private  private\n");
218        printf("size     RSS      PSS      clean    dirty    clean    dirty    object\n");
219        printf("-------- -------- -------- -------- -------- -------- -------- ------------------------------\n");
220    }
221
222    for (mi = milist; mi;) {
223        mapinfo* last = mi;
224
225        shared_clean += mi->shared_clean;
226        shared_dirty += mi->shared_dirty;
227        private_clean += mi->private_clean;
228        private_dirty += mi->private_dirty;
229        rss += mi->rss;
230        pss += mi->pss;
231        size += mi->size;
232
233        if (terse && !mi->private_dirty) {
234            goto out;
235        }
236
237        if (addresses) {
238            printf("%08x %08x %8d %8d %s\n", mi->start, mi->end,
239                   mi->shared_clean + mi->shared_dirty,
240                   mi->private_clean + mi->private_dirty,
241                   mi->name);
242        } else {
243            printf("%8d %8d %8d %8d %8d %8d %8d %s\n", mi->size,
244                   mi->rss,
245                   mi->pss,
246                   mi->shared_clean, mi->shared_dirty,
247                   mi->private_clean, mi->private_dirty,
248                   mi->name);
249        }
250
251out:
252        mi = mi->next;
253        free(last);
254    }
255
256    if (addresses) {
257        printf("-------- -------- -------- -------- ------------------------------\n");
258        printf("                  %8d %8d TOTAL\n",
259               shared_dirty + shared_clean,
260               private_dirty + private_clean);
261    } else {
262        printf("-------- -------- -------- -------- -------- -------- -------- ------------------------------\n");
263        printf("%8d %8d %8d %8d %8d %8d %8d TOTAL\n", size,
264               rss, pss,
265               shared_clean, shared_dirty,
266               private_clean, private_dirty);
267    }
268
269    return 0;
270}
271
272int main(int argc, char *argv[])
273{
274    int usage = 1;
275
276    for (argc--, argv++; argc > 0; argc--, argv++) {
277        if (!strcmp(argv[0],"-v")) {
278            verbose = 1;
279            continue;
280        }
281        if (!strcmp(argv[0],"-t")) {
282            terse = 1;
283            continue;
284        }
285        if (!strcmp(argv[0],"-a")) {
286            addresses = 1;
287            continue;
288        }
289        show_map(atoi(argv[0]));
290        usage = 0;
291    }
292
293    if (usage) {
294        fprintf(stderr,
295                "showmap [-t] [-v] [-c] <pid>\n"
296                "        -t = terse (show only items with private pages)\n"
297                "        -v = verbose (don't coalesce adjacant maps)\n"
298                "        -a = addresses (show virtual memory map)\n"
299                );
300    }
301
302    return 0;
303}
304