From f95601708a46c098582eb836fe25889866858ad9 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Mon, 11 Jul 2011 15:29:54 -0700 Subject: showmap enhancements Fixed a bug where maps were being coalesced even when they were not necessarily coalesced. Enhanced the BSS segment detection heuristic so that it takes the map start / end addresses into account and displays the inferred map type directly. Automatically sort all maps by name or address as appropriate. Fixed a bug parsing maps with paths that contain whitespace. Enhanced the -a argument to show the same columns as the normal mode but just prefixed with the virtual memory map information. Change-Id: Ice78afb0c5b597683cb1a1ba65e3c10f10258f7c --- showmap/showmap.c | 276 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 156 insertions(+), 120 deletions(-) (limited to 'showmap') diff --git a/showmap/showmap.c b/showmap/showmap.c index 0a9a58ab..575f4985 100644 --- a/showmap/showmap.c +++ b/showmap/showmap.c @@ -22,30 +22,47 @@ struct mapinfo { unsigned shared_dirty; unsigned private_clean; unsigned private_dirty; + int is_bss; char name[1]; }; +static int is_library(const char *name) { + int len = strlen(name); + return len >= 4 && name[0] == '/' + && name[len - 3] == '.' && name[len - 2] == 's' && name[len - 1] == 'o'; +} + // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /android/lib/libcomposer.so // 012345678901234567890123456789012345678901234567890123456789 // 0 1 2 3 4 5 -int parse_header(char* line, int len, mapinfo** mi) { +static int parse_header(const char* line, const mapinfo* prev, mapinfo** mi) { unsigned long start; unsigned long end; char name[128]; + int name_pos; + int is_bss = 0; - name[0] = '\0'; + if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) { + *mi = NULL; + return -1; + } - // Sometimes the name is missing. - if (sscanf(line, "%lx-%lx %*s %*lx %*x:%*x %*ld %127s", &start, &end, name) < 2) { - return 0; + while (isspace(line[name_pos])) { + name_pos += 1; } - if (name[0] == '\0') { + if (line[name_pos]) { + strlcpy(name, line + name_pos, sizeof(name)); + } else { if ((start >= 0x10000000) && (start < 0x40000000)) { strlcpy(name, "[stack]", sizeof(name)); - } else if (start > 0x50000000) { - strlcpy(name, "[lib_bss]", sizeof(name)); + } else if (prev && start == prev->end && is_library(prev->name)) { + // anonymous mappings immediately adjacent to shared libraries + // usually correspond to the library BSS segment, so we use the + // library's own name + strlcpy(name, prev->name, sizeof(name)); + is_bss = 1; } else { strlcpy(name, "[anon]", sizeof(name)); } @@ -54,19 +71,20 @@ int parse_header(char* line, int len, mapinfo** mi) { const int name_size = strlen(name) + 1; struct mapinfo* info = calloc(1, sizeof(mapinfo) + name_size); if (info == NULL) { - return -1; + fprintf(stderr, "out of memory\n"); + exit(1); } info->start = start; info->end = end; + info->is_bss = is_bss; strlcpy(info->name, name, name_size); *mi = info; - return 0; } -int parse_field(mapinfo* mi, char* line) { +static int parse_field(mapinfo* mi, const char* line) { char field[64]; int size; @@ -93,107 +111,105 @@ int parse_field(mapinfo* mi, char* line) { return 0; } -mapinfo *read_mapinfo(FILE *fp) +static int order_before(const mapinfo *a, const mapinfo *b, int sort_by_address) { + if (sort_by_address) { + return a->start < b->start + || (a->start == b->start && a->end < b->end); + } else { + return strcmp(a->name, b->name) < 0; + } +} + +static void enqueue_map(mapinfo **head, mapinfo *map, int sort_by_address, int coalesce_by_name) { + mapinfo *prev = NULL; + mapinfo *current = *head; + + if (!map) { + return; + } + + for (;;) { + if (current && coalesce_by_name && !strcmp(map->name, current->name)) { + current->size += map->size; + current->rss += map->rss; + current->pss += map->pss; + current->shared_clean += map->shared_clean; + current->shared_dirty += map->shared_dirty; + current->private_clean += map->private_clean; + current->private_dirty += map->private_dirty; + current->is_bss &= map->is_bss; + free(map); + break; + } + + if (!current || order_before(map, current, sort_by_address)) { + if (prev) { + prev->next = map; + } else { + *head = map; + } + map->next = current; + break; + } + + prev = current; + current = current->next; + } +} + +static mapinfo *load_maps(int pid, int sort_by_address, int coalesce_by_name) { + char fn[128]; + FILE *fp; char line[1024]; + mapinfo *head = NULL; mapinfo *current = NULL; int len; - int skip; + + snprintf(fn, sizeof(fn), "/proc/%d/smaps", pid); + fp = fopen(fn, "r"); + if (fp == 0) { + fprintf(stderr, "cannot open /proc/%d/smaps: %s\n", pid, strerror(errno)); + return NULL; + } while (fgets(line, sizeof(line), fp) != 0) { - if (current != NULL) { - parse_field(current, line); + len = strlen(line); + if (line[len - 1] == '\n') { + line[--len] = 0; } - len = strlen(line); - if (len < 1) { - return NULL; + if (current != NULL && !parse_field(current, line)) { + continue; } - line[--len] = 0; - mapinfo *next = NULL; - if (parse_header(line, len, &next) < 0) { - goto err; - } else if (next != NULL) { - next->next = current; + mapinfo *next; + if (!parse_header(line, current, &next)) { + enqueue_map(&head, current, sort_by_address, coalesce_by_name); current = next; continue; } - } - - return current; -err: - while (current != NULL) { - mapinfo* next = current->next; - free(current); - current = next; + fprintf(stderr, "warning: could not parse map info line: %s\n", line); } - return NULL; -} - + enqueue_map(&head, current, sort_by_address, coalesce_by_name); -mapinfo *load_maps(int pid, int verbose) -{ - char tmp[128]; - FILE *fp; - mapinfo *milist = 0; - mapinfo *mi; - - snprintf(tmp, sizeof(tmp), "/proc/%d/smaps", pid); - fp = fopen(tmp, "r"); - if (fp == 0) { - fprintf(stderr, "cannot open /proc/%d/smaps: %s\n", pid, strerror(errno)); - return NULL; - } - - milist = read_mapinfo(fp); fclose(fp); - if (!milist) { + if (!head) { fprintf(stderr, "could not read /proc/%d/smaps\n", pid); return NULL; } - - /* if not verbose, coalesce mappings from the same entity */ - if (!verbose) { - mapinfo* current = milist; - mapinfo* last = NULL; - - while (current != NULL) { - mapinfo* next = current->next; - - if (last != NULL - && ((current->name[0] != '[' && !strcmp(last->name, current->name)) - || !strcmp(current->name, "[lib_bss]"))) { - last->size += current->size; - last->rss += current->rss; - last->pss += current->pss; - last->shared_clean += current->shared_clean; - last->shared_dirty += current->shared_dirty; - last->private_clean += current->private_clean; - last->private_dirty += current->private_dirty; - last->end = current->end; - - last->next = next; - free(current); - } else { - last = current; - } - - current = next; - } - } - return milist; + return head; } static int verbose = 0; static int terse = 0; static int addresses = 0; -int show_map(int pid) +static int show_map(int pid) { mapinfo *milist; mapinfo *mi; @@ -204,20 +220,26 @@ int show_map(int pid) unsigned rss = 0; unsigned pss = 0; unsigned size = 0; - - milist = load_maps(pid, verbose); + + milist = load_maps(pid, addresses, !verbose && !addresses); if (milist == NULL) { return 1; } if (addresses) { - printf("start end shared private object\n"); - printf("-------- -------- -------- -------- ------------------------------\n"); - } else { - printf("virtual shared shared private private\n"); - printf("size RSS PSS clean dirty clean dirty object\n"); - printf("-------- -------- -------- -------- -------- -------- -------- ------------------------------\n"); + printf("start end "); } + printf("virtual shared shared private private\n"); + + if (addresses) { + printf("addr addr "); + } + printf("size RSS PSS clean dirty clean dirty object\n"); + + if (addresses) { + printf("-------- -------- "); + } + printf("-------- -------- -------- -------- -------- -------- -------- ------------------------------\n"); for (mi = milist; mi;) { mapinfo* last = mi; @@ -235,18 +257,14 @@ int show_map(int pid) } if (addresses) { - printf("%08x %08x %8d %8d %s\n", mi->start, mi->end, - mi->shared_clean + mi->shared_dirty, - mi->private_clean + mi->private_dirty, - mi->name); - } else { - printf("%8d %8d %8d %8d %8d %8d %8d %s\n", mi->size, - mi->rss, - mi->pss, - mi->shared_clean, mi->shared_dirty, - mi->private_clean, mi->private_dirty, - mi->name); + printf("%08x %08x ", mi->start, mi->end); } + printf("%8d %8d %8d %8d %8d %8d %8d %s%s\n", mi->size, + mi->rss, + mi->pss, + mi->shared_clean, mi->shared_dirty, + mi->private_clean, mi->private_dirty, + mi->name, mi->is_bss ? " [bss]" : ""); out: mi = mi->next; @@ -254,17 +272,17 @@ out: } if (addresses) { - printf("-------- -------- -------- -------- ------------------------------\n"); - printf(" %8d %8d TOTAL\n", - shared_dirty + shared_clean, - private_dirty + private_clean); - } else { - printf("-------- -------- -------- -------- -------- -------- -------- ------------------------------\n"); - printf("%8d %8d %8d %8d %8d %8d %8d TOTAL\n", size, - rss, pss, - shared_clean, shared_dirty, - private_clean, private_dirty); + printf("-------- -------- "); } + printf("-------- -------- -------- -------- -------- -------- -------- ------------------------------\n"); + + if (addresses) { + printf(" "); + } + printf("%8d %8d %8d %8d %8d %8d %8d TOTAL\n", size, + rss, pss, + shared_clean, shared_dirty, + private_clean, private_dirty); return 0; } @@ -272,32 +290,50 @@ out: int main(int argc, char *argv[]) { int usage = 1; - + int result = 0; + int pid; + char *arg; + char *argend; + for (argc--, argv++; argc > 0; argc--, argv++) { - if (!strcmp(argv[0],"-v")) { + arg = argv[0]; + if (!strcmp(arg,"-v")) { verbose = 1; continue; } - if (!strcmp(argv[0],"-t")) { + if (!strcmp(arg,"-t")) { terse = 1; continue; } - if (!strcmp(argv[0],"-a")) { + if (!strcmp(arg,"-a")) { addresses = 1; continue; } - show_map(atoi(argv[0])); - usage = 0; + if (argc != 1) { + fprintf(stderr, "too many arguments\n"); + break; + } + pid = strtol(arg, &argend, 10); + if (*arg && !*argend) { + usage = 0; + if (show_map(pid)) { + result = 1; + } + break; + } + fprintf(stderr, "unrecognized argument: %s\n", arg); + break; } if (usage) { fprintf(stderr, "showmap [-t] [-v] [-c] \n" " -t = terse (show only items with private pages)\n" - " -v = verbose (don't coalesce adjacant maps)\n" + " -v = verbose (don't coalesce maps with the same name)\n" " -a = addresses (show virtual memory map)\n" ); + result = 1; } - return 0; + return result; } -- cgit v1.2.3