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 <stdlib.h>
     20 #include <sys/types.h>
     21 #include <unistd.h>
     22 
     23 #include <pagemap/pagemap.h>
     24 
     25 struct proc_info {
     26     pid_t pid;
     27     pm_memusage_t usage;
     28     unsigned long wss;
     29 };
     30 
     31 static void usage(char *myname);
     32 static int getprocname(pid_t pid, char *buf, int len);
     33 static int numcmp(long long a, long long b);
     34 
     35 #define declare_sort(field) \
     36     static int sort_by_ ## field (const void *a, const void *b)
     37 
     38 declare_sort(vss);
     39 declare_sort(rss);
     40 declare_sort(pss);
     41 declare_sort(uss);
     42 
     43 int (*compfn)(const void *a, const void *b);
     44 static int order;
     45 
     46 int main(int argc, char *argv[]) {
     47     pm_kernel_t *ker;
     48     pm_process_t *proc;
     49     pid_t *pids;
     50     struct proc_info **procs;
     51     size_t num_procs;
     52     char cmdline[256]; // this must be within the range of int
     53     int error;
     54 
     55     #define WS_OFF   0
     56     #define WS_ONLY  1
     57     #define WS_RESET 2
     58     int ws;
     59 
     60     int arg;
     61     size_t i, j;
     62 
     63     compfn = &sort_by_pss;
     64     order = -1;
     65     ws = WS_OFF;
     66 
     67     for (arg = 1; arg < argc; arg++) {
     68         if (!strcmp(argv[arg], "-v")) { compfn = &sort_by_vss; continue; }
     69         if (!strcmp(argv[arg], "-r")) { compfn = &sort_by_rss; continue; }
     70         if (!strcmp(argv[arg], "-p")) { compfn = &sort_by_pss; continue; }
     71         if (!strcmp(argv[arg], "-u")) { compfn = &sort_by_uss; continue; }
     72         if (!strcmp(argv[arg], "-w")) { ws = WS_ONLY; continue; }
     73         if (!strcmp(argv[arg], "-W")) { ws = WS_RESET; continue; }
     74         if (!strcmp(argv[arg], "-R")) { order *= -1; continue; }
     75         if (!strcmp(argv[arg], "-h")) { usage(argv[0]); exit(0); }
     76         fprintf(stderr, "Invalid argument \"%s\".\n", argv[arg]);
     77         usage(argv[0]);
     78         exit(EXIT_FAILURE);
     79     }
     80 
     81     error = pm_kernel_create(&ker);
     82     if (error) {
     83         fprintf(stderr, "Error creating kernel interface -- "
     84                         "does this kernel have pagemap?\n");
     85         exit(EXIT_FAILURE);
     86     }
     87 
     88     error = pm_kernel_pids(ker, &pids, &num_procs);
     89     if (error) {
     90         fprintf(stderr, "Error listing processes.\n");
     91         exit(EXIT_FAILURE);
     92     }
     93 
     94     procs = calloc(num_procs, sizeof(struct proc_info*));
     95     if (procs == NULL) {
     96         fprintf(stderr, "calloc: %s", strerror(errno));
     97         exit(EXIT_FAILURE);
     98     }
     99 
    100     for (i = 0; i < num_procs; i++) {
    101         procs[i] = malloc(sizeof(struct proc_info));
    102         if (procs[i] == NULL) {
    103             fprintf(stderr, "malloc: %s\n", strerror(errno));
    104             exit(EXIT_FAILURE);
    105         }
    106         procs[i]->pid = pids[i];
    107         error = pm_process_create(ker, pids[i], &proc);
    108         if (!error) {
    109             switch (ws) {
    110             case WS_OFF:
    111                 pm_process_usage(proc, &procs[i]->usage);
    112                 break;
    113             case WS_ONLY:
    114                 pm_process_workingset(proc, &procs[i]->usage, 0);
    115                 break;
    116             case WS_RESET:
    117                 pm_process_workingset(proc, NULL, 1);
    118                 break;
    119             }
    120             pm_process_destroy(proc);
    121         } else {
    122             fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]);
    123             pm_memusage_zero(&procs[i]->usage);
    124         }
    125     }
    126 
    127     free(pids);
    128 
    129     if (ws == WS_RESET) exit(0);
    130 
    131     j = 0;
    132     for (i = 0; i < num_procs; i++) {
    133         if (procs[i]->usage.vss) {
    134             procs[j++] = procs[i];
    135         } else {
    136             free(procs[i]);
    137         }
    138     }
    139     num_procs = j;
    140 
    141     qsort(procs, num_procs, sizeof(procs[0]), compfn);
    142 
    143     if (ws)
    144         printf("%5s  %7s  %7s  %7s  %s\n", "PID", "WRss", "WPss", "WUss", "cmdline");
    145     else
    146         printf("%5s  %7s  %7s  %7s  %7s  %s\n", "PID", "Vss", "Rss", "Pss", "Uss", "cmdline");
    147 
    148     for (i = 0; i < num_procs; i++) {
    149         if (getprocname(procs[i]->pid, cmdline, (int)sizeof(cmdline)) < 0) {
    150             /*
    151              * Something is probably seriously wrong if writing to the stack
    152              * failed.
    153              */
    154             free(procs[i]);
    155             continue;
    156         }
    157 
    158         if (ws)
    159             printf("%5d  %6dK  %6dK  %6dK  %s\n",
    160                 procs[i]->pid,
    161                 procs[i]->usage.rss / 1024,
    162                 procs[i]->usage.pss / 1024,
    163                 procs[i]->usage.uss / 1024,
    164                 cmdline
    165             );
    166         else
    167             printf("%5d  %6dK  %6dK  %6dK  %6dK  %s\n",
    168                 procs[i]->pid,
    169                 procs[i]->usage.vss / 1024,
    170                 procs[i]->usage.rss / 1024,
    171                 procs[i]->usage.pss / 1024,
    172                 procs[i]->usage.uss / 1024,
    173                 cmdline
    174             );
    175 
    176         free(procs[i]);
    177     }
    178 
    179     free(procs);
    180     return 0;
    181 }
    182 
    183 static void usage(char *myname) {
    184     fprintf(stderr, "Usage: %s [ -W ] [ -v | -r | -p | -u | -h ]\n"
    185                     "    -v  Sort by VSS.\n"
    186                     "    -r  Sort by RSS.\n"
    187                     "    -p  Sort by PSS.\n"
    188                     "    -u  Sort by USS.\n"
    189                     "        (Default sort order is PSS.)\n"
    190                     "    -R  Reverse sort order (default is descending).\n"
    191                     "    -w  Display statistics for working set only.\n"
    192                     "    -W  Reset working set of all processes.\n"
    193                     "    -h  Display this help screen.\n",
    194     myname);
    195 }
    196 
    197 /*
    198  * Get the process name for a given PID. Inserts the process name into buffer
    199  * buf of length len. The size of the buffer must be greater than zero to get
    200  * any useful output.
    201  *
    202  * Note that fgets(3) only declares length as an int, so our buffer size is
    203  * also declared as an int.
    204  *
    205  * Returns 0 on success, a positive value on partial success, and -1 on
    206  * failure. Other interesting values:
    207  *   1 on failure to create string to examine proc cmdline entry
    208  *   2 on failure to open proc cmdline entry
    209  *   3 on failure to read proc cmdline entry
    210  */
    211 static int getprocname(pid_t pid, char *buf, int len) {
    212     char *filename;
    213     FILE *f;
    214     int rc = 0;
    215     static const char* unknown_cmdline = "<unknown>";
    216 
    217     if (len <= 0) {
    218         return -1;
    219     }
    220 
    221     if (asprintf(&filename, "/proc/%zd/cmdline", pid) < 0) {
    222         rc = 1;
    223         goto exit;
    224     }
    225 
    226     f = fopen(filename, "r");
    227     if (f == NULL) {
    228         rc = 2;
    229         goto releasefilename;
    230     }
    231 
    232     if (fgets(buf, len, f) == NULL) {
    233         rc = 3;
    234         goto closefile;
    235     }
    236 
    237 closefile:
    238     (void) fclose(f);
    239 releasefilename:
    240     free(filename);
    241 exit:
    242     if (rc != 0) {
    243         /*
    244          * The process went away before we could read its process name. Try
    245          * to give the user "<unknown>" here, but otherwise they get to look
    246          * at a blank.
    247          */
    248         if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) {
    249             rc = 4;
    250         }
    251     }
    252 
    253     return rc;
    254 }
    255 
    256 static int numcmp(long long a, long long b) {
    257     if (a < b) return -1;
    258     if (a > b) return 1;
    259     return 0;
    260 }
    261 
    262 #define create_sort(field, compfn) \
    263     static int sort_by_ ## field (const void *a, const void *b) { \
    264         return order * compfn( \
    265             (*((struct proc_info**)a))->usage.field, \
    266             (*((struct proc_info**)b))->usage.field \
    267         ); \
    268     }
    269 
    270 create_sort(vss, numcmp)
    271 create_sort(rss, numcmp)
    272 create_sort(pss, numcmp)
    273 create_sort(uss, numcmp)
    274