blob: 799cc8571a2144c6da6dd988545915b73d9a515e [file] [log] [blame]
Mathieu Chartierc942fb42018-05-03 20:14:29 -07001/*
2 * Copyright (C) 2005 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Elliott Hughesddbe8c32015-12-08 15:13:36 -080016#include <ctype.h>
17#include <errno.h>
18#include <fcntl.h>
19#include <math.h>
20#include <stddef.h>
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080021#include <stdio.h>
22#include <stdlib.h>
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080023#include <string.h>
Mathieu Chartierc942fb42018-05-03 20:14:29 -070024#include <sys/signal.h>
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080025#include <unistd.h>
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080026
27struct mapinfo {
28 mapinfo *next;
29 unsigned start;
30 unsigned end;
31 unsigned size;
32 unsigned rss;
33 unsigned pss;
34 unsigned shared_clean;
35 unsigned shared_dirty;
36 unsigned private_clean;
37 unsigned private_dirty;
Martijn Coenen50173c32015-12-04 17:22:15 +010038 unsigned swap;
Mathieu Chartier737412d2017-07-21 12:08:14 -070039 unsigned swap_pss;
Jeff Brownf9560172011-07-11 15:29:54 -070040 int is_bss;
Dianne Hackborn42ec7352011-11-03 19:17:31 -070041 int count;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080042 char name[1];
43};
44
Felipe Lemea5aa8f92016-03-17 14:56:34 -070045static bool verbose = false;
46static bool terse = false;
47static bool addresses = false;
48static bool quiet = false;
49
Jeff Brownf9560172011-07-11 15:29:54 -070050static int is_library(const char *name) {
51 int len = strlen(name);
52 return len >= 4 && name[0] == '/'
53 && name[len - 3] == '.' && name[len - 2] == 's' && name[len - 1] == 'o';
54}
55
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080056// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /android/lib/libcomposer.so
57// 012345678901234567890123456789012345678901234567890123456789
58// 0 1 2 3 4 5
59
Jeff Brownf9560172011-07-11 15:29:54 -070060static int parse_header(const char* line, const mapinfo* prev, mapinfo** mi) {
Kenny Rootfda77ea2011-07-06 14:59:57 -070061 unsigned long start;
62 unsigned long end;
63 char name[128];
Jeff Brownf9560172011-07-11 15:29:54 -070064 int name_pos;
65 int is_bss = 0;
Kenny Rootfda77ea2011-07-06 14:59:57 -070066
Jeff Brownf9560172011-07-11 15:29:54 -070067 if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
68 *mi = NULL;
69 return -1;
Kenny Rootfda77ea2011-07-06 14:59:57 -070070 }
71
Jeff Brownf9560172011-07-11 15:29:54 -070072 while (isspace(line[name_pos])) {
73 name_pos += 1;
74 }
75
76 if (line[name_pos]) {
Mathieu Chartierc942fb42018-05-03 20:14:29 -070077 strncpy(name, line + name_pos, sizeof(name));
Jeff Brownf9560172011-07-11 15:29:54 -070078 } else {
Nick Kralevichd3cb0302011-11-18 10:37:48 -080079 if (prev && start == prev->end && is_library(prev->name)) {
Jeff Brownf9560172011-07-11 15:29:54 -070080 // anonymous mappings immediately adjacent to shared libraries
81 // usually correspond to the library BSS segment, so we use the
82 // library's own name
Mathieu Chartierc942fb42018-05-03 20:14:29 -070083 strncpy(name, prev->name, sizeof(name));
Jeff Brownf9560172011-07-11 15:29:54 -070084 is_bss = 1;
Kenny Rootfda77ea2011-07-06 14:59:57 -070085 } else {
Mathieu Chartierc942fb42018-05-03 20:14:29 -070086 strncpy(name, "[anon]", sizeof(name));
Kenny Rootfda77ea2011-07-06 14:59:57 -070087 }
88 }
Greg Kaiser4d509722018-05-07 06:39:34 -070089 name[sizeof(name) - 1] = '\0'; // Assure we're null-terminated.
Kenny Rootfda77ea2011-07-06 14:59:57 -070090
91 const int name_size = strlen(name) + 1;
Elliott Hughesddbe8c32015-12-08 15:13:36 -080092 struct mapinfo* info = reinterpret_cast<mapinfo*>(calloc(1, sizeof(mapinfo) + name_size));
Kenny Rootfda77ea2011-07-06 14:59:57 -070093 if (info == NULL) {
Jeff Brownf9560172011-07-11 15:29:54 -070094 fprintf(stderr, "out of memory\n");
95 exit(1);
Kenny Rootfda77ea2011-07-06 14:59:57 -070096 }
97
98 info->start = start;
99 info->end = end;
Jeff Brownf9560172011-07-11 15:29:54 -0700100 info->is_bss = is_bss;
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700101 info->count = 1;
Greg Kaiser4d509722018-05-07 06:39:34 -0700102 memcpy(info->name, name, name_size);
Kenny Rootfda77ea2011-07-06 14:59:57 -0700103
104 *mi = info;
Kenny Rootfda77ea2011-07-06 14:59:57 -0700105 return 0;
106}
107
Jeff Brownf9560172011-07-11 15:29:54 -0700108static int parse_field(mapinfo* mi, const char* line) {
Kenny Rootfda77ea2011-07-06 14:59:57 -0700109 char field[64];
Jeff Brown9a213e02014-04-18 16:47:20 -0700110 int len;
Kenny Rootfda77ea2011-07-06 14:59:57 -0700111
Jeff Brown9a213e02014-04-18 16:47:20 -0700112 if (sscanf(line, "%63s %n", field, &len) == 1
113 && *field && field[strlen(field) - 1] == ':') {
114 int size;
115 if (sscanf(line + len, "%d kB", &size) == 1) {
116 if (!strcmp(field, "Size:")) {
117 mi->size = size;
118 } else if (!strcmp(field, "Rss:")) {
119 mi->rss = size;
120 } else if (!strcmp(field, "Pss:")) {
121 mi->pss = size;
122 } else if (!strcmp(field, "Shared_Clean:")) {
123 mi->shared_clean = size;
124 } else if (!strcmp(field, "Shared_Dirty:")) {
125 mi->shared_dirty = size;
126 } else if (!strcmp(field, "Private_Clean:")) {
127 mi->private_clean = size;
128 } else if (!strcmp(field, "Private_Dirty:")) {
129 mi->private_dirty = size;
Martijn Coenen50173c32015-12-04 17:22:15 +0100130 } else if (!strcmp(field, "Swap:")) {
131 mi->swap = size;
Mathieu Chartier737412d2017-07-21 12:08:14 -0700132 } else if (!strcmp(field, "SwapPss:")) {
133 mi->swap_pss = size;
Jeff Brown9a213e02014-04-18 16:47:20 -0700134 }
135 }
136 return 0;
Kenny Rootfda77ea2011-07-06 14:59:57 -0700137 }
Jeff Brown9a213e02014-04-18 16:47:20 -0700138 return -1;
Kenny Rootfda77ea2011-07-06 14:59:57 -0700139}
140
Jeff Brownf9560172011-07-11 15:29:54 -0700141static int order_before(const mapinfo *a, const mapinfo *b, int sort_by_address) {
142 if (sort_by_address) {
143 return a->start < b->start
144 || (a->start == b->start && a->end < b->end);
145 } else {
146 return strcmp(a->name, b->name) < 0;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800147 }
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800148}
149
Jeff Brownf9560172011-07-11 15:29:54 -0700150static void enqueue_map(mapinfo **head, mapinfo *map, int sort_by_address, int coalesce_by_name) {
151 mapinfo *prev = NULL;
152 mapinfo *current = *head;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800153
Jeff Brownf9560172011-07-11 15:29:54 -0700154 if (!map) {
155 return;
156 }
157
158 for (;;) {
159 if (current && coalesce_by_name && !strcmp(map->name, current->name)) {
160 current->size += map->size;
161 current->rss += map->rss;
162 current->pss += map->pss;
163 current->shared_clean += map->shared_clean;
164 current->shared_dirty += map->shared_dirty;
165 current->private_clean += map->private_clean;
166 current->private_dirty += map->private_dirty;
Martijn Coenen50173c32015-12-04 17:22:15 +0100167 current->swap += map->swap;
Mathieu Chartier737412d2017-07-21 12:08:14 -0700168 current->swap_pss += map->swap_pss;
Jeff Brownf9560172011-07-11 15:29:54 -0700169 current->is_bss &= map->is_bss;
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700170 current->count++;
Jeff Brownf9560172011-07-11 15:29:54 -0700171 free(map);
172 break;
173 }
174
175 if (!current || order_before(map, current, sort_by_address)) {
176 if (prev) {
177 prev->next = map;
178 } else {
179 *head = map;
180 }
181 map->next = current;
182 break;
183 }
184
185 prev = current;
186 current = current->next;
187 }
188}
189
Mathieu Chartierc942fb42018-05-03 20:14:29 -0700190static mapinfo *load_maps_from_file(const char* filename,
191 int sort_by_address,
192 int coalesce_by_name) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800193 FILE *fp;
Jeff Brownf9560172011-07-11 15:29:54 -0700194 char line[1024];
195 mapinfo *head = NULL;
196 mapinfo *current = NULL;
197 int len;
198
Mathieu Chartierc942fb42018-05-03 20:14:29 -0700199 fp = fopen(filename, "r");
Kenny Rootfda77ea2011-07-06 14:59:57 -0700200 if (fp == 0) {
Mathieu Chartierc942fb42018-05-03 20:14:29 -0700201 if (!quiet) fprintf(stderr, "cannot open %s: %s\n", filename, strerror(errno));
Kenny Rootfda77ea2011-07-06 14:59:57 -0700202 return NULL;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800203 }
Kenny Rootfda77ea2011-07-06 14:59:57 -0700204
Jeff Brownf9560172011-07-11 15:29:54 -0700205 while (fgets(line, sizeof(line), fp) != 0) {
206 len = strlen(line);
207 if (line[len - 1] == '\n') {
208 line[--len] = 0;
209 }
210
211 if (current != NULL && !parse_field(current, line)) {
212 continue;
213 }
214
215 mapinfo *next;
216 if (!parse_header(line, current, &next)) {
217 enqueue_map(&head, current, sort_by_address, coalesce_by_name);
218 current = next;
219 continue;
220 }
221
222 fprintf(stderr, "warning: could not parse map info line: %s\n", line);
223 }
224
225 enqueue_map(&head, current, sort_by_address, coalesce_by_name);
226
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800227 fclose(fp);
Kenny Rootfda77ea2011-07-06 14:59:57 -0700228
Jeff Brownf9560172011-07-11 15:29:54 -0700229 if (!head) {
Mathieu Chartierc942fb42018-05-03 20:14:29 -0700230 if (!quiet) fprintf(stderr, "could not read %s\n", filename);
Kenny Rootfda77ea2011-07-06 14:59:57 -0700231 return NULL;
232 }
Kenny Rootfda77ea2011-07-06 14:59:57 -0700233
Jeff Brownf9560172011-07-11 15:29:54 -0700234 return head;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800235}
236
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700237static void print_header()
238{
Elliott Hughesddbe8c32015-12-08 15:13:36 -0800239 const char *addr1 = addresses ? " start end " : "";
240 const char *addr2 = addresses ? " addr addr " : "";
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700241
Elliott Hughesddbe8c32015-12-08 15:13:36 -0800242 printf("%s virtual shared shared private private\n", addr1);
Mathieu Chartier737412d2017-07-21 12:08:14 -0700243 printf("%s size RSS PSS clean dirty clean dirty swap swapPSS", addr2);
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700244 if (!verbose && !addresses) {
245 printf(" # ");
246 }
247 printf("object\n");
248}
249
250static void print_divider()
251{
252 if (addresses) {
253 printf("-------- -------- ");
254 }
Mathieu Chartier737412d2017-07-21 12:08:14 -0700255 printf("-------- -------- -------- -------- -------- -------- -------- -------- -------- ");
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700256 if (!verbose && !addresses) {
257 printf("---- ");
258 }
259 printf("------------------------------\n");
260}
261
Elliott Hughesddbe8c32015-12-08 15:13:36 -0800262static void print_mi(mapinfo *mi, bool total)
263{
264 if (addresses) {
265 if (total) {
266 printf(" ");
267 } else {
268 printf("%08x %08x ", mi->start, mi->end);
269 }
270 }
Mathieu Chartier737412d2017-07-21 12:08:14 -0700271 printf("%8d %8d %8d %8d %8d %8d %8d %8d %8d ", mi->size,
Elliott Hughesddbe8c32015-12-08 15:13:36 -0800272 mi->rss,
273 mi->pss,
274 mi->shared_clean, mi->shared_dirty,
Mathieu Chartier737412d2017-07-21 12:08:14 -0700275 mi->private_clean, mi->private_dirty, mi->swap, mi->swap_pss);
Elliott Hughesddbe8c32015-12-08 15:13:36 -0800276 if (!verbose && !addresses) {
277 printf("%4d ", mi->count);
278 }
279}
280
Mathieu Chartierc942fb42018-05-03 20:14:29 -0700281static int show_map(char* filename)
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800282{
Elliott Hughesddbe8c32015-12-08 15:13:36 -0800283 mapinfo total;
284 memset(&total, 0, sizeof(total));
Jeff Brownf9560172011-07-11 15:29:54 -0700285
Mathieu Chartierc942fb42018-05-03 20:14:29 -0700286 mapinfo *milist = load_maps_from_file(filename, addresses, !verbose && !addresses);
Kenny Rootfda77ea2011-07-06 14:59:57 -0700287 if (milist == NULL) {
Felipe Lemea5aa8f92016-03-17 14:56:34 -0700288 return quiet ? 0 : 1;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800289 }
290
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700291 print_header();
292 print_divider();
Kenny Rootfda77ea2011-07-06 14:59:57 -0700293
Elliott Hughesddbe8c32015-12-08 15:13:36 -0800294 for (mapinfo *mi = milist; mi;) {
Kenny Rootfda77ea2011-07-06 14:59:57 -0700295 mapinfo* last = mi;
296
Elliott Hughesddbe8c32015-12-08 15:13:36 -0800297 total.shared_clean += mi->shared_clean;
298 total.shared_dirty += mi->shared_dirty;
299 total.private_clean += mi->private_clean;
300 total.private_dirty += mi->private_dirty;
301 total.swap += mi->swap;
Mathieu Chartier737412d2017-07-21 12:08:14 -0700302 total.swap_pss += mi->swap_pss;
Elliott Hughesddbe8c32015-12-08 15:13:36 -0800303 total.rss += mi->rss;
304 total.pss += mi->pss;
305 total.size += mi->size;
306 total.count += mi->count;
Felipe Lemea5aa8f92016-03-17 14:56:34 -0700307
Kenny Rootfda77ea2011-07-06 14:59:57 -0700308 if (terse && !mi->private_dirty) {
309 goto out;
310 }
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800311
Elliott Hughesddbe8c32015-12-08 15:13:36 -0800312 print_mi(mi, false);
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700313 printf("%s%s\n", mi->name, mi->is_bss ? " [bss]" : "");
Kenny Rootfda77ea2011-07-06 14:59:57 -0700314
315out:
316 mi = mi->next;
317 free(last);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800318 }
Kenny Rootfda77ea2011-07-06 14:59:57 -0700319
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700320 print_divider();
321 print_header();
322 print_divider();
Jeff Brownf9560172011-07-11 15:29:54 -0700323
Elliott Hughesddbe8c32015-12-08 15:13:36 -0800324 print_mi(&total, true);
Dianne Hackborn42ec7352011-11-03 19:17:31 -0700325 printf("TOTAL\n");
Kenny Rootfda77ea2011-07-06 14:59:57 -0700326
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800327 return 0;
328}
329
Mathieu Chartierc942fb42018-05-03 20:14:29 -0700330int main(int argc, char *argv[]) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800331 int usage = 1;
Jeff Brownf9560172011-07-11 15:29:54 -0700332 int result = 0;
Mathieu Chartierc942fb42018-05-03 20:14:29 -0700333 char *filename = NULL;
334 char temp_filename[128] = {};
335 bool file_mode = false;
Jeff Brownf9560172011-07-11 15:29:54 -0700336
JP Abgrall80cb1552012-05-11 14:09:59 -0700337 signal(SIGPIPE, SIG_IGN);
Mathieu Chartierc942fb42018-05-03 20:14:29 -0700338 for (int i = 1; i < argc; ++i) {
339 char* arg = argv[i];
Jeff Brownf9560172011-07-11 15:29:54 -0700340 if (!strcmp(arg,"-v")) {
Elliott Hughesddbe8c32015-12-08 15:13:36 -0800341 verbose = true;
Mathieu Chartierc942fb42018-05-03 20:14:29 -0700342 } else if (!strcmp(arg,"-t")) {
Elliott Hughesddbe8c32015-12-08 15:13:36 -0800343 terse = true;
Mathieu Chartierc942fb42018-05-03 20:14:29 -0700344 } else if (!strcmp(arg,"-a")) {
Elliott Hughesddbe8c32015-12-08 15:13:36 -0800345 addresses = true;
Mathieu Chartierc942fb42018-05-03 20:14:29 -0700346 } else if (!strcmp(arg,"-q")) {
Felipe Lemea5aa8f92016-03-17 14:56:34 -0700347 quiet = true;
Mathieu Chartierc942fb42018-05-03 20:14:29 -0700348 } else if (!strcmp(arg,"-f")) {
349 file_mode = true;
350 } else {
351 if (i + 1 != argc) {
352 fprintf(stderr, "too many arguments\n");
353 break;
Jeff Brownf9560172011-07-11 15:29:54 -0700354 }
Mathieu Chartierc942fb42018-05-03 20:14:29 -0700355 if (file_mode) {
356 filename = arg;
357 usage = 0;
358 } else {
359 char *argend;
360 int pid = strtol(arg, &argend, 10);
361 if (*arg && !*argend) {
362 usage = 0;
363 snprintf(temp_filename, sizeof(temp_filename), "/proc/%d/smaps", pid);
364 filename = temp_filename;
365 }
366 }
367
368 if (filename != nullptr) {
369 if (show_map(filename)) {
370 result = 1;
371 }
372 break;
373 }
374 fprintf(stderr, "unrecognized argument: %s\n", arg);
Jeff Brownf9560172011-07-11 15:29:54 -0700375 break;
376 }
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800377 }
378
Kenny Rootfda77ea2011-07-06 14:59:57 -0700379 if (usage) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800380 fprintf(stderr,
Mathieu Chartierc942fb42018-05-03 20:14:29 -0700381 "showmap [-t] [-v] [-c] [-q] [-f] <pid|file>\n"
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800382 " -t = terse (show only items with private pages)\n"
Jeff Brownf9560172011-07-11 15:29:54 -0700383 " -v = verbose (don't coalesce maps with the same name)\n"
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800384 " -a = addresses (show virtual memory map)\n"
Felipe Lemea5aa8f92016-03-17 14:56:34 -0700385 " -q = quiet (don't show error if map could not be read)\n"
Mathieu Chartierc942fb42018-05-03 20:14:29 -0700386 " -f = read from input file instead of pid\n"
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800387 );
Jeff Brownf9560172011-07-11 15:29:54 -0700388 result = 1;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800389 }
390
Jeff Brownf9560172011-07-11 15:29:54 -0700391 return result;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800392}