blob: d90f269f2d5d65381ea87ec9924aa95a3c4c5fd8 [file] [log] [blame]
The Android Open Source Projecte16cb842009-03-03 19:32:58 -08001#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;
Jeff Brownf9560172011-07-11 15:29:54 -070025 int is_bss;
Dianne Hackborn42ec7352011-11-03 19:17:31 -070026 int count;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080027 char name[1];
28};
29
Jeff Brownf9560172011-07-11 15:29:54 -070030static int is_library(const char *name) {
31 int len = strlen(name);
32 return len >= 4 && name[0] == '/'
33 && name[len - 3] == '.' && name[len - 2] == 's' && name[len - 1] == 'o';
34}
35
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080036// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /android/lib/libcomposer.so
37// 012345678901234567890123456789012345678901234567890123456789
38// 0 1 2 3 4 5
39
Jeff Brownf9560172011-07-11 15:29:54 -070040static int parse_header(const char* line, const mapinfo* prev, mapinfo** mi) {
Kenny Rootfda77ea2011-07-06 14:59:57 -070041 unsigned long start;
42 unsigned long end;
43 char name[128];
Jeff Brownf9560172011-07-11 15:29:54 -070044 int name_pos;
45 int is_bss = 0;
Kenny Rootfda77ea2011-07-06 14:59:57 -070046
Jeff Brownf9560172011-07-11 15:29:54 -070047 if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
48 *mi = NULL;
49 return -1;
Kenny Rootfda77ea2011-07-06 14:59:57 -070050 }
51
Jeff Brownf9560172011-07-11 15:29:54 -070052 while (isspace(line[name_pos])) {
53 name_pos += 1;
54 }
55
56 if (line[name_pos]) {
57 strlcpy(name, line + name_pos, sizeof(name));
58 } else {
Nick Kralevichd3cb0302011-11-18 10:37:48 -080059 if (prev && start == prev->end && is_library(prev->name)) {
Jeff Brownf9560172011-07-11 15:29:54 -070060 // anonymous mappings immediately adjacent to shared libraries
61 // usually correspond to the library BSS segment, so we use the
62 // library's own name
63 strlcpy(name, prev->name, sizeof(name));
64 is_bss = 1;
Kenny Rootfda77ea2011-07-06 14:59:57 -070065 } else {
66 strlcpy(name, "[anon]", sizeof(name));
67 }
68 }
69
70 const int name_size = strlen(name) + 1;
71 struct mapinfo* info = calloc(1, sizeof(mapinfo) + name_size);
72 if (info == NULL) {
Jeff Brownf9560172011-07-11 15:29:54 -070073 fprintf(stderr, "out of memory\n");
74 exit(1);
Kenny Rootfda77ea2011-07-06 14:59:57 -070075 }
76
77 info->start = start;
78 info->end = end;
Jeff Brownf9560172011-07-11 15:29:54 -070079 info->is_bss = is_bss;
Dianne Hackborn42ec7352011-11-03 19:17:31 -070080 info->count = 1;
Kenny Rootfda77ea2011-07-06 14:59:57 -070081 strlcpy(info->name, name, name_size);
82
83 *mi = info;
Kenny Rootfda77ea2011-07-06 14:59:57 -070084 return 0;
85}
86
Jeff Brownf9560172011-07-11 15:29:54 -070087static int parse_field(mapinfo* mi, const char* line) {
Kenny Rootfda77ea2011-07-06 14:59:57 -070088 char field[64];
Jeff Brown9a213e02014-04-18 16:47:20 -070089 int len;
Kenny Rootfda77ea2011-07-06 14:59:57 -070090
Jeff Brown9a213e02014-04-18 16:47:20 -070091 if (sscanf(line, "%63s %n", field, &len) == 1
92 && *field && field[strlen(field) - 1] == ':') {
93 int size;
94 if (sscanf(line + len, "%d kB", &size) == 1) {
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;
Kenny Rootfda77ea2011-07-06 14:59:57 -0700112 }
Jeff Brown9a213e02014-04-18 16:47:20 -0700113 return -1;
Kenny Rootfda77ea2011-07-06 14:59:57 -0700114}
115
Jeff Brownf9560172011-07-11 15:29:54 -0700116static int order_before(const mapinfo *a, const mapinfo *b, int sort_by_address) {
117 if (sort_by_address) {
118 return a->start < b->start
119 || (a->start == b->start && a->end < b->end);
120 } else {
121 return strcmp(a->name, b->name) < 0;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800122 }
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800123}
124
Jeff Brownf9560172011-07-11 15:29:54 -0700125static void enqueue_map(mapinfo **head, mapinfo *map, int sort_by_address, int coalesce_by_name) {
126 mapinfo *prev = NULL;
127 mapinfo *current = *head;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800128
Jeff Brownf9560172011-07-11 15:29:54 -0700129 if (!map) {
130 return;
131 }
132
133 for (;;) {
134 if (current && coalesce_by_name && !strcmp(map->name, current->name)) {
135 current->size += map->size;
136 current->rss += map->rss;
137 current->pss += map->pss;
138 current->shared_clean += map->shared_clean;
139 current->shared_dirty += map->shared_dirty;
140 current->private_clean += map->private_clean;
141 current->private_dirty += map->private_dirty;
142 current->is_bss &= map->is_bss;
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700143 current->count++;
Jeff Brownf9560172011-07-11 15:29:54 -0700144 free(map);
145 break;
146 }
147
148 if (!current || order_before(map, current, sort_by_address)) {
149 if (prev) {
150 prev->next = map;
151 } else {
152 *head = map;
153 }
154 map->next = current;
155 break;
156 }
157
158 prev = current;
159 current = current->next;
160 }
161}
162
163static mapinfo *load_maps(int pid, int sort_by_address, int coalesce_by_name)
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800164{
Jeff Brownf9560172011-07-11 15:29:54 -0700165 char fn[128];
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800166 FILE *fp;
Jeff Brownf9560172011-07-11 15:29:54 -0700167 char line[1024];
168 mapinfo *head = NULL;
169 mapinfo *current = NULL;
170 int len;
171
172 snprintf(fn, sizeof(fn), "/proc/%d/smaps", pid);
173 fp = fopen(fn, "r");
Kenny Rootfda77ea2011-07-06 14:59:57 -0700174 if (fp == 0) {
175 fprintf(stderr, "cannot open /proc/%d/smaps: %s\n", pid, strerror(errno));
176 return NULL;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800177 }
Kenny Rootfda77ea2011-07-06 14:59:57 -0700178
Jeff Brownf9560172011-07-11 15:29:54 -0700179 while (fgets(line, sizeof(line), fp) != 0) {
180 len = strlen(line);
181 if (line[len - 1] == '\n') {
182 line[--len] = 0;
183 }
184
185 if (current != NULL && !parse_field(current, line)) {
186 continue;
187 }
188
189 mapinfo *next;
190 if (!parse_header(line, current, &next)) {
191 enqueue_map(&head, current, sort_by_address, coalesce_by_name);
192 current = next;
193 continue;
194 }
195
196 fprintf(stderr, "warning: could not parse map info line: %s\n", line);
197 }
198
199 enqueue_map(&head, current, sort_by_address, coalesce_by_name);
200
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800201 fclose(fp);
Kenny Rootfda77ea2011-07-06 14:59:57 -0700202
Jeff Brownf9560172011-07-11 15:29:54 -0700203 if (!head) {
Kenny Rootfda77ea2011-07-06 14:59:57 -0700204 fprintf(stderr, "could not read /proc/%d/smaps\n", pid);
205 return NULL;
206 }
Kenny Rootfda77ea2011-07-06 14:59:57 -0700207
Jeff Brownf9560172011-07-11 15:29:54 -0700208 return head;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800209}
210
211static int verbose = 0;
212static int terse = 0;
213static int addresses = 0;
214
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700215static void print_header()
216{
217 if (addresses) {
218 printf(" start end ");
219 }
220 printf(" virtual shared shared private private\n");
221
222 if (addresses) {
223 printf(" addr addr ");
224 }
225 printf(" size RSS PSS clean dirty clean dirty ");
226 if (!verbose && !addresses) {
227 printf(" # ");
228 }
229 printf("object\n");
230}
231
232static void print_divider()
233{
234 if (addresses) {
235 printf("-------- -------- ");
236 }
237 printf("-------- -------- -------- -------- -------- -------- -------- ");
238 if (!verbose && !addresses) {
239 printf("---- ");
240 }
241 printf("------------------------------\n");
242}
243
Jeff Brownf9560172011-07-11 15:29:54 -0700244static int show_map(int pid)
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800245{
246 mapinfo *milist;
247 mapinfo *mi;
248 unsigned shared_dirty = 0;
249 unsigned shared_clean = 0;
250 unsigned private_dirty = 0;
251 unsigned private_clean = 0;
252 unsigned rss = 0;
253 unsigned pss = 0;
254 unsigned size = 0;
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700255 unsigned count = 0;
Jeff Brownf9560172011-07-11 15:29:54 -0700256
257 milist = load_maps(pid, addresses, !verbose && !addresses);
Kenny Rootfda77ea2011-07-06 14:59:57 -0700258 if (milist == NULL) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800259 return 1;
260 }
261
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700262 print_header();
263 print_divider();
Kenny Rootfda77ea2011-07-06 14:59:57 -0700264
265 for (mi = milist; mi;) {
266 mapinfo* last = mi;
267
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800268 shared_clean += mi->shared_clean;
269 shared_dirty += mi->shared_dirty;
270 private_clean += mi->private_clean;
271 private_dirty += mi->private_dirty;
272 rss += mi->rss;
273 pss += mi->pss;
274 size += mi->size;
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700275 count += mi->count;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800276
Kenny Rootfda77ea2011-07-06 14:59:57 -0700277 if (terse && !mi->private_dirty) {
278 goto out;
279 }
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800280
Kenny Rootfda77ea2011-07-06 14:59:57 -0700281 if (addresses) {
Jeff Brownf9560172011-07-11 15:29:54 -0700282 printf("%08x %08x ", mi->start, mi->end);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800283 }
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700284 printf("%8d %8d %8d %8d %8d %8d %8d ", mi->size,
Jeff Brownf9560172011-07-11 15:29:54 -0700285 mi->rss,
286 mi->pss,
287 mi->shared_clean, mi->shared_dirty,
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700288 mi->private_clean, mi->private_dirty);
289 if (!verbose && !addresses) {
290 printf("%4d ", mi->count);
291 }
292 printf("%s%s\n", mi->name, mi->is_bss ? " [bss]" : "");
Kenny Rootfda77ea2011-07-06 14:59:57 -0700293
294out:
295 mi = mi->next;
296 free(last);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800297 }
Kenny Rootfda77ea2011-07-06 14:59:57 -0700298
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700299 print_divider();
300 print_header();
301 print_divider();
Jeff Brownf9560172011-07-11 15:29:54 -0700302
303 if (addresses) {
304 printf(" ");
305 }
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700306 printf("%8d %8d %8d %8d %8d %8d %8d ", size,
Jeff Brownf9560172011-07-11 15:29:54 -0700307 rss, pss,
308 shared_clean, shared_dirty,
309 private_clean, private_dirty);
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700310 if (!verbose && !addresses) {
311 printf("%4d ", count);
312 }
313 printf("TOTAL\n");
Kenny Rootfda77ea2011-07-06 14:59:57 -0700314
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800315 return 0;
316}
317
318int main(int argc, char *argv[])
319{
320 int usage = 1;
Jeff Brownf9560172011-07-11 15:29:54 -0700321 int result = 0;
322 int pid;
323 char *arg;
324 char *argend;
325
JP Abgrall80cb1552012-05-11 14:09:59 -0700326 signal(SIGPIPE, SIG_IGN);
Kenny Rootfda77ea2011-07-06 14:59:57 -0700327 for (argc--, argv++; argc > 0; argc--, argv++) {
Jeff Brownf9560172011-07-11 15:29:54 -0700328 arg = argv[0];
329 if (!strcmp(arg,"-v")) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800330 verbose = 1;
331 continue;
332 }
Jeff Brownf9560172011-07-11 15:29:54 -0700333 if (!strcmp(arg,"-t")) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800334 terse = 1;
335 continue;
336 }
Jeff Brownf9560172011-07-11 15:29:54 -0700337 if (!strcmp(arg,"-a")) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800338 addresses = 1;
339 continue;
340 }
Jeff Brownf9560172011-07-11 15:29:54 -0700341 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;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800355 }
356
Kenny Rootfda77ea2011-07-06 14:59:57 -0700357 if (usage) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800358 fprintf(stderr,
359 "showmap [-t] [-v] [-c] <pid>\n"
360 " -t = terse (show only items with private pages)\n"
Jeff Brownf9560172011-07-11 15:29:54 -0700361 " -v = verbose (don't coalesce maps with the same name)\n"
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800362 " -a = addresses (show virtual memory map)\n"
363 );
Jeff Brownf9560172011-07-11 15:29:54 -0700364 result = 1;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800365 }
366
Jeff Brownf9560172011-07-11 15:29:54 -0700367 return result;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800368}