#include #include #include #include #include #include #include #include #include typedef struct mapinfo mapinfo; struct mapinfo { mapinfo *next; unsigned start; unsigned end; unsigned size; unsigned rss; unsigned pss; unsigned shared_clean; unsigned shared_dirty; unsigned private_clean; unsigned private_dirty; char name[1]; }; // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /android/lib/libcomposer.so // 012345678901234567890123456789012345678901234567890123456789 // 0 1 2 3 4 5 mapinfo *read_mapinfo(FILE *fp) { char line[1024]; mapinfo *mi; int len; int skip; again: skip = 0; if(fgets(line, 1024, fp) == 0) return 0; len = strlen(line); if(len < 1) return 0; line[--len] = 0; mi = calloc(1, sizeof(mapinfo) + len + 16); if(mi == 0) return 0; mi->start = strtoul(line, 0, 16); mi->end = strtoul(line + 9, 0, 16); if(len < 50) { if((mi->start >= 0x10000000) && (mi->start < 0x40000000)) { strcpy(mi->name, "[stack]"); } else if(mi->start > 0x50000000) { strcpy(mi->name, "[lib_bss]"); } else { strcpy(mi->name, "[anon]"); } } else { strcpy(mi->name, line + 49); } if(fgets(line, 1024, fp) == 0) goto oops; if(sscanf(line, "Size: %d kB", &mi->size) != 1) goto oops; if(fgets(line, 1024, fp) == 0) goto oops; if(sscanf(line, "Rss: %d kB", &mi->rss) != 1) goto oops; if(fgets(line, 1024, fp) == 0) goto oops; if(sscanf(line, "Pss: %d kB", &mi->pss) == 1) if(fgets(line, 1024, fp) == 0) goto oops; if(sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) != 1) goto oops; if(fgets(line, 1024, fp) == 0) goto oops; if(sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) != 1) goto oops; if(fgets(line, 1024, fp) == 0) goto oops; if(sscanf(line, "Private_Clean: %d kB", &mi->private_clean) != 1) goto oops; if(fgets(line, 1024, fp) == 0) goto oops; if(sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) != 1) goto oops; if(fgets(line, 1024, fp) == 0) goto oops; // Referenced if(fgets(line, 1024, fp) == 0) goto oops; // Swap if(fgets(line, 1024, fp) == 0) goto oops; // KernelPageSize if(fgets(line, 1024, fp) == 0) goto oops; // MMUPageSize if(skip) { free(mi); goto again; } return mi; oops: fprintf(stderr, "WARNING: Format of /proc//smaps has changed!\n"); free(mi); return 0; } mapinfo *load_maps(int pid, int verbose) { char tmp[128]; FILE *fp; mapinfo *milist = 0; mapinfo *mi; sprintf(tmp, "/proc/%d/smaps", pid); fp = fopen(tmp, "r"); if(fp == 0) return 0; while((mi = read_mapinfo(fp)) != 0) { /* if not verbose, coalesce mappings from the same entity */ if(!verbose && milist) { if((!strcmp(mi->name, milist->name) && (mi->name[0] != '[')) || !strcmp(mi->name,"[lib_bss]")) { milist->size += mi->size; milist->rss += mi->rss; milist->pss += mi->pss; milist->shared_clean += mi->shared_clean; milist->shared_dirty += mi->shared_dirty; milist->private_clean += mi->private_clean; milist->private_dirty += mi->private_dirty; milist->end = mi->end; free(mi); continue; } } mi->next = milist; milist = mi; } fclose(fp); return milist; } static int verbose = 0; static int terse = 0; static int addresses = 0; int show_map(int pid) { mapinfo *milist; mapinfo *mi; unsigned shared_dirty = 0; unsigned shared_clean = 0; unsigned private_dirty = 0; unsigned private_clean = 0; unsigned rss = 0; unsigned pss = 0; unsigned size = 0; milist = load_maps(pid, verbose); if(milist == 0) { fprintf(stderr,"cannot get /proc/smaps for pid %d\n", pid); 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"); } for(mi = milist; mi; mi = mi->next){ shared_clean += mi->shared_clean; shared_dirty += mi->shared_dirty; private_clean += mi->private_clean; private_dirty += mi->private_dirty; rss += mi->rss; pss += mi->pss; size += mi->size; if(terse && !mi->private_dirty) continue; 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); } } 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); } return 0; } int main(int argc, char *argv[]) { int usage = 1; for(argc--, argv++; argc > 0; argc--, argv++) { if(!strcmp(argv[0],"-v")) { verbose = 1; continue; } if(!strcmp(argv[0],"-t")) { terse = 1; continue; } if(!strcmp(argv[0],"-a")) { addresses = 1; continue; } show_map(atoi(argv[0])); usage = 0; } 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" " -a = addresses (show virtual memory map)\n" ); } return 0; }