Home | History | Annotate | Download | only in showmap
      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <math.h>
      4 #include <string.h>
      5 #include <errno.h>
      6 #include <unistd.h>
      7 #include <fcntl.h>
      8 
      9 #include <ctype.h>
     10 #include <stddef.h>
     11 
     12 typedef struct mapinfo mapinfo;
     13 
     14 struct mapinfo {
     15     mapinfo *next;
     16     unsigned start;
     17     unsigned end;
     18     unsigned size;
     19     unsigned rss;
     20     unsigned pss;
     21     unsigned shared_clean;
     22     unsigned shared_dirty;
     23     unsigned private_clean;
     24     unsigned private_dirty;
     25     int is_bss;
     26     int count;
     27     char name[1];
     28 };
     29 
     30 static int is_library(const char *name) {
     31     int len = strlen(name);
     32     return len >= 4 && name[0] == '/'
     33             && name[len - 3] == '.' && name[len - 2] == 's' && name[len - 1] == 'o';
     34 }
     35 
     36 // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /android/lib/libcomposer.so
     37 // 012345678901234567890123456789012345678901234567890123456789
     38 // 0         1         2         3         4         5
     39 
     40 static int parse_header(const char* line, const mapinfo* prev, mapinfo** mi) {
     41     unsigned long start;
     42     unsigned long end;
     43     char name[128];
     44     int name_pos;
     45     int is_bss = 0;
     46 
     47     if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
     48         *mi = NULL;
     49         return -1;
     50     }
     51 
     52     while (isspace(line[name_pos])) {
     53         name_pos += 1;
     54     }
     55 
     56     if (line[name_pos]) {
     57         strlcpy(name, line + name_pos, sizeof(name));
     58     } else {
     59         if (prev && start == prev->end && is_library(prev->name)) {
     60             // anonymous mappings immediately adjacent to shared libraries
     61             // usually correspond to the library BSS segment, so we use the
     62             // library's own name
     63             strlcpy(name, prev->name, sizeof(name));
     64             is_bss = 1;
     65         } else {
     66             strlcpy(name, "[anon]", sizeof(name));
     67         }
     68     }
     69 
     70     const int name_size = strlen(name) + 1;
     71     struct mapinfo* info = calloc(1, sizeof(mapinfo) + name_size);
     72     if (info == NULL) {
     73         fprintf(stderr, "out of memory\n");
     74         exit(1);
     75     }
     76 
     77     info->start = start;
     78     info->end = end;
     79     info->is_bss = is_bss;
     80     info->count = 1;
     81     strlcpy(info->name, name, name_size);
     82 
     83     *mi = info;
     84     return 0;
     85 }
     86 
     87 static int parse_field(mapinfo* mi, const char* line) {
     88     char field[64];
     89     int len;
     90 
     91     if (sscanf(line, "%63s %n", field, &len) == 1
     92             && *field && field[strlen(field) - 1] == ':') {
     93         int size;
     94         if (sscanf(line + len, "%d kB", &size) == 1) {
     95             if (!strcmp(field, "Size:")) {
     96                 mi->size = size;
     97             } else if (!strcmp(field, "Rss:")) {
     98                 mi->rss = size;
     99             } else if (!strcmp(field, "Pss:")) {
    100                 mi->pss = size;
    101             } else if (!strcmp(field, "Shared_Clean:")) {
    102                 mi->shared_clean = size;
    103             } else if (!strcmp(field, "Shared_Dirty:")) {
    104                 mi->shared_dirty = size;
    105             } else if (!strcmp(field, "Private_Clean:")) {
    106                 mi->private_clean = size;
    107             } else if (!strcmp(field, "Private_Dirty:")) {
    108                 mi->private_dirty = size;
    109             }
    110         }
    111         return 0;
    112     }
    113     return -1;
    114 }
    115 
    116 static int order_before(const mapinfo *a, const mapinfo *b, int sort_by_address) {
    117     if (sort_by_address) {
    118         return a->start < b->start
    119                 || (a->start == b->start && a->end < b->end);
    120     } else {
    121         return strcmp(a->name, b->name) < 0;
    122     }
    123 }
    124 
    125 static void enqueue_map(mapinfo **head, mapinfo *map, int sort_by_address, int coalesce_by_name) {
    126     mapinfo *prev = NULL;
    127     mapinfo *current = *head;
    128 
    129     if (!map) {
    130         return;
    131     }
    132 
    133     for (;;) {
    134         if (current && coalesce_by_name && !strcmp(map->name, current->name)) {
    135             current->size += map->size;
    136             current->rss += map->rss;
    137             current->pss += map->pss;
    138             current->shared_clean += map->shared_clean;
    139             current->shared_dirty += map->shared_dirty;
    140             current->private_clean += map->private_clean;
    141             current->private_dirty += map->private_dirty;
    142             current->is_bss &= map->is_bss;
    143             current->count++;
    144             free(map);
    145             break;
    146         }
    147 
    148         if (!current || order_before(map, current, sort_by_address)) {
    149             if (prev) {
    150                 prev->next = map;
    151             } else {
    152                 *head = map;
    153             }
    154             map->next = current;
    155             break;
    156         }
    157 
    158         prev = current;
    159         current = current->next;
    160     }
    161 }
    162 
    163 static mapinfo *load_maps(int pid, int sort_by_address, int coalesce_by_name)
    164 {
    165     char fn[128];
    166     FILE *fp;
    167     char line[1024];
    168     mapinfo *head = NULL;
    169     mapinfo *current = NULL;
    170     int len;
    171 
    172     snprintf(fn, sizeof(fn), "/proc/%d/smaps", pid);
    173     fp = fopen(fn, "r");
    174     if (fp == 0) {
    175         fprintf(stderr, "cannot open /proc/%d/smaps: %s\n", pid, strerror(errno));
    176         return NULL;
    177     }
    178 
    179     while (fgets(line, sizeof(line), fp) != 0) {
    180         len = strlen(line);
    181         if (line[len - 1] == '\n') {
    182             line[--len] = 0;
    183         }
    184 
    185         if (current != NULL && !parse_field(current, line)) {
    186             continue;
    187         }
    188 
    189         mapinfo *next;
    190         if (!parse_header(line, current, &next)) {
    191             enqueue_map(&head, current, sort_by_address, coalesce_by_name);
    192             current = next;
    193             continue;
    194         }
    195 
    196         fprintf(stderr, "warning: could not parse map info line: %s\n", line);
    197     }
    198 
    199     enqueue_map(&head, current, sort_by_address, coalesce_by_name);
    200 
    201     fclose(fp);
    202 
    203     if (!head) {
    204         fprintf(stderr, "could not read /proc/%d/smaps\n", pid);
    205         return NULL;
    206     }
    207 
    208     return head;
    209 }
    210 
    211 static int verbose = 0;
    212 static int terse = 0;
    213 static int addresses = 0;
    214 
    215 static void print_header()
    216 {
    217     if (addresses) {
    218         printf("   start      end ");
    219     }
    220     printf(" virtual                     shared   shared  private  private\n");
    221 
    222     if (addresses) {
    223         printf("    addr     addr ");
    224     }
    225     printf("    size      RSS      PSS    clean    dirty    clean    dirty ");
    226     if (!verbose && !addresses) {
    227         printf("   # ");
    228     }
    229     printf("object\n");
    230 }
    231 
    232 static void print_divider()
    233 {
    234     if (addresses) {
    235         printf("-------- -------- ");
    236     }
    237     printf("-------- -------- -------- -------- -------- -------- -------- ");
    238     if (!verbose && !addresses) {
    239         printf("---- ");
    240     }
    241     printf("------------------------------\n");
    242 }
    243 
    244 static int show_map(int pid)
    245 {
    246     mapinfo *milist;
    247     mapinfo *mi;
    248     unsigned shared_dirty = 0;
    249     unsigned shared_clean = 0;
    250     unsigned private_dirty = 0;
    251     unsigned private_clean = 0;
    252     unsigned rss = 0;
    253     unsigned pss = 0;
    254     unsigned size = 0;
    255     unsigned count = 0;
    256 
    257     milist = load_maps(pid, addresses, !verbose && !addresses);
    258     if (milist == NULL) {
    259         return 1;
    260     }
    261 
    262     print_header();
    263     print_divider();
    264 
    265     for (mi = milist; mi;) {
    266         mapinfo* last = mi;
    267 
    268         shared_clean += mi->shared_clean;
    269         shared_dirty += mi->shared_dirty;
    270         private_clean += mi->private_clean;
    271         private_dirty += mi->private_dirty;
    272         rss += mi->rss;
    273         pss += mi->pss;
    274         size += mi->size;
    275         count += mi->count;
    276 
    277         if (terse && !mi->private_dirty) {
    278             goto out;
    279         }
    280 
    281         if (addresses) {
    282             printf("%08x %08x ", mi->start, mi->end);
    283         }
    284         printf("%8d %8d %8d %8d %8d %8d %8d ", mi->size,
    285                mi->rss,
    286                mi->pss,
    287                mi->shared_clean, mi->shared_dirty,
    288                mi->private_clean, mi->private_dirty);
    289         if (!verbose && !addresses) {
    290             printf("%4d ", mi->count);
    291         }
    292         printf("%s%s\n", mi->name, mi->is_bss ? " [bss]" : "");
    293 
    294 out:
    295         mi = mi->next;
    296         free(last);
    297     }
    298 
    299     print_divider();
    300     print_header();
    301     print_divider();
    302 
    303     if (addresses) {
    304         printf("                  ");
    305     }
    306     printf("%8d %8d %8d %8d %8d %8d %8d ", size,
    307             rss, pss,
    308             shared_clean, shared_dirty,
    309             private_clean, private_dirty);
    310     if (!verbose && !addresses) {
    311         printf("%4d ", count);
    312     }
    313     printf("TOTAL\n");
    314 
    315     return 0;
    316 }
    317 
    318 int main(int argc, char *argv[])
    319 {
    320     int usage = 1;
    321     int result = 0;
    322     int pid;
    323     char *arg;
    324     char *argend;
    325 
    326     signal(SIGPIPE, SIG_IGN);
    327     for (argc--, argv++; argc > 0; argc--, argv++) {
    328         arg = argv[0];
    329         if (!strcmp(arg,"-v")) {
    330             verbose = 1;
    331             continue;
    332         }
    333         if (!strcmp(arg,"-t")) {
    334             terse = 1;
    335             continue;
    336         }
    337         if (!strcmp(arg,"-a")) {
    338             addresses = 1;
    339             continue;
    340         }
    341         if (argc != 1) {
    342             fprintf(stderr, "too many arguments\n");
    343             break;
    344         }
    345         pid = strtol(arg, &argend, 10);
    346         if (*arg && !*argend) {
    347             usage = 0;
    348             if (show_map(pid)) {
    349                 result = 1;
    350             }
    351             break;
    352         }
    353         fprintf(stderr, "unrecognized argument: %s\n", arg);
    354         break;
    355     }
    356 
    357     if (usage) {
    358         fprintf(stderr,
    359                 "showmap [-t] [-v] [-c] <pid>\n"
    360                 "        -t = terse (show only items with private pages)\n"
    361                 "        -v = verbose (don't coalesce maps with the same name)\n"
    362                 "        -a = addresses (show virtual memory map)\n"
    363                 );
    364         result = 1;
    365     }
    366 
    367     return result;
    368 }
    369