| #include <stdio.h> |
| #include <stdlib.h> |
| #include <math.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| |
| #include <ctype.h> |
| #include <stddef.h> |
| |
| 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/<pid>/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] <pid>\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; |
| } |