Home | History | Annotate | Download | only in librank
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <assert.h>
     18 #include <dirent.h>
     19 #include <errno.h>
     20 #include <getopt.h>
     21 #include <stdbool.h>
     22 #include <stdlib.h>
     23 #include <string.h>
     24 #include <sys/types.h>
     25 #include <unistd.h>
     26 
     27 #include <pagemap/pagemap.h>
     28 
     29 #define MAX_CMDLINE 256
     30 
     31 struct process_info {
     32     pid_t pid;
     33     char cmdline[MAX_CMDLINE];
     34 };
     35 
     36 struct mapping_info {
     37     struct process_info *proc;
     38     pm_memusage_t usage;
     39 };
     40 
     41 struct library_info {
     42     struct library_info *next;
     43     char *name;
     44     struct mapping_info **mappings;
     45     size_t mappings_count;
     46     size_t mappings_size;
     47     pm_memusage_t total_usage;
     48 };
     49 
     50 static void usage(char *myname);
     51 static int getprocname(pid_t pid, char *buf, size_t len);
     52 static int numcmp(long long a, long long b);
     53 static int licmp(const void *a, const void *b);
     54 
     55 char *library_name_blacklist[] = { "[heap]", "[stack]", "", NULL };
     56 
     57 #define declare_sort(field) \
     58     static int sort_by_ ## field (const void *a, const void *b)
     59 
     60 declare_sort(vss);
     61 declare_sort(rss);
     62 declare_sort(pss);
     63 declare_sort(uss);
     64 declare_sort(swap);
     65 
     66 #define INIT_LIBRARIES 16
     67 #define INIT_MAPPINGS 4
     68 
     69 static int order;
     70 
     71 struct library_info **libraries;
     72 size_t libraries_count;
     73 size_t libraries_size;
     74 
     75 struct library_info *get_library(const char *name, bool all) {
     76     size_t i;
     77     struct library_info *library;
     78 
     79     if (!all) {
     80         for (i = 0; library_name_blacklist[i]; i++)
     81             if (!strcmp(name, library_name_blacklist[i]))
     82                 return NULL;
     83     } else {
     84         if (name[0] == 0) {
     85             name = "[anon]";
     86         }
     87     }
     88 
     89     for (i = 0; i < libraries_count; i++) {
     90         if (!strcmp(libraries[i]->name, name))
     91             return libraries[i];
     92     }
     93 
     94     if (libraries_count >= libraries_size) {
     95         libraries = realloc(libraries, 2 * libraries_size * sizeof(struct library_info *));
     96         if (!libraries) {
     97             fprintf(stderr, "Couldn't resize libraries array: %s\n", strerror(errno));
     98             exit(EXIT_FAILURE);
     99         }
    100         libraries_size = 2 * libraries_size;
    101     }
    102 
    103     library = calloc(1, sizeof(*library));
    104     if (!library) {
    105         fprintf(stderr, "Couldn't allocate space for library struct: %s\n", strerror(errno));
    106         exit(EXIT_FAILURE);
    107     }
    108     library->name = malloc(strlen(name) + 1);
    109     if (!library->name) {
    110         fprintf(stderr, "Couldn't allocate space for library name: %s\n", strerror(errno));
    111         exit(EXIT_FAILURE);
    112     }
    113     strcpy(library->name, name);
    114     library->mappings = malloc(INIT_MAPPINGS * sizeof(struct mapping_info *));
    115     if (!library->mappings) {
    116         fprintf(stderr, "Couldn't allocate space for library mappings array: %s\n", strerror(errno));
    117         exit(EXIT_FAILURE);
    118     }
    119     library->mappings_count = 0; library->mappings_size = INIT_MAPPINGS;
    120     pm_memusage_zero(&library->total_usage);
    121 
    122     libraries[libraries_count++] = library;
    123 
    124     return library;
    125 }
    126 
    127 struct mapping_info *get_mapping(struct library_info *library, struct process_info *proc) {
    128     struct mapping_info *mapping;
    129     size_t i;
    130 
    131     for (i = 0; i < library->mappings_count; i++) {
    132         if (library->mappings[i]->proc == proc)
    133             return library->mappings[i];
    134     }
    135 
    136     if (library->mappings_count >= library->mappings_size) {
    137         library->mappings = realloc(library->mappings,
    138             2 * library->mappings_size * sizeof(struct mapping*));
    139         if (!library->mappings) {
    140             fprintf(stderr, "Couldn't resize mappings array: %s\n", strerror(errno));
    141             exit(EXIT_FAILURE);
    142         }
    143         library->mappings_size = 2 * library->mappings_size;
    144     }
    145 
    146     mapping = calloc(1, sizeof(*mapping));
    147     if (!mapping) {
    148         fprintf(stderr, "Couldn't allocate space for mapping struct: %s\n", strerror(errno));
    149         exit(EXIT_FAILURE);
    150     }
    151     mapping->proc = proc;
    152     pm_memusage_zero(&mapping->usage);
    153 
    154     library->mappings[library->mappings_count++] = mapping;
    155 
    156     return mapping;
    157 }
    158 
    159 struct process_info *get_process(pid_t pid) {
    160     struct process_info *process;
    161 
    162     process = calloc(1, sizeof(*process));
    163     if (!process) {
    164         fprintf(stderr, "Couldn't allocate space for process struct: %s\n", strerror(errno));
    165         exit(EXIT_FAILURE);
    166     }
    167 
    168     process->pid = pid;
    169     getprocname(pid, process->cmdline, sizeof(process->cmdline));
    170 
    171     return process;
    172 }
    173 
    174 static int parse_perm(const char *perm)
    175 {
    176     int ret = 0;
    177 
    178     while (*perm) {
    179         switch(*perm) {
    180         case 'r':
    181             ret |= PM_MAP_READ;
    182             break;
    183         case 'w':
    184             ret |= PM_MAP_WRITE;
    185             break;
    186         case 'x':
    187             ret |= PM_MAP_EXEC;
    188             break;
    189         default:
    190             fprintf(stderr, "Unknown permission '%c'\n", *perm);
    191             exit(EXIT_FAILURE);
    192         }
    193         perm++;
    194     }
    195     return ret;
    196 }
    197 
    198 int main(int argc, char *argv[]) {
    199     char cmdline[256];
    200     char *prefix;
    201     size_t prefix_len;
    202     int (*compfn)(const void *a, const void *b);
    203 
    204     pm_kernel_t *ker;
    205     pm_process_t *proc;
    206 
    207     pid_t *pids;
    208     size_t num_procs;
    209 
    210     pm_map_t **maps;
    211     size_t num_maps;
    212     pm_memusage_t map_usage;
    213 
    214     struct library_info *li, **lis;
    215     struct mapping_info *mi, **mis;
    216     struct process_info *pi;
    217 
    218     size_t i, j;
    219     int error;
    220     int perm;
    221     bool all;
    222     uint64_t required_flags;
    223     uint64_t flags_mask;
    224 
    225     bool has_swap = false;
    226 
    227     signal(SIGPIPE, SIG_IGN);
    228     compfn = &sort_by_pss;
    229     order = -1;
    230     prefix = NULL;
    231     prefix_len = 0;
    232     opterr = 0;
    233     perm = 0;
    234     all = false;
    235     required_flags = 0;
    236     flags_mask = 0;
    237 
    238     while (1) {
    239         int c;
    240         const struct option longopts[] = {
    241             {"all", 0, 0, 'a'},
    242             {"cached", 0, 0, 'c'},
    243             {"nocached", 0, 0, 'C'},
    244             {"ksm", 0, 0, 'k'},
    245             {"help", 0, 0, 'h'},
    246             {"pss", 0, 0, 'p'},
    247             {"uss", 0, 0, 'u'},
    248             {"vss", 0, 0, 'v'},
    249             {"rss", 0, 0, 'r'},
    250             {"swap", 0, 0, 's'},
    251             {"reverse", 0, 0, 'R'},
    252             {"path", required_argument, 0, 'P'},
    253             {"perm", required_argument, 0, 'm'},
    254             {0, 0, 0, 0}
    255         };
    256         c = getopt_long(argc, argv, "acChkm:pP:uvrsR", longopts, NULL);
    257         if (c < 0) {
    258             break;
    259         }
    260         /* Alphabetical cases */
    261         switch (c) {
    262         case 'a':
    263             all = true;
    264             break;
    265         case 'c':
    266             required_flags = 0;
    267             flags_mask = PM_PAGE_SWAPBACKED;
    268             break;
    269         case 'C':
    270             required_flags = PM_PAGE_SWAPBACKED;
    271             flags_mask = PM_PAGE_SWAPBACKED;
    272             break;
    273         case 'k':
    274             required_flags = PM_PAGE_KSM;
    275             flags_mask = PM_PAGE_KSM;
    276             break;
    277         case 'h':
    278             usage(argv[0]);
    279             exit(EXIT_SUCCESS);
    280         case 'm':
    281             perm = parse_perm(optarg);
    282             break;
    283         case 'p':
    284             compfn = &sort_by_pss;
    285             break;
    286         case 'P':
    287             prefix = optarg;
    288             prefix_len = strlen(prefix);
    289             break;
    290         case 'u':
    291             compfn = &sort_by_uss;
    292             break;
    293         case 'v':
    294             compfn = &sort_by_vss;
    295             break;
    296         case 'r':
    297             compfn = &sort_by_rss;
    298             break;
    299         case 's':
    300             compfn = &sort_by_swap;
    301             break;
    302         case 'R':
    303             order *= -1;
    304             break;
    305         case '?':
    306             fprintf(stderr, "Invalid argument \"%s\".\n", argv[optind - 1]);
    307             usage(argv[0]);
    308             exit(EXIT_FAILURE);
    309         default:
    310             abort();
    311         }
    312     }
    313 
    314     argc -= optind;
    315     argv += optind;
    316 
    317     libraries = malloc(INIT_LIBRARIES * sizeof(struct library_info *));
    318     libraries_count = 0; libraries_size = INIT_LIBRARIES;
    319 
    320     error = pm_kernel_create(&ker);
    321     if (error) {
    322         fprintf(stderr, "Error initializing kernel interface -- "
    323                         "does this kernel have pagemap?\n");
    324         exit(EXIT_FAILURE);
    325     }
    326 
    327     error = pm_kernel_pids(ker, &pids, &num_procs);
    328     if (error) {
    329         fprintf(stderr, "Error listing processes.\n");
    330         exit(EXIT_FAILURE);
    331     }
    332 
    333     for (i = 0; i < num_procs; i++) {
    334         error = pm_process_create(ker, pids[i], &proc);
    335         if (error) {
    336             fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]);
    337             continue;
    338         }
    339 
    340         pi = get_process(pids[i]);
    341 
    342         error = pm_process_maps(proc, &maps, &num_maps);
    343         if (error) {
    344             fprintf(stderr, "Error listing maps for process %d.\n", proc->pid);
    345             exit(EXIT_FAILURE);
    346         }
    347 
    348         for (j = 0; j < num_maps; j++) {
    349             if (prefix && (strncmp(pm_map_name(maps[j]), prefix, prefix_len)))
    350                 continue;
    351 
    352             if (perm && (pm_map_flags(maps[j]) & PM_MAP_PERMISSIONS) != perm)
    353                 continue;
    354 
    355             li = get_library(pm_map_name(maps[j]), all);
    356             if (!li)
    357                 continue;
    358 
    359             mi = get_mapping(li, pi);
    360 
    361             error = pm_map_usage_flags(maps[j], &map_usage, flags_mask,
    362                                        required_flags);
    363             if (error) {
    364                 fprintf(stderr, "Error getting map memory usage of "
    365                                 "map %s in process %d.\n",
    366                         pm_map_name(maps[j]), proc->pid);
    367                 exit(EXIT_FAILURE);
    368             }
    369 
    370             if (map_usage.swap) {
    371                 has_swap = true;
    372             }
    373 
    374             pm_memusage_add(&mi->usage, &map_usage);
    375             pm_memusage_add(&li->total_usage, &map_usage);
    376         }
    377     }
    378 
    379     printf(" %6s   %6s   %6s   %6s   %6s  ", "RSStot", "VSS", "RSS", "PSS", "USS");
    380 
    381     if (has_swap) {
    382         printf(" %6s  ", "Swap");
    383     }
    384 
    385     printf("Name/PID\n");
    386     fflush(stdout);
    387 
    388     qsort(libraries, libraries_count, sizeof(libraries[0]), &licmp);
    389 
    390     for (i = 0; i < libraries_count; i++) {
    391         li = libraries[i];
    392 
    393         printf("%6zdK   %6s   %6s   %6s   %6s  ", li->total_usage.pss / 1024, "", "", "", "");
    394         if (has_swap) {
    395             printf(" %6s  ", "");
    396         }
    397         printf("%s\n", li->name);
    398         fflush(stdout);
    399 
    400         qsort(li->mappings, li->mappings_count, sizeof(li->mappings[0]), compfn);
    401 
    402         for (j = 0; j < li->mappings_count; j++) {
    403             mi = li->mappings[j];
    404             pi = mi->proc;
    405             printf(   " %6s  %6zdK  %6zdK  %6zdK  %6zdK  ", "",
    406                 mi->usage.vss / 1024,
    407                 mi->usage.rss / 1024,
    408                 mi->usage.pss / 1024,
    409                 mi->usage.uss / 1024);
    410             if (has_swap) {
    411                 printf("%6zdK  ", mi->usage.swap / 1024);
    412             }
    413             printf("  %s [%d]\n",
    414                 pi->cmdline,
    415                 pi->pid);
    416         }
    417         printf("\n");
    418         fflush(stdout);
    419     }
    420 
    421     return 0;
    422 }
    423 
    424 static void usage(char *myname) {
    425     fprintf(stderr, "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -s | -h ]\n"
    426                     "\n"
    427                     "Sort options:\n"
    428                     "    -v  Sort processes by VSS.\n"
    429                     "    -r  Sort processes by RSS.\n"
    430                     "    -p  Sort processes by PSS.\n"
    431                     "    -u  Sort processes by USS.\n"
    432                     "    -s  Sort processes by swap.\n"
    433                     "        (Default sort order is PSS.)\n"
    434                     "    -a  Show all mappings, including stack, heap and anon.\n"
    435                     "    -P /path  Limit libraries displayed to those in path.\n"
    436                     "    -R  Reverse sort order (default is descending).\n"
    437                     "    -m [r][w][x] Only list pages that exactly match permissions\n"
    438                     "    -c  Only show cached (storage backed) pages\n"
    439                     "    -C  Only show non-cached (ram/swap backed) pages\n"
    440                     "    -k  Only show pages collapsed by KSM\n"
    441                     "    -h  Display this help screen.\n",
    442     myname);
    443 }
    444 
    445 static int getprocname(pid_t pid, char *buf, size_t len) {
    446     char filename[20];
    447     FILE *f;
    448 
    449     sprintf(filename, "/proc/%d/cmdline", pid);
    450     f = fopen(filename, "r");
    451     if (!f) { *buf = '\0'; return 1; }
    452     if (!fgets(buf, len, f)) { *buf = '\0'; return 2; }
    453     fclose(f);
    454     return 0;
    455 }
    456 
    457 static int numcmp(long long a, long long b) {
    458     if (a < b) return -1;
    459     if (a > b) return 1;
    460     return 0;
    461 }
    462 
    463 static int licmp(const void *a, const void *b) {
    464     return order * numcmp(
    465         (*((struct library_info**)a))->total_usage.pss,
    466         (*((struct library_info**)b))->total_usage.pss
    467     );
    468 }
    469 
    470 #define create_sort(field, compfn) \
    471     static int sort_by_ ## field (const void *a, const void *b) { \
    472         return order * compfn( \
    473             (*((struct mapping_info**)a))->usage.field, \
    474             (*((struct mapping_info**)b))->usage.field \
    475         ); \
    476     }
    477 
    478 create_sort(vss, numcmp)
    479 create_sort(rss, numcmp)
    480 create_sort(pss, numcmp)
    481 create_sort(uss, numcmp)
    482 create_sort(swap, numcmp)
    483