summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2011-07-11 15:29:54 -0700
committerJeff Brown <jeffbrown@google.com>2011-07-13 21:34:47 -0700
commitf95601708a46c098582eb836fe25889866858ad9 (patch)
treeb9649c198b62adece5edc30b7049f22e4f8598f4
parentfda77ea946fa4c58775d5ff63895cf9d41d3b568 (diff)
downloadextras-f95601708a46c098582eb836fe25889866858ad9.tar.gz
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
-rw-r--r--showmap/showmap.c276
1 files changed, 156 insertions, 120 deletions
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] <pid>\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;
}