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 char name[1];
27 };
28
is_library(const char * name)29 static 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
parse_header(const char * line,const mapinfo * prev,mapinfo ** mi)39 static 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
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 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
load_maps(int pid,int sort_by_address,int coalesce_by_name)160 static 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
208 static int verbose = 0;
209 static int terse = 0;
210 static int addresses = 0;
211
show_map(int pid)212 static 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
269 out:
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
main(int argc,char * argv[])290 int 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