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     int mappings_count;
     46     int 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 int libraries_count;
     73 int libraries_size;
     74 
     75 struct library_info *get_library(const char *name, bool all) {
     76     int 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     int 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     int i, j, error;
    219     int perm;
    220     bool all;
    221     uint64_t required_flags;
    222     uint64_t flags_mask;
    223 
    224     bool has_swap = false;
    225 
    226     signal(SIGPIPE, SIG_IGN);
    227     compfn = &sort_by_pss;
    228     order = -1;
    229     prefix = NULL;
    230     prefix_len = 0;
    231     opterr = 0;
    232     perm = 0;
    233     all = false;
    234     required_flags = 0;
    235     flags_mask = 0;
    236 
    237     while (1) {
    238         int c;
    239         const struct option longopts[] = {
    240             {"all", 0, 0, 'a'},
    241             {"cached", 0, 0, 'c'},
    242             {"nocached", 0, 0, 'C'},
    243             {"ksm", 0, 0, 'k'},
    244             {"help", 0, 0, 'h'},
    245             {"pss", 0, 0, 'p'},
    246             {"uss", 0, 0, 'u'},
    247             {"vss", 0, 0, 'v'},
    248             {"rss", 0, 0, 'r'},
    249             {"swap", 0, 0, 's'},
    250             {"reverse", 0, 0, 'R'},
    251             {"path", required_argument, 0, 'P'},
    252             {"perm", required_argument, 0, 'm'},
    253             {0, 0, 0, 0}
    254         };
    255         c = getopt_long(argc, argv, "acChkm:pP:uvrsR", longopts, NULL);
    256         if (c < 0) {
    257             break;
    258         }
    259         /* Alphabetical cases */
    260         switch (c) {
    261         case 'a':
    262             all = true;
    263             break;
    264         case 'c':
    265             required_flags = 0;
    266             flags_mask = PM_PAGE_SWAPBACKED;
    267             break;
    268         case 'C':
    269             required_flags = PM_PAGE_SWAPBACKED;
    270             flags_mask = PM_PAGE_SWAPBACKED;
    271             break;
    272         case 'k':
    273             required_flags = PM_PAGE_KSM;
    274             flags_mask = PM_PAGE_KSM;
    275             break;
    276         case 'h':
    277             usage(argv[0]);
    278             exit(EXIT_SUCCESS);
    279         case 'm':
    280             perm = parse_perm(optarg);
    281             break;
    282         case 'p':
    283             compfn = &sort_by_pss;
    284             break;
    285         case 'P':
    286             prefix = optarg;
    287             prefix_len = strlen(prefix);
    288             break;
    289         case 'u':
    290             compfn = &sort_by_uss;
    291             break;
    292         case 'v':
    293             compfn = &sort_by_vss;
    294             break;
    295         case 'r':
    296             compfn = &sort_by_rss;
    297             break;
    298         case 's':
    299             compfn = &sort_by_swap;
    300             break;
    301         case 'R':
    302             order *= -1;
    303             break;
    304         case '?':
    305             fprintf(stderr, "Invalid argument \"%s\".\n", argv[optind - 1]);
    306             usage(argv[0]);
    307             exit(EXIT_FAILURE);
    308         default:
    309             abort();
    310         }
    311     }
    312 
    313     argc -= optind;
    314     argv += optind;
    315 
    316     libraries = malloc(INIT_LIBRARIES * sizeof(struct library_info *));
    317     libraries_count = 0; libraries_size = INIT_LIBRARIES;
    318 
    319     error = pm_kernel_create(&ker);
    320     if (error) {
    321         fprintf(stderr, "Error initializing kernel interface -- "
    322                         "does this kernel have pagemap?\n");
    323         exit(EXIT_FAILURE);
    324     }
    325 
    326     error = pm_kernel_pids(ker, &pids, &num_procs);
    327     if (error) {
    328         fprintf(stderr, "Error listing processes.\n");
    329         exit(EXIT_FAILURE);
    330     }
    331 
    332     for (i = 0; i < num_procs; i++) {
    333         error = pm_process_create(ker, pids[i], &proc);
    334         if (error) {
    335             fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]);
    336             continue;
    337         }
    338 
    339         pi = get_process(pids[i]);
    340 
    341         error = pm_process_maps(proc, &maps, &num_maps);
    342         if (error) {
    343             fprintf(stderr, "Error listing maps for process %d.\n", proc->pid);
    344             exit(EXIT_FAILURE);
    345         }
    346 
    347         for (j = 0; j < num_maps; j++) {
    348             if (prefix && (strncmp(pm_map_name(maps[j]), prefix, prefix_len)))
    349                 continue;
    350 
    351             if (perm && (pm_map_flags(maps[j]) & PM_MAP_PERMISSIONS) != perm)
    352                 continue;
    353 
    354             li = get_library(pm_map_name(maps[j]), all);
    355             if (!li)
    356                 continue;
    357 
    358             mi = get_mapping(li, pi);
    359 
    360             error = pm_map_usage_flags(maps[j], &map_usage, flags_mask,
    361                                        required_flags);
    362             if (error) {
    363                 fprintf(stderr, "Error getting map memory usage of "
    364                                 "map %s in process %d.\n",
    365                         pm_map_name(maps[j]), proc->pid);
    366                 exit(EXIT_FAILURE);
    367             }
    368 
    369             if (map_usage.swap) {
    370                 has_swap = true;
    371             }
    372 
    373             pm_memusage_add(&mi->usage, &map_usage);
    374             pm_memusage_add(&li->total_usage, &map_usage);
    375         }
    376     }
    377 
    378     printf(" %6s   %6s   %6s   %6s   %6s  ", "RSStot", "VSS", "RSS", "PSS", "USS");
    379 
    380     if (has_swap) {
    381         printf(" %6s  ", "Swap");
    382     }
    383 
    384     printf("Name/PID\n");
    385     fflush(stdout);
    386 
    387     qsort(libraries, libraries_count, sizeof(libraries[0]), &licmp);
    388 
    389     for (i = 0; i < libraries_count; i++) {
    390         li = libraries[i];
    391 
    392         printf("%6dK   %6s   %6s   %6s   %6s  ", li->total_usage.pss / 1024, "", "", "", "");
    393         if (has_swap) {
    394             printf(" %6s  ", "");
    395         }
    396         printf("%s\n", li->name);
    397         fflush(stdout);
    398 
    399         qsort(li->mappings, li->mappings_count, sizeof(li->mappings[0]), compfn);
    400 
    401         for (j = 0; j < li->mappings_count; j++) {
    402             mi = li->mappings[j];
    403             pi = mi->proc;
    404             printf(   " %6s  %6dK  %6dK  %6dK  %6dK  ", "",
    405                 mi->usage.vss / 1024,
    406                 mi->usage.rss / 1024,
    407                 mi->usage.pss / 1024,
    408                 mi->usage.uss / 1024);
    409             if (has_swap) {
    410                 printf("%6dK  ", mi->usage.swap / 1024);
    411             }
    412             printf("  %s [%d]\n",
    413                 pi->cmdline,
    414                 pi->pid);
    415         }
    416         printf("\n");
    417         fflush(stdout);
    418     }
    419 
    420     return 0;
    421 }
    422 
    423 static void usage(char *myname) {
    424     fprintf(stderr, "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -s | -h ]\n"
    425                     "\n"
    426                     "Sort options:\n"
    427                     "    -v  Sort processes by VSS.\n"
    428                     "    -r  Sort processes by RSS.\n"
    429                     "    -p  Sort processes by PSS.\n"
    430                     "    -u  Sort processes by USS.\n"
    431                     "    -s  Sort processes by swap.\n"
    432                     "        (Default sort order is PSS.)\n"
    433                     "    -a  Show all mappings, including stack, heap and anon.\n"
    434                     "    -P /path  Limit libraries displayed to those in path.\n"
    435                     "    -R  Reverse sort order (default is descending).\n"
    436                     "    -m [r][w][x] Only list pages that exactly match permissions\n"
    437                     "    -c  Only show cached (storage backed) pages\n"
    438                     "    -C  Only show non-cached (ram/swap backed) pages\n"
    439                     "    -k  Only show pages collapsed by KSM\n"
    440                     "    -h  Display this help screen.\n",
    441     myname);
    442 }
    443 
    444 static int getprocname(pid_t pid, char *buf, size_t len) {
    445     char filename[20];
    446     FILE *f;
    447 
    448     sprintf(filename, "/proc/%d/cmdline", pid);
    449     f = fopen(filename, "r");
    450     if (!f) { *buf = '\0'; return 1; }
    451     if (!fgets(buf, len, f)) { *buf = '\0'; return 2; }
    452     fclose(f);
    453     return 0;
    454 }
    455 
    456 static int numcmp(long long a, long long b) {
    457     if (a < b) return -1;
    458     if (a > b) return 1;
    459     return 0;
    460 }
    461 
    462 static int licmp(const void *a, const void *b) {
    463     return order * numcmp(
    464         (*((struct library_info**)a))->total_usage.pss,
    465         (*((struct library_info**)b))->total_usage.pss
    466     );
    467 }
    468 
    469 #define create_sort(field, compfn) \
    470     static int sort_by_ ## field (const void *a, const void *b) { \
    471         return order * compfn( \
    472             (*((struct mapping_info**)a))->usage.field, \
    473             (*((struct mapping_info**)b))->usage.field \
    474         ); \
    475     }
    476 
    477 create_sort(vss, numcmp)
    478 create_sort(rss, numcmp)
    479 create_sort(pss, numcmp)
    480 create_sort(uss, numcmp)
    481 create_sort(swap, numcmp)
    482