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