18 unsigned shared_clean;
19 unsigned shared_dirty;
20 unsigned private_clean;
21 unsigned private_dirty;
28 static bool verbose = false;
29 static bool terse = false;
30 static bool addresses = false;
31 static bool quiet = false;
33 static int is_library(const char *name) {
34 int len = strlen(name);
35 return len >= 4 && name[0] == '/'
36 && name[len - 3] == '.' && name[len - 2] == 's' && name[len - 1] == 'o';
39 // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /android/lib/libcomposer.so
40 // 012345678901234567890123456789012345678901234567890123456789
43 static int parse_header(const char* line, const mapinfo* prev, mapinfo** mi) {
50 if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
55 while (isspace(line[name_pos])) {
60 strlcpy(name, line + name_pos, sizeof(name));
62 if (prev && start == prev->end && is_library(prev->name)) {
63 // anonymous mappings immediately adjacent to shared libraries
64 // usually correspond to the library BSS segment, so we use the
66 strlcpy(name, prev->name, sizeof(name));
69 strlcpy(name, "[anon]", sizeof(name));
73 const int name_size = strlen(name) + 1;
74 struct mapinfo* info = reinterpret_cast<mapinfo*>(calloc(1, sizeof(mapinfo) + name_size));
76 fprintf(stderr, "out of memory\n");
82 info->is_bss = is_bss;
84 strlcpy(info->name, name, name_size);
90 static int parse_field(mapinfo* mi, const char* line) {
94 if (sscanf(line, "%63s %n", field, &len) == 1
95 && *field && field[strlen(field) - 1] == ':') {
97 if (sscanf(line + len, "%d kB", &size) == 1) {
98 if (!strcmp(field, "Size:")) {
100 } else if (!strcmp(field, "Rss:")) {
102 } else if (!strcmp(field, "Pss:")) {
104 } else if (!strcmp(field, "Shared_Clean:")) {
105 mi->shared_clean = size;
106 } else if (!strcmp(field, "Shared_Dirty:")) {
107 mi->shared_dirty = size;
108 } else if (!strcmp(field, "Private_Clean:")) {
109 mi->private_clean = size;
110 } else if (!strcmp(field, "Private_Dirty:")) {
111 mi->private_dirty = size;
112 } else if (!strcmp(field, "Swap:")) {
121 static int order_before(const mapinfo *a, const mapinfo *b, int sort_by_address) {
122 if (sort_by_address) {
123 return a->start < b->start
124 || (a->start == b->start && a->end < b->end);
126 return strcmp(a->name, b->name) < 0;
130 static void enqueue_map(mapinfo **head, mapinfo *map, int sort_by_address, int coalesce_by_name) {
131 mapinfo *prev = NULL;
132 mapinfo *current = *head;
139 if (current && coalesce_by_name && !strcmp(map->name, current->name)) {
140 current->size += map->size;
141 current->rss += map->rss;
142 current->pss += map->pss;
143 current->shared_clean += map->shared_clean;
144 current->shared_dirty += map->shared_dirty;
145 current->private_clean += map->private_clean;
146 current->private_dirty += map->private_dirty;
147 current->swap += map->swap;
148 current->is_bss &= map->is_bss;
154 if (!current || order_before(map, current, sort_by_address)) {
165 current = current->next;
169 static mapinfo *load_maps(int pid, int sort_by_address, int coalesce_by_name)
174 mapinfo *head = NULL;
175 mapinfo *current = NULL;
178 snprintf(fn, sizeof(fn), "/proc/%d/smaps", pid);
181 if (!quiet) fprintf(stderr, "cannot open /proc/%d/smaps: %s\n", pid, strerror(errno));
185 while (fgets(line, sizeof(line), fp) != 0) {
187 if (line[len - 1] == '\n') {
191 if (current != NULL && !parse_field(current, line)) {
196 if (!parse_header(line, current, &next)) {
197 enqueue_map(&head, current, sort_by_address, coalesce_by_name);
202 fprintf(stderr, "warning: could not parse map info line: %s\n", line);
205 enqueue_map(&head, current, sort_by_address, coalesce_by_name);
210 if (!quiet) fprintf(stderr, "could not read /proc/%d/smaps\n", pid);
217 static void print_header()
219 const char *addr1 = addresses ? " start end " : "";
220 const char *addr2 = addresses ? " addr addr " : "";
222 printf("%s virtual shared shared private private\n", addr1);
223 printf("%s size RSS PSS clean dirty clean dirty swap ", addr2);
224 if (!verbose && !addresses) {
230 static void print_divider()
233 printf("-------- -------- ");
235 printf("-------- -------- -------- -------- -------- -------- -------- -------- ");
236 if (!verbose && !addresses) {
239 printf("------------------------------\n");
242 static void print_mi(mapinfo *mi, bool total)
248 printf("%08x %08x ", mi->start, mi->end);
251 printf("%8d %8d %8d %8d %8d %8d %8d %8d ", mi->size,
254 mi->shared_clean, mi->shared_dirty,
255 mi->private_clean, mi->private_dirty, mi->swap);
256 if (!verbose && !addresses) {
257 printf("%4d ", mi->count);
261 static int show_map(int pid)
264 memset(&total, 0, sizeof(total));
266 mapinfo *milist = load_maps(pid, addresses, !verbose && !addresses);
267 if (milist == NULL) {
268 return quiet ? 0 : 1;
274 for (mapinfo *mi = milist; mi;) {
277 total.shared_clean += mi->shared_clean;
278 total.shared_dirty += mi->shared_dirty;
279 total.private_clean += mi->private_clean;
280 total.private_dirty += mi->private_dirty;
281 total.swap += mi->swap;
282 total.rss += mi->rss;
283 total.pss += mi->pss;
284 total.size += mi->size;
285 total.count += mi->count;
287 if (terse && !mi->private_dirty) {
292 printf("%s%s\n", mi->name, mi->is_bss ? " [bss]" : "");
303 print_mi(&total, true);
309 int main(int argc, char *argv[])
317 signal(SIGPIPE, SIG_IGN);
318 for (argc--, argv++; argc > 0; argc--, argv++) {
320 if (!strcmp(arg,"-v")) {
324 if (!strcmp(arg,"-t")) {
328 if (!strcmp(arg,"-a")) {
332 if (!strcmp(arg,"-q")) {
337 fprintf(stderr, "too many arguments\n");
340 pid = strtol(arg, &argend, 10);
341 if (*arg && !*argend) {
348 fprintf(stderr, "unrecognized argument: %s\n", arg);
354 "showmap [-t] [-v] [-c] [-q] <pid>\n"
355 " -t = terse (show only items with private pages)\n"
356 " -v = verbose (don't coalesce maps with the same name)\n"
357 " -a = addresses (show virtual memory map)\n"
358 " -q = quiet (don't show error if map could not be read)\n"