Home | History | Annotate | Download | only in procrank
      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 <dirent.h>
     18 #include <errno.h>
     19 #include <stdbool.h>
     20 #include <stdlib.h>
     21 #include <sys/types.h>
     22 #include <unistd.h>
     23 #include <string.h>
     24 #include <fcntl.h>
     25 
     26 #include <pagemap/pagemap.h>
     27 
     28 struct proc_info {
     29     pid_t pid;
     30     pm_memusage_t usage;
     31     unsigned long wss;
     32 };
     33 
     34 static void usage(char *myname);
     35 static int getprocname(pid_t pid, char *buf, int len);
     36 static int numcmp(long long a, long long b);
     37 
     38 #define declare_sort(field) \
     39     static int sort_by_ ## field (const void *a, const void *b)
     40 
     41 declare_sort(vss);
     42 declare_sort(rss);
     43 declare_sort(pss);
     44 declare_sort(uss);
     45 declare_sort(swap);
     46 
     47 int (*compfn)(const void *a, const void *b);
     48 static int order;
     49 
     50 void print_mem_info() {
     51     char buffer[1024];
     52     int numFound = 0;
     53 
     54     int fd = open("/proc/meminfo", O_RDONLY);
     55 
     56     if (fd < 0) {
     57         printf("Unable to open /proc/meminfo: %s\n", strerror(errno));
     58         return;
     59     }
     60 
     61     const int len = read(fd, buffer, sizeof(buffer)-1);
     62     close(fd);
     63 
     64     if (len < 0) {
     65         printf("Empty /proc/meminfo");
     66         return;
     67     }
     68     buffer[len] = 0;
     69 
     70     static const char* const tags[] = {
     71             "MemTotal:",
     72             "MemFree:",
     73             "Buffers:",
     74             "Cached:",
     75             "Shmem:",
     76             "Slab:",
     77             NULL
     78     };
     79     static const int tagsLen[] = {
     80             9,
     81             8,
     82             8,
     83             7,
     84             6,
     85             5,
     86             0
     87     };
     88     long mem[] = { 0, 0, 0, 0, 0, 0 };
     89 
     90     char* p = buffer;
     91     while (*p && numFound < 6) {
     92         int i = 0;
     93         while (tags[i]) {
     94             if (strncmp(p, tags[i], tagsLen[i]) == 0) {
     95                 p += tagsLen[i];
     96                 while (*p == ' ') p++;
     97                 char* num = p;
     98                 while (*p >= '0' && *p <= '9') p++;
     99                 if (*p != 0) {
    100                     *p = 0;
    101                     p++;
    102                 }
    103                 mem[i] = atoll(num);
    104                 numFound++;
    105                 break;
    106             }
    107             i++;
    108         }
    109         while (*p && *p != '\n') {
    110             p++;
    111         }
    112         if (*p) p++;
    113     }
    114 
    115     printf("RAM: %ldK total, %ldK free, %ldK buffers, %ldK cached, %ldK shmem, %ldK slab\n",
    116             mem[0], mem[1], mem[2], mem[3], mem[4], mem[5]);
    117 }
    118 
    119 int main(int argc, char *argv[]) {
    120     pm_kernel_t *ker;
    121     pm_process_t *proc;
    122     pid_t *pids;
    123     struct proc_info **procs;
    124     size_t num_procs;
    125     unsigned long total_pss;
    126     unsigned long total_uss;
    127     unsigned long total_swap;
    128     char cmdline[256]; // this must be within the range of int
    129     int error;
    130     bool has_swap = false;
    131     uint64_t required_flags = 0;
    132     uint64_t flags_mask = 0;
    133 
    134     #define WS_OFF   0
    135     #define WS_ONLY  1
    136     #define WS_RESET 2
    137     int ws;
    138 
    139     int arg;
    140     size_t i, j;
    141 
    142     signal(SIGPIPE, SIG_IGN);
    143     compfn = &sort_by_pss;
    144     order = -1;
    145     ws = WS_OFF;
    146 
    147     for (arg = 1; arg < argc; arg++) {
    148         if (!strcmp(argv[arg], "-v")) { compfn = &sort_by_vss; continue; }
    149         if (!strcmp(argv[arg], "-r")) { compfn = &sort_by_rss; continue; }
    150         if (!strcmp(argv[arg], "-p")) { compfn = &sort_by_pss; continue; }
    151         if (!strcmp(argv[arg], "-u")) { compfn = &sort_by_uss; continue; }
    152         if (!strcmp(argv[arg], "-s")) { compfn = &sort_by_swap; continue; }
    153         if (!strcmp(argv[arg], "-c")) { required_flags = 0; flags_mask = PM_PAGE_SWAPBACKED; continue; }
    154         if (!strcmp(argv[arg], "-C")) { required_flags = flags_mask = PM_PAGE_SWAPBACKED; continue; }
    155         if (!strcmp(argv[arg], "-k")) { required_flags = flags_mask = PM_PAGE_KSM; continue; }
    156         if (!strcmp(argv[arg], "-w")) { ws = WS_ONLY; continue; }
    157         if (!strcmp(argv[arg], "-W")) { ws = WS_RESET; continue; }
    158         if (!strcmp(argv[arg], "-R")) { order *= -1; continue; }
    159         if (!strcmp(argv[arg], "-h")) { usage(argv[0]); exit(0); }
    160         fprintf(stderr, "Invalid argument \"%s\".\n", argv[arg]);
    161         usage(argv[0]);
    162         exit(EXIT_FAILURE);
    163     }
    164 
    165     error = pm_kernel_create(&ker);
    166     if (error) {
    167         fprintf(stderr, "Error creating kernel interface -- "
    168                         "does this kernel have pagemap?\n");
    169         exit(EXIT_FAILURE);
    170     }
    171 
    172     error = pm_kernel_pids(ker, &pids, &num_procs);
    173     if (error) {
    174         fprintf(stderr, "Error listing processes.\n");
    175         exit(EXIT_FAILURE);
    176     }
    177 
    178     procs = calloc(num_procs, sizeof(struct proc_info*));
    179     if (procs == NULL) {
    180         fprintf(stderr, "calloc: %s", strerror(errno));
    181         exit(EXIT_FAILURE);
    182     }
    183 
    184     for (i = 0; i < num_procs; i++) {
    185         procs[i] = malloc(sizeof(struct proc_info));
    186         if (procs[i] == NULL) {
    187             fprintf(stderr, "malloc: %s\n", strerror(errno));
    188             exit(EXIT_FAILURE);
    189         }
    190         procs[i]->pid = pids[i];
    191         pm_memusage_zero(&procs[i]->usage);
    192         error = pm_process_create(ker, pids[i], &proc);
    193         if (error) {
    194             fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]);
    195             continue;
    196         }
    197 
    198         switch (ws) {
    199         case WS_OFF:
    200             error = pm_process_usage_flags(proc, &procs[i]->usage, flags_mask,
    201                                            required_flags);
    202             break;
    203         case WS_ONLY:
    204             error = pm_process_workingset(proc, &procs[i]->usage, 0);
    205             break;
    206         case WS_RESET:
    207             error = pm_process_workingset(proc, NULL, 1);
    208             break;
    209         }
    210 
    211         if (error) {
    212             fprintf(stderr, "warning: could not read usage for %d\n", pids[i]);
    213         }
    214 
    215         if (ws != WS_RESET && procs[i]->usage.swap) {
    216             has_swap = true;
    217         }
    218 
    219         pm_process_destroy(proc);
    220     }
    221 
    222     free(pids);
    223 
    224     if (ws == WS_RESET) exit(0);
    225 
    226     j = 0;
    227     for (i = 0; i < num_procs; i++) {
    228         if (procs[i]->usage.vss) {
    229             procs[j++] = procs[i];
    230         } else {
    231             free(procs[i]);
    232         }
    233     }
    234     num_procs = j;
    235 
    236     qsort(procs, num_procs, sizeof(procs[0]), compfn);
    237 
    238     printf("%5s  ", "PID");
    239     if (ws) {
    240         printf("%s  %7s  %7s  ", "WRss", "WPss", "WUss");
    241         if (has_swap) {
    242             printf("%7s  ", "WSwap");
    243         }
    244     } else {
    245         printf("%8s  %7s  %7s  %7s  ", "Vss", "Rss", "Pss", "Uss");
    246         if (has_swap) {
    247             printf("%7s  ", "Swap");
    248         }
    249     }
    250 
    251     printf("%s\n", "cmdline");
    252 
    253     total_pss = 0;
    254     total_uss = 0;
    255     total_swap = 0;
    256 
    257     for (i = 0; i < num_procs; i++) {
    258         if (getprocname(procs[i]->pid, cmdline, (int)sizeof(cmdline)) < 0) {
    259             /*
    260              * Something is probably seriously wrong if writing to the stack
    261              * failed.
    262              */
    263             free(procs[i]);
    264             continue;
    265         }
    266 
    267         total_pss += procs[i]->usage.pss;
    268         total_uss += procs[i]->usage.uss;
    269         total_swap += procs[i]->usage.swap;
    270 
    271         printf("%5d  ", procs[i]->pid);
    272 
    273         if (ws) {
    274             printf("%6dK  %6dK  %6dK  ",
    275                 procs[i]->usage.rss / 1024,
    276                 procs[i]->usage.pss / 1024,
    277                 procs[i]->usage.uss / 1024
    278             );
    279         } else {
    280             printf("%7dK  %6dK  %6dK  %6dK  ",
    281                 procs[i]->usage.vss / 1024,
    282                 procs[i]->usage.rss / 1024,
    283                 procs[i]->usage.pss / 1024,
    284                 procs[i]->usage.uss / 1024
    285             );
    286         }
    287 
    288         if (has_swap) {
    289             printf("%6dK  ", procs[i]->usage.swap / 1024);
    290         }
    291 
    292         printf("%s\n", cmdline);
    293 
    294         free(procs[i]);
    295     }
    296 
    297     free(procs);
    298 
    299     /* Print the separator line */
    300     printf("%5s  ", "");
    301 
    302     if (ws) {
    303         printf("%7s  %7s  %7s  ", "", "------", "------");
    304     } else {
    305         printf("%8s  %7s  %7s  %7s  ", "", "", "------", "------");
    306     }
    307 
    308     if (has_swap) {
    309         printf("%7s  ", "------");
    310     }
    311 
    312     printf("%s\n", "------");
    313 
    314     /* Print the total line */
    315     printf("%5s  ", "");
    316     if (ws) {
    317         printf("%7s  %6ldK  %6ldK  ",
    318             "", total_pss / 1024, total_uss / 1024);
    319     } else {
    320         printf("%8s  %7s  %6ldK  %6ldK  ",
    321             "", "", total_pss / 1024, total_uss / 1024);
    322     }
    323 
    324     if (has_swap) {
    325         printf("%6ldK  ", total_swap);
    326     }
    327 
    328     printf("TOTAL\n");
    329 
    330     printf("\n");
    331     print_mem_info();
    332 
    333     return 0;
    334 }
    335 
    336 static void usage(char *myname) {
    337     fprintf(stderr, "Usage: %s [ -W ] [ -v | -r | -p | -u | -s | -h ]\n"
    338                     "    -v  Sort by VSS.\n"
    339                     "    -r  Sort by RSS.\n"
    340                     "    -p  Sort by PSS.\n"
    341                     "    -u  Sort by USS.\n"
    342                     "    -s  Sort by swap.\n"
    343                     "        (Default sort order is PSS.)\n"
    344                     "    -R  Reverse sort order (default is descending).\n"
    345                     "    -c  Only show cached (storage backed) pages\n"
    346                     "    -C  Only show non-cached (ram/swap backed) pages\n"
    347                     "    -k  Only show pages collapsed by KSM\n"
    348                     "    -w  Display statistics for working set only.\n"
    349                     "    -W  Reset working set of all processes.\n"
    350                     "    -h  Display this help screen.\n",
    351     myname);
    352 }
    353 
    354 /*
    355  * Get the process name for a given PID. Inserts the process name into buffer
    356  * buf of length len. The size of the buffer must be greater than zero to get
    357  * any useful output.
    358  *
    359  * Note that fgets(3) only declares length as an int, so our buffer size is
    360  * also declared as an int.
    361  *
    362  * Returns 0 on success, a positive value on partial success, and -1 on
    363  * failure. Other interesting values:
    364  *   1 on failure to create string to examine proc cmdline entry
    365  *   2 on failure to open proc cmdline entry
    366  *   3 on failure to read proc cmdline entry
    367  */
    368 static int getprocname(pid_t pid, char *buf, int len) {
    369     char *filename;
    370     FILE *f;
    371     int rc = 0;
    372     static const char* unknown_cmdline = "<unknown>";
    373 
    374     if (len <= 0) {
    375         return -1;
    376     }
    377 
    378     if (asprintf(&filename, "/proc/%zd/cmdline", pid) < 0) {
    379         rc = 1;
    380         goto exit;
    381     }
    382 
    383     f = fopen(filename, "r");
    384     if (f == NULL) {
    385         rc = 2;
    386         goto releasefilename;
    387     }
    388 
    389     if (fgets(buf, len, f) == NULL) {
    390         rc = 3;
    391         goto closefile;
    392     }
    393 
    394 closefile:
    395     (void) fclose(f);
    396 releasefilename:
    397     free(filename);
    398 exit:
    399     if (rc != 0) {
    400         /*
    401          * The process went away before we could read its process name. Try
    402          * to give the user "<unknown>" here, but otherwise they get to look
    403          * at a blank.
    404          */
    405         if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) {
    406             rc = 4;
    407         }
    408     }
    409 
    410     return rc;
    411 }
    412 
    413 static int numcmp(long long a, long long b) {
    414     if (a < b) return -1;
    415     if (a > b) return 1;
    416     return 0;
    417 }
    418 
    419 #define create_sort(field, compfn) \
    420     static int sort_by_ ## field (const void *a, const void *b) { \
    421         return order * compfn( \
    422             (*((struct proc_info**)a))->usage.field, \
    423             (*((struct proc_info**)b))->usage.field \
    424         ); \
    425     }
    426 
    427 create_sort(vss, numcmp)
    428 create_sort(rss, numcmp)
    429 create_sort(pss, numcmp)
    430 create_sort(uss, numcmp)
    431 create_sort(swap, numcmp)
    432