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