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     compfn = &sort_by_pss;
    188     order = -1;
    189     prefix = NULL;
    190     prefix_len = 0;
    191 
    192     for (i = 1; i < argc; i++) {
    193         if (!strcmp(argv[i], "-P")) {
    194             if (i + 1 >= argc) {
    195                 fprintf(stderr, "Option -P requires an argument.\n");
    196                 usage(argv[0]);
    197                 exit(EXIT_FAILURE);
    198             }
    199             prefix = argv[++i];
    200             prefix_len = strlen(prefix);
    201             continue;
    202         }
    203         if (!strcmp(argv[i], "-v")) { compfn = &sort_by_vss; continue; }
    204         if (!strcmp(argv[i], "-r")) { compfn = &sort_by_rss; continue; }
    205         if (!strcmp(argv[i], "-p")) { compfn = &sort_by_pss; continue; }
    206         if (!strcmp(argv[i], "-u")) { compfn = &sort_by_uss; continue; }
    207         if (!strcmp(argv[i], "-R")) { order *= -1; continue; }
    208         if (!strcmp(argv[i], "-h")) { usage(argv[0]); exit(0); }
    209         fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]);
    210         usage(argv[0]);
    211         exit(EXIT_FAILURE);
    212     }
    213 
    214     libraries = malloc(INIT_LIBRARIES * sizeof(struct library_info *));
    215     libraries_count = 0; libraries_size = INIT_LIBRARIES;
    216 
    217     error = pm_kernel_create(&ker);
    218     if (error) {
    219         fprintf(stderr, "Error initializing kernel interface -- "
    220                         "does this kernel have pagemap?\n");
    221         exit(EXIT_FAILURE);
    222     }
    223 
    224     error = pm_kernel_pids(ker, &pids, &num_procs);
    225     if (error) {
    226         fprintf(stderr, "Error listing processes.\n");
    227         exit(EXIT_FAILURE);
    228     }
    229 
    230     for (i = 0; i < num_procs; i++) {
    231         error = pm_process_create(ker, pids[i], &proc);
    232         if (error) {
    233             fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]);
    234             continue;
    235         }
    236 
    237         pi = get_process(pids[i]);
    238 
    239         error = pm_process_maps(proc, &maps, &num_maps);
    240         if (error) {
    241             fprintf(stderr, "Error listing maps for process %d.\n", proc->pid);
    242             exit(EXIT_FAILURE);
    243         }
    244 
    245         for (j = 0; j < num_maps; j++) {
    246             if (prefix && (strncmp(pm_map_name(maps[j]), prefix, prefix_len)))
    247                 continue;
    248 
    249             li = get_library(pm_map_name(maps[j]));
    250             if (!li)
    251                 continue;
    252 
    253             mi = get_mapping(li, pi);
    254 
    255             error = pm_map_usage(maps[j], &map_usage);
    256             if (error) {
    257                 fprintf(stderr, "Error getting map memory usage of "
    258                                 "map %s in process %d.\n",
    259                         pm_map_name(maps[j]), proc->pid);
    260                 exit(EXIT_FAILURE);
    261             }
    262             pm_memusage_add(&mi->usage, &map_usage);
    263             pm_memusage_add(&li->total_usage, &map_usage);
    264         }
    265     }
    266 
    267     printf(          " %6s   %6s   %6s   %6s   %6s  %s\n", "RSStot", "VSS", "RSS", "PSS", "USS", "Name/PID");
    268     fflush(stdout);
    269 
    270     qsort(libraries, libraries_count, sizeof(libraries[0]), &licmp);
    271 
    272     for (i = 0; i < libraries_count; i++) {
    273         li = libraries[i];
    274 
    275         printf("%6dK   %6s   %6s   %6s   %6s  %s\n", li->total_usage.pss / 1024, "", "", "", "", li->name);
    276         fflush(stdout);
    277 
    278         qsort(li->mappings, li->mappings_count, sizeof(li->mappings[0]), compfn);
    279 
    280         for (j = 0; j < li->mappings_count; j++) {
    281             mi = li->mappings[j];
    282             pi = mi->proc;
    283             printf(   " %6s  %6dK  %6dK  %6dK  %6dK    %s [%d]\n", "",
    284                 mi->usage.vss / 1024,
    285                 mi->usage.rss / 1024,
    286                 mi->usage.pss / 1024,
    287                 mi->usage.uss / 1024,
    288                 pi->cmdline,
    289                 pi->pid);
    290         }
    291         printf("\n");
    292         fflush(stdout);
    293     }
    294 
    295     return 0;
    296 }
    297 
    298 static void usage(char *myname) {
    299     fprintf(stderr, "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -h ]\n"
    300                     "\n"
    301                     "Sort options:\n"
    302                     "    -v  Sort processes by VSS.\n"
    303                     "    -r  Sort processes by RSS.\n"
    304                     "    -p  Sort processes by PSS.\n"
    305                     "    -u  Sort processes by USS.\n"
    306                     "        (Default sort order is PSS.)\n"
    307                     "    -P /path  Limit libraries displayed to those in path.\n"
    308                     "    -R  Reverse sort order (default is descending).\n"
    309                     "    -h  Display this help screen.\n",
    310     myname);
    311 }
    312 
    313 static int getprocname(pid_t pid, char *buf, size_t len) {
    314     char filename[20];
    315     FILE *f;
    316 
    317     sprintf(filename, "/proc/%d/cmdline", pid);
    318     f = fopen(filename, "r");
    319     if (!f) { *buf = '\0'; return 1; }
    320     if (!fgets(buf, len, f)) { *buf = '\0'; return 2; }
    321     fclose(f);
    322     return 0;
    323 }
    324 
    325 static int numcmp(long long a, long long b) {
    326     if (a < b) return -1;
    327     if (a > b) return 1;
    328     return 0;
    329 }
    330 
    331 static int licmp(const void *a, const void *b) {
    332     return order * numcmp(
    333         (*((struct library_info**)a))->total_usage.pss,
    334         (*((struct library_info**)b))->total_usage.pss
    335     );
    336 }
    337 
    338 #define create_sort(field, compfn) \
    339     static int sort_by_ ## field (const void *a, const void *b) { \
    340         return order * compfn( \
    341             (*((struct mapping_info**)a))->usage.field, \
    342             (*((struct mapping_info**)b))->usage.field \
    343         ); \
    344     }
    345 
    346 create_sort(vss, numcmp)
    347 create_sort(rss, numcmp)
    348 create_sort(pss, numcmp)
    349 create_sort(uss, numcmp)
    350