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