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 <stdlib.h>
     21 #include <string.h>
     22 #include <sys/types.h>
     23 #include <unistd.h>
     24 
     25 #include <pagemap/pagemap.h>
     26 
     27 #define MAX_CMDLINE 256
     28 
     29 struct process_info {
     30     pid_t pid;
     31     char cmdline[MAX_CMDLINE];
     32 };
     33 
     34 struct mapping_info {
     35     struct process_info *proc;
     36     pm_memusage_t usage;
     37 };
     38 
     39 struct library_info {
     40     struct library_info *next;
     41     char *name;
     42     struct mapping_info **mappings;
     43     int mappings_count;
     44     int mappings_size;
     45     pm_memusage_t total_usage;
     46 };
     47 
     48 static void usage(char *myname);
     49 static int getprocname(pid_t pid, char *buf, size_t len);
     50 static int numcmp(long long a, long long b);
     51 static int licmp(const void *a, const void *b);
     52 
     53 char *library_name_blacklist[] = { "[heap]", "[stack]", "", NULL };
     54 
     55 #define declare_sort(field) \
     56     static int sort_by_ ## field (const void *a, const void *b)
     57 
     58 declare_sort(vss);
     59 declare_sort(rss);
     60 declare_sort(pss);
     61 declare_sort(uss);
     62 
     63 #define INIT_LIBRARIES 16
     64 #define INIT_MAPPINGS 4
     65 
     66 static int order;
     67 
     68 struct library_info **libraries;
     69 int libraries_count;
     70 int libraries_size;
     71 
     72 struct library_info *get_library(char *name) {
     73     int i;
     74     struct library_info *library;
     75 
     76     for (i = 0; library_name_blacklist[i]; i++)
     77         if (!strcmp(name, library_name_blacklist[i]))
     78             return NULL;
     79 
     80     for (i = 0; i < libraries_count; i++) {
     81         if (!strcmp(libraries[i]->name, name))
     82             return libraries[i];
     83     }
     84 
     85     if (libraries_count >= libraries_size) {
     86         libraries = realloc(libraries, 2 * libraries_size * sizeof(struct library_info *));
     87         if (!libraries) {
     88             fprintf(stderr, "Couldn't resize libraries array: %s\n", strerror(errno));
     89             exit(EXIT_FAILURE);
     90         }
     91         libraries_size = 2 * libraries_size;
     92     }
     93 
     94     library = calloc(1, sizeof(*library));
     95     if (!library) {
     96         fprintf(stderr, "Couldn't allocate space for library struct: %s\n", strerror(errno));
     97         exit(EXIT_FAILURE);
     98     }
     99     library->name = malloc(strlen(name) + 1);
    100     if (!library->name) {
    101         fprintf(stderr, "Couldn't allocate space for library name: %s\n", strerror(errno));
    102         exit(EXIT_FAILURE);
    103     }
    104     strcpy(library->name, name);
    105     library->mappings = malloc(INIT_MAPPINGS * sizeof(struct mapping_info *));
    106     if (!library->mappings) {
    107         fprintf(stderr, "Couldn't allocate space for library mappings array: %s\n", strerror(errno));
    108         exit(EXIT_FAILURE);
    109     }
    110     library->mappings_count = 0; library->mappings_size = INIT_MAPPINGS;
    111     pm_memusage_zero(&library->total_usage);
    112 
    113     libraries[libraries_count++] = library;
    114 
    115     return library;
    116 }
    117 
    118 struct mapping_info *get_mapping(struct library_info *library, struct process_info *proc) {
    119     struct mapping_info *mapping;
    120     int i;
    121 
    122     for (i = 0; i < library->mappings_count; i++) {
    123         if (library->mappings[i]->proc == proc)
    124             return library->mappings[i];
    125     }
    126 
    127     if (library->mappings_count >= library->mappings_size) {
    128         library->mappings = realloc(library->mappings,
    129             2 * library->mappings_size * sizeof(struct mapping*));
    130         if (!library->mappings) {
    131             fprintf(stderr, "Couldn't resize mappings array: %s\n", strerror(errno));
    132             exit(EXIT_FAILURE);
    133         }
    134         library->mappings_size = 2 * library->mappings_size;
    135     }
    136 
    137     mapping = calloc(1, sizeof(*mapping));
    138     if (!mapping) {
    139         fprintf(stderr, "Couldn't allocate space for mapping struct: %s\n", strerror(errno));
    140         exit(EXIT_FAILURE);
    141     }
    142     mapping->proc = proc;
    143     pm_memusage_zero(&mapping->usage);
    144 
    145     library->mappings[library->mappings_count++] = mapping;
    146 
    147     return mapping;
    148 }
    149 
    150 struct process_info *get_process(pid_t pid) {
    151     struct process_info *process;
    152 
    153     process = calloc(1, sizeof(*process));
    154     if (!process) {
    155         fprintf(stderr, "Couldn't allocate space for process struct: %s\n", strerror(errno));
    156         exit(EXIT_FAILURE);
    157     }
    158 
    159     process->pid = pid;
    160     getprocname(pid, process->cmdline, sizeof(process->cmdline));
    161 
    162     return process;
    163 }
    164 
    165 int main(int argc, char *argv[]) {
    166     char cmdline[256];
    167     char *prefix;
    168     size_t prefix_len;
    169     int (*compfn)(const void *a, const void *b);
    170 
    171     pm_kernel_t *ker;
    172     pm_process_t *proc;
    173 
    174     pid_t *pids;
    175     size_t num_procs;
    176 
    177     pm_map_t **maps;
    178     size_t num_maps;
    179     pm_memusage_t map_usage;
    180 
    181     struct library_info *li, **lis;
    182     struct mapping_info *mi, **mis;
    183     struct process_info *pi;
    184 
    185     int i, j, error;
    186 
    187     signal(SIGPIPE, SIG_IGN);
    188     compfn = &sort_by_pss;
    189     order = -1;
    190     prefix = NULL;
    191     prefix_len = 0;
    192 
    193     for (i = 1; i < argc; i++) {
    194         if (!strcmp(argv[i], "-P")) {
    195             if (i + 1 >= argc) {
    196                 fprintf(stderr, "Option -P requires an argument.\n");
    197                 usage(argv[0]);
    198                 exit(EXIT_FAILURE);
    199             }
    200             prefix = argv[++i];
    201             prefix_len = strlen(prefix);
    202             continue;
    203         }
    204         if (!strcmp(argv[i], "-v")) { compfn = &sort_by_vss; continue; }
    205         if (!strcmp(argv[i], "-r")) { compfn = &sort_by_rss; continue; }
    206         if (!strcmp(argv[i], "-p")) { compfn = &sort_by_pss; continue; }
    207         if (!strcmp(argv[i], "-u")) { compfn = &sort_by_uss; continue; }
    208         if (!strcmp(argv[i], "-R")) { order *= -1; continue; }
    209         if (!strcmp(argv[i], "-h")) { usage(argv[0]); exit(0); }
    210         fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]);
    211         usage(argv[0]);
    212         exit(EXIT_FAILURE);
    213     }
    214 
    215     libraries = malloc(INIT_LIBRARIES * sizeof(struct library_info *));
    216     libraries_count = 0; libraries_size = INIT_LIBRARIES;
    217 
    218     error = pm_kernel_create(&ker);
    219     if (error) {
    220         fprintf(stderr, "Error initializing kernel interface -- "
    221                         "does this kernel have pagemap?\n");
    222         exit(EXIT_FAILURE);
    223     }
    224 
    225     error = pm_kernel_pids(ker, &pids, &num_procs);
    226     if (error) {
    227         fprintf(stderr, "Error listing processes.\n");
    228         exit(EXIT_FAILURE);
    229     }
    230 
    231     for (i = 0; i < num_procs; i++) {
    232         error = pm_process_create(ker, pids[i], &proc);
    233         if (error) {
    234             fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]);
    235             continue;
    236         }
    237 
    238         pi = get_process(pids[i]);
    239 
    240         error = pm_process_maps(proc, &maps, &num_maps);
    241         if (error) {
    242             fprintf(stderr, "Error listing maps for process %d.\n", proc->pid);
    243             exit(EXIT_FAILURE);
    244         }
    245 
    246         for (j = 0; j < num_maps; j++) {
    247             if (prefix && (strncmp(pm_map_name(maps[j]), prefix, prefix_len)))
    248                 continue;
    249 
    250             li = get_library(pm_map_name(maps[j]));
    251             if (!li)
    252                 continue;
    253 
    254             mi = get_mapping(li, pi);
    255 
    256             error = pm_map_usage(maps[j], &map_usage);
    257             if (error) {
    258                 fprintf(stderr, "Error getting map memory usage of "
    259                                 "map %s in process %d.\n",
    260                         pm_map_name(maps[j]), proc->pid);
    261                 exit(EXIT_FAILURE);
    262             }
    263             pm_memusage_add(&mi->usage, &map_usage);
    264             pm_memusage_add(&li->total_usage, &map_usage);
    265         }
    266     }
    267 
    268     printf(          " %6s   %6s   %6s   %6s   %6s  %s\n", "RSStot", "VSS", "RSS", "PSS", "USS", "Name/PID");
    269     fflush(stdout);
    270 
    271     qsort(libraries, libraries_count, sizeof(libraries[0]), &licmp);
    272 
    273     for (i = 0; i < libraries_count; i++) {
    274         li = libraries[i];
    275 
    276         printf("%6dK   %6s   %6s   %6s   %6s  %s\n", li->total_usage.pss / 1024, "", "", "", "", li->name);
    277         fflush(stdout);
    278 
    279         qsort(li->mappings, li->mappings_count, sizeof(li->mappings[0]), compfn);
    280 
    281         for (j = 0; j < li->mappings_count; j++) {
    282             mi = li->mappings[j];
    283             pi = mi->proc;
    284             printf(   " %6s  %6dK  %6dK  %6dK  %6dK    %s [%d]\n", "",
    285                 mi->usage.vss / 1024,
    286                 mi->usage.rss / 1024,
    287                 mi->usage.pss / 1024,
    288                 mi->usage.uss / 1024,
    289                 pi->cmdline,
    290                 pi->pid);
    291         }
    292         printf("\n");
    293         fflush(stdout);
    294     }
    295 
    296     return 0;
    297 }
    298 
    299 static void usage(char *myname) {
    300     fprintf(stderr, "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -h ]\n"
    301                     "\n"
    302                     "Sort options:\n"
    303                     "    -v  Sort processes by VSS.\n"
    304                     "    -r  Sort processes by RSS.\n"
    305                     "    -p  Sort processes by PSS.\n"
    306                     "    -u  Sort processes by USS.\n"
    307                     "        (Default sort order is PSS.)\n"
    308                     "    -P /path  Limit libraries displayed to those in path.\n"
    309                     "    -R  Reverse sort order (default is descending).\n"
    310                     "    -h  Display this help screen.\n",
    311     myname);
    312 }
    313 
    314 static int getprocname(pid_t pid, char *buf, size_t len) {
    315     char filename[20];
    316     FILE *f;
    317 
    318     sprintf(filename, "/proc/%d/cmdline", pid);
    319     f = fopen(filename, "r");
    320     if (!f) { *buf = '\0'; return 1; }
    321     if (!fgets(buf, len, f)) { *buf = '\0'; return 2; }
    322     fclose(f);
    323     return 0;
    324 }
    325 
    326 static int numcmp(long long a, long long b) {
    327     if (a < b) return -1;
    328     if (a > b) return 1;
    329     return 0;
    330 }
    331 
    332 static int licmp(const void *a, const void *b) {
    333     return order * numcmp(
    334         (*((struct library_info**)a))->total_usage.pss,
    335         (*((struct library_info**)b))->total_usage.pss
    336     );
    337 }
    338 
    339 #define create_sort(field, compfn) \
    340     static int sort_by_ ## field (const void *a, const void *b) { \
    341         return order * compfn( \
    342             (*((struct mapping_info**)a))->usage.field, \
    343             (*((struct mapping_info**)b))->usage.field \
    344         ); \
    345     }
    346 
    347 create_sort(vss, numcmp)
    348 create_sort(rss, numcmp)
    349 create_sort(pss, numcmp)
    350 create_sort(uss, numcmp)
    351