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
32mapinfo *read_mapinfo(FILE *fp)
33{
34    char line[1024];
35    mapinfo *mi;
36    int len;
37    int skip;
38
39again:
40    skip = 0;
41
42    if(fgets(line, 1024, fp) == 0) return 0;
43
44    len = strlen(line);
45    if(len < 1) return 0;
46    line[--len] = 0;
47
48    mi = calloc(1, sizeof(mapinfo) + len + 16);
49    if(mi == 0) return 0;
50
51    mi->start = strtoul(line, 0, 16);
52    mi->end = strtoul(line + 9, 0, 16);
53
54    if(len < 50) {
55        if((mi->start >= 0x10000000) && (mi->start < 0x40000000)) {
56            strcpy(mi->name, "[stack]");
57        } else if(mi->start > 0x50000000) {
58            strcpy(mi->name, "[lib_bss]");
59        } else {
60            strcpy(mi->name, "[anon]");
61        }
62    } else {
63        strcpy(mi->name, line + 49);
64    }
65
66    if(fgets(line, 1024, fp) == 0) goto oops;
67    if(sscanf(line, "Size: %d kB", &mi->size) != 1) goto oops;
68    if(fgets(line, 1024, fp) == 0) goto oops;
69    if(sscanf(line, "Rss: %d kB", &mi->rss) != 1) goto oops;
70    if(fgets(line, 1024, fp) == 0) goto oops;
71    if(sscanf(line, "Pss: %d kB", &mi->pss) == 1)
72        if(fgets(line, 1024, fp) == 0) goto oops;
73    if(sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) != 1) goto oops;
74    if(fgets(line, 1024, fp) == 0) goto oops;
75    if(sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) != 1) goto oops;
76    if(fgets(line, 1024, fp) == 0) goto oops;
77    if(sscanf(line, "Private_Clean: %d kB", &mi->private_clean) != 1) goto oops;
78    if(fgets(line, 1024, fp) == 0) goto oops;
79    if(sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) != 1) goto oops;
80
81    if(fgets(line, 1024, fp) == 0) goto oops; // Referenced
82    if(fgets(line, 1024, fp) == 0) goto oops; // Swap
83    if(fgets(line, 1024, fp) == 0) goto oops; // KernelPageSize
84    if(fgets(line, 1024, fp) == 0) goto oops; // MMUPageSize
85
86    if(skip) {
87        free(mi);
88        goto again;
89    }
90
91    return mi;
92oops:
93    fprintf(stderr, "WARNING: Format of /proc/<pid>/smaps has changed!\n");
94    free(mi);
95    return 0;
96}
97
98
99mapinfo *load_maps(int pid, int verbose)
100{
101    char tmp[128];
102    FILE *fp;
103    mapinfo *milist = 0;
104    mapinfo *mi;
105
106    sprintf(tmp, "/proc/%d/smaps", pid);
107    fp = fopen(tmp, "r");
108    if(fp == 0) return 0;
109
110    while((mi = read_mapinfo(fp)) != 0) {
111            /* if not verbose, coalesce mappings from the same entity */
112        if(!verbose && milist) {
113            if((!strcmp(mi->name, milist->name) && (mi->name[0] != '['))
114               || !strcmp(mi->name,"[lib_bss]")) {
115                milist->size += mi->size;
116                milist->rss += mi->rss;
117                milist->pss += mi->pss;
118                milist->shared_clean += mi->shared_clean;
119                milist->shared_dirty += mi->shared_dirty;
120                milist->private_clean += mi->private_clean;
121                milist->private_dirty += mi->private_dirty;
122                milist->end = mi->end;
123                free(mi);
124                continue;
125            }
126        }
127
128        mi->next = milist;
129        milist = mi;
130    }
131    fclose(fp);
132
133    return milist;
134}
135
136static int verbose = 0;
137static int terse = 0;
138static int addresses = 0;
139
140int show_map(int pid)
141{
142    mapinfo *milist;
143    mapinfo *mi;
144    unsigned shared_dirty = 0;
145    unsigned shared_clean = 0;
146    unsigned private_dirty = 0;
147    unsigned private_clean = 0;
148    unsigned rss = 0;
149    unsigned pss = 0;
150    unsigned size = 0;
151
152    milist = load_maps(pid, verbose);
153    if(milist == 0) {
154        fprintf(stderr,"cannot get /proc/smaps for pid %d\n", pid);
155        return 1;
156    }
157
158    if(addresses) {
159        printf("start    end      shared   private  object\n");
160        printf("-------- -------- -------- -------- ------------------------------\n");
161    } else {
162        printf("virtual                    shared   shared   private  private\n");
163        printf("size     RSS      PSS      clean    dirty    clean    dirty    object\n");
164        printf("-------- -------- -------- -------- -------- -------- -------- ------------------------------\n");
165    }
166    for(mi = milist; mi; mi = mi->next){
167        shared_clean += mi->shared_clean;
168        shared_dirty += mi->shared_dirty;
169        private_clean += mi->private_clean;
170        private_dirty += mi->private_dirty;
171        rss += mi->rss;
172        pss += mi->pss;
173        size += mi->size;
174
175        if(terse && !mi->private_dirty) continue;
176
177        if(addresses) {
178            printf("%08x %08x %8d %8d %s\n", mi->start, mi->end,
179                   mi->shared_clean + mi->shared_dirty,
180                   mi->private_clean + mi->private_dirty,
181                   mi->name);
182        } else {
183            printf("%8d %8d %8d %8d %8d %8d %8d %s\n", mi->size,
184                   mi->rss,
185                   mi->pss,
186                   mi->shared_clean, mi->shared_dirty,
187                   mi->private_clean, mi->private_dirty,
188                   mi->name);
189        }
190    }
191    if(addresses) {
192        printf("-------- -------- -------- -------- ------------------------------\n");
193        printf("                  %8d %8d TOTAL\n",
194               shared_dirty + shared_clean,
195               private_dirty + private_clean);
196    } else {
197        printf("-------- -------- -------- -------- -------- -------- -------- ------------------------------\n");
198        printf("%8d %8d %8d %8d %8d %8d %8d TOTAL\n", size,
199               rss, pss,
200               shared_clean, shared_dirty,
201               private_clean, private_dirty);
202    }
203    return 0;
204}
205
206int main(int argc, char *argv[])
207{
208    int usage = 1;
209
210    for(argc--, argv++; argc > 0; argc--, argv++) {
211        if(!strcmp(argv[0],"-v")) {
212            verbose = 1;
213            continue;
214        }
215        if(!strcmp(argv[0],"-t")) {
216            terse = 1;
217            continue;
218        }
219        if(!strcmp(argv[0],"-a")) {
220            addresses = 1;
221            continue;
222        }
223        show_map(atoi(argv[0]));
224        usage = 0;
225    }
226
227    if(usage) {
228        fprintf(stderr,
229                "showmap [-t] [-v] [-c] <pid>\n"
230                "        -t = terse (show only items with private pages)\n"
231                "        -v = verbose (don't coalesce adjacant maps)\n"
232                "        -a = addresses (show virtual memory map)\n"
233                );
234    }
235
236	return 0;
237}
238