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 <fcntl.h>
     20 #include <inttypes.h>
     21 #include <stdbool.h>
     22 #include <stdlib.h>
     23 #include <string.h>
     24 #include <sys/types.h>
     25 #include <unistd.h>
     26 
     27 #include <vector>
     28 
     29 #include <android-base/file.h>
     30 #include <android-base/stringprintf.h>
     31 #include <android-base/strings.h>
     32 #include <pagemap/pagemap.h>
     33 
     34 struct proc_info {
     35     pid_t pid;
     36     pm_memusage_t usage;
     37     uint64_t wss;
     38     int oomadj;
     39 };
     40 
     41 static void usage(char *myname);
     42 static std::string getprocname(pid_t pid);
     43 static int getoomadj(pid_t pid);
     44 static bool getminfree(std::vector<uint64_t>* minfree, std::vector<int>* adj);
     45 static int numcmp(uint64_t a, uint64_t b);
     46 
     47 #define declare_sort(field) \
     48     static int sort_by_ ## field (const void *a, const void *b)
     49 
     50 declare_sort(vss);
     51 declare_sort(rss);
     52 declare_sort(pss);
     53 declare_sort(uss);
     54 declare_sort(swap);
     55 declare_sort(oomadj);
     56 
     57 int (*compfn)(const void *a, const void *b);
     58 static int order;
     59 
     60 enum {
     61     MEMINFO_TOTAL,
     62     MEMINFO_FREE,
     63     MEMINFO_BUFFERS,
     64     MEMINFO_CACHED,
     65     MEMINFO_SHMEM,
     66     MEMINFO_SLAB,
     67     MEMINFO_SWAP_TOTAL,
     68     MEMINFO_SWAP_FREE,
     69     MEMINFO_ZRAM_TOTAL,
     70     MEMINFO_MAPPED,
     71     MEMINFO_VMALLOC_USED,
     72     MEMINFO_PAGE_TABLES,
     73     MEMINFO_KERNEL_STACK,
     74     MEMINFO_COUNT
     75 };
     76 
     77 void get_mem_info(uint64_t mem[]) {
     78     char buffer[1024];
     79     unsigned int numFound = 0;
     80 
     81     int fd = open("/proc/meminfo", O_RDONLY);
     82 
     83     if (fd < 0) {
     84         printf("Unable to open /proc/meminfo: %s\n", strerror(errno));
     85         return;
     86     }
     87 
     88     const int len = read(fd, buffer, sizeof(buffer)-1);
     89     close(fd);
     90 
     91     if (len < 0) {
     92         printf("Empty /proc/meminfo");
     93         return;
     94     }
     95     buffer[len] = 0;
     96 
     97     static const char* const tags[] = {
     98             "MemTotal:",
     99             "MemFree:",
    100             "Buffers:",
    101             "Cached:",
    102             "Shmem:",
    103             "Slab:",
    104             "SwapTotal:",
    105             "SwapFree:",
    106             "ZRam:",            // not read from meminfo but from /sys/block/zram0
    107             "Mapped:",
    108             "VmallocUsed:",
    109             "PageTables:",
    110             "KernelStack:",
    111             NULL
    112     };
    113     static const int tagsLen[] = {
    114             9,
    115             8,
    116             8,
    117             7,
    118             6,
    119             5,
    120             10,
    121             9,
    122             5,
    123             7,
    124             12,
    125             11,
    126             12,
    127             0
    128     };
    129 
    130     char* p = buffer;
    131     while (*p && (numFound < (sizeof(tagsLen) / sizeof(tagsLen[0])))) {
    132         int i = 0;
    133         while (tags[i]) {
    134             if (strncmp(p, tags[i], tagsLen[i]) == 0) {
    135                 p += tagsLen[i];
    136                 while (*p == ' ') p++;
    137                 char* num = p;
    138                 while (*p >= '0' && *p <= '9') p++;
    139                 if (*p != 0) {
    140                     *p = 0;
    141                     p++;
    142                 }
    143                 mem[i] = atoll(num);
    144                 numFound++;
    145                 break;
    146             }
    147             i++;
    148         }
    149         while (*p && *p != '\n') {
    150             p++;
    151         }
    152         if (*p) p++;
    153     }
    154 }
    155 
    156 static uint64_t get_zram_mem_used() {
    157 #define ZRAM_SYSFS "/sys/block/zram0/"
    158     FILE *f = fopen(ZRAM_SYSFS "mm_stat", "r");
    159     if (f) {
    160         uint64_t mem_used_total = 0;
    161 
    162         int matched = fscanf(f, "%*d %*d %" SCNu64 " %*d %*d %*d %*d", &mem_used_total);
    163         if (matched != 1)
    164             fprintf(stderr, "warning: failed to parse " ZRAM_SYSFS "mm_stat\n");
    165 
    166         fclose(f);
    167         return mem_used_total;
    168     }
    169 
    170     f = fopen(ZRAM_SYSFS "mem_used_total", "r");
    171     if (f) {
    172         uint64_t mem_used_total = 0;
    173 
    174         int matched = fscanf(f, "%" SCNu64, &mem_used_total);
    175         if (matched != 1)
    176             fprintf(stderr, "warning: failed to parse " ZRAM_SYSFS "mem_used_total\n");
    177 
    178         fclose(f);
    179         return mem_used_total;
    180     }
    181 
    182     return 0;
    183 }
    184 
    185 int main(int argc, char *argv[]) {
    186     pm_kernel_t *ker;
    187     pm_process_t *proc;
    188     pid_t *pids;
    189     size_t num_procs;
    190     uint64_t total_pss;
    191     uint64_t total_uss;
    192     uint64_t total_swap;
    193     uint64_t total_pswap;
    194     uint64_t total_uswap;
    195     uint64_t total_zswap;
    196     int error;
    197     bool has_swap = false, has_zram = false;
    198     uint64_t required_flags = 0;
    199     uint64_t flags_mask = 0;
    200 
    201     int arg;
    202     size_t i;
    203 
    204     enum {
    205         WS_OFF,
    206         WS_ONLY,
    207         WS_RESET,
    208     } ws;
    209 
    210     uint64_t mem[MEMINFO_COUNT] = { };
    211     pm_proportional_swap_t *p_swap;
    212     float zram_cr = 0.0;
    213 
    214     signal(SIGPIPE, SIG_IGN);
    215     compfn = &sort_by_pss;
    216     order = -1;
    217     ws = WS_OFF;
    218     bool oomadj = false;
    219 
    220     for (arg = 1; arg < argc; arg++) {
    221         if (!strcmp(argv[arg], "-v")) { compfn = &sort_by_vss; continue; }
    222         if (!strcmp(argv[arg], "-r")) { compfn = &sort_by_rss; continue; }
    223         if (!strcmp(argv[arg], "-p")) { compfn = &sort_by_pss; continue; }
    224         if (!strcmp(argv[arg], "-u")) { compfn = &sort_by_uss; continue; }
    225         if (!strcmp(argv[arg], "-s")) { compfn = &sort_by_swap; continue; }
    226         if (!strcmp(argv[arg], "-o")) { compfn = &sort_by_oomadj; oomadj = true; continue; }
    227         if (!strcmp(argv[arg], "-c")) { required_flags = 0; flags_mask = KPF_SWAPBACKED; continue; }
    228         if (!strcmp(argv[arg], "-C")) { required_flags = flags_mask = KPF_SWAPBACKED; continue; }
    229         if (!strcmp(argv[arg], "-k")) { required_flags = flags_mask = KPF_KSM; continue; }
    230         if (!strcmp(argv[arg], "-w")) { ws = WS_ONLY; continue; }
    231         if (!strcmp(argv[arg], "-W")) { ws = WS_RESET; continue; }
    232         if (!strcmp(argv[arg], "-R")) { order *= -1; continue; }
    233         if (!strcmp(argv[arg], "-h")) { usage(argv[0]); exit(0); }
    234         fprintf(stderr, "Invalid argument \"%s\".\n", argv[arg]);
    235         usage(argv[0]);
    236         exit(EXIT_FAILURE);
    237     }
    238 
    239     get_mem_info(mem);
    240     p_swap = pm_memusage_pswap_create(mem[MEMINFO_SWAP_TOTAL] * 1024);
    241 
    242     error = pm_kernel_create(&ker);
    243     if (error) {
    244         fprintf(stderr, "Error creating kernel interface -- "
    245                         "does this kernel have pagemap?\n");
    246         exit(EXIT_FAILURE);
    247     }
    248 
    249     error = pm_kernel_pids(ker, &pids, &num_procs);
    250     if (error) {
    251         fprintf(stderr, "Error listing processes.\n");
    252         exit(EXIT_FAILURE);
    253     }
    254 
    255     std::vector<proc_info> procs(num_procs);
    256     for (i = 0; i < num_procs; i++) {
    257         procs[i].pid = pids[i];
    258         procs[i].oomadj = getoomadj(pids[i]);
    259         pm_memusage_zero(&procs[i].usage);
    260         pm_memusage_pswap_init_handle(&procs[i].usage, p_swap);
    261         error = pm_process_create(ker, pids[i], &proc);
    262         if (error) {
    263             fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]);
    264             continue;
    265         }
    266 
    267         switch (ws) {
    268         case WS_OFF:
    269             error = pm_process_usage_flags(proc, &procs[i].usage, flags_mask,
    270                                            required_flags);
    271             break;
    272         case WS_ONLY:
    273             error = pm_process_workingset(proc, &procs[i].usage, 0);
    274             break;
    275         case WS_RESET:
    276             error = pm_process_workingset(proc, NULL, 1);
    277             break;
    278         }
    279 
    280         if (error) {
    281             fprintf(stderr, "warning: could not read usage for %d\n", pids[i]);
    282         }
    283 
    284         if (ws != WS_RESET && procs[i].usage.swap) {
    285             has_swap = true;
    286         }
    287 
    288         pm_process_destroy(proc);
    289     }
    290 
    291     free(pids);
    292 
    293     if (ws == WS_RESET) exit(0);
    294 
    295     procs.erase(std::remove_if(procs.begin(),
    296                                procs.end(),
    297                                [](auto proc){
    298                                    return proc.usage.vss == 0;
    299                                }),
    300                 procs.end());
    301 
    302     qsort(procs.data(), procs.size(), sizeof(procs[0]), compfn);
    303 
    304     if (has_swap) {
    305         uint64_t zram_mem_used = get_zram_mem_used();
    306         if (zram_mem_used) {
    307             mem[MEMINFO_ZRAM_TOTAL] = zram_mem_used/1024;
    308             zram_cr = (float) mem[MEMINFO_ZRAM_TOTAL] /
    309                     (mem[MEMINFO_SWAP_TOTAL] - mem[MEMINFO_SWAP_FREE]);
    310             has_zram = true;
    311         }
    312     }
    313 
    314     printf("%5s  ", "PID");
    315     if (oomadj) {
    316         printf("%5s  ", "oom");
    317     }
    318     if (ws) {
    319         printf("%7s  %7s  %7s  ", "WRss", "WPss", "WUss");
    320         if (has_swap) {
    321             printf("%7s  %7s  %7s  ", "WSwap", "WPSwap", "WUSwap");
    322             if (has_zram) {
    323                 printf("%7s  ", "WZSwap");
    324             }
    325         }
    326     } else {
    327         printf("%8s  %7s  %7s  %7s  ", "Vss", "Rss", "Pss", "Uss");
    328         if (has_swap) {
    329             printf("%7s  %7s  %7s  ", "Swap", "PSwap", "USwap");
    330             if (has_zram) {
    331                 printf("%7s  ", "ZSwap");
    332             }
    333         }
    334     }
    335 
    336     printf("%s\n", "cmdline");
    337 
    338     total_pss = 0;
    339     total_uss = 0;
    340     total_swap = 0;
    341     total_pswap = 0;
    342     total_uswap = 0;
    343     total_zswap = 0;
    344 
    345     std::vector<uint64_t> lmk_minfree;
    346     std::vector<int> lmk_adj;
    347     if (oomadj) {
    348         getminfree(&lmk_minfree, &lmk_adj);
    349     }
    350     auto lmk_minfree_it = lmk_minfree.cbegin();
    351     auto lmk_adj_it = lmk_adj.cbegin();
    352 
    353     auto print_oomadj_totals = [&](int adj){
    354         for (; lmk_adj_it != lmk_adj.cend() && lmk_minfree_it != lmk_minfree.cend() &&
    355                  adj > *lmk_adj_it; lmk_adj_it++, lmk_minfree_it++) {
    356             // Print the cumulative total line
    357             printf("%5s  ", ""); // pid
    358 
    359             printf("%5s  ", ""); // oomadj
    360 
    361             if (ws) {
    362                 printf("%7s  %6" PRIu64 "K  %6" PRIu64 "K  ",
    363                        "", total_pss / 1024, total_uss / 1024);
    364             } else {
    365                 printf("%8s  %7s  %6" PRIu64 "K  %6" PRIu64 "K  ",
    366                        "", "", total_pss / 1024, total_uss / 1024);
    367             }
    368 
    369             if (has_swap) {
    370                 printf("%6" PRIu64 "K  ", total_swap / 1024);
    371                 printf("%6" PRIu64 "K  ", total_pswap / 1024);
    372                 printf("%6" PRIu64 "K  ", total_uswap / 1024);
    373                 if (has_zram) {
    374                     printf("%6" PRIu64 "K  ", total_zswap / 1024);
    375                 }
    376             }
    377 
    378             printf("TOTAL for oomadj < %d (%6" PRIu64 "K)\n", *lmk_adj_it, *lmk_minfree_it / 1024);
    379         }
    380     };
    381 
    382     for (auto& proc: procs) {
    383         if (oomadj) {
    384             print_oomadj_totals(proc.oomadj);
    385         }
    386 
    387         std::string cmdline = getprocname(proc.pid);
    388 
    389         total_pss += proc.usage.pss;
    390         total_uss += proc.usage.uss;
    391         total_swap += proc.usage.swap;
    392 
    393         printf("%5d  ", proc.pid);
    394 
    395         if (oomadj) {
    396             printf("%5d  ", proc.oomadj);
    397         }
    398 
    399         if (ws) {
    400             printf("%6zuK  %6zuK  %6zuK  ",
    401                 proc.usage.rss / 1024,
    402                 proc.usage.pss / 1024,
    403                 proc.usage.uss / 1024
    404             );
    405         } else {
    406             printf("%7zuK  %6zuK  %6zuK  %6zuK  ",
    407                 proc.usage.vss / 1024,
    408                 proc.usage.rss / 1024,
    409                 proc.usage.pss / 1024,
    410                 proc.usage.uss / 1024
    411             );
    412         }
    413 
    414         if (has_swap) {
    415             pm_swapusage_t su;
    416 
    417             pm_memusage_pswap_get_usage(&proc.usage, &su);
    418             printf("%6zuK  ", proc.usage.swap / 1024);
    419             printf("%6zuK  ", su.proportional / 1024);
    420             printf("%6zuK  ", su.unique / 1024);
    421             total_pswap += su.proportional;
    422             total_uswap += su.unique;
    423             pm_memusage_pswap_free(&proc.usage);
    424             if (has_zram) {
    425                 size_t zpswap = su.proportional * zram_cr;
    426                 printf("%6zuK  ", zpswap / 1024);
    427                 total_zswap += zpswap;
    428             }
    429         }
    430 
    431         printf("%s\n", cmdline.c_str());
    432     }
    433 
    434     pm_memusage_pswap_destroy(p_swap);
    435 
    436     if (oomadj) {
    437         print_oomadj_totals(INT_MAX);
    438     }
    439 
    440     // Print the separator line
    441     printf("%5s  ", "");
    442 
    443     if (oomadj) {
    444         printf("%5s  ", "");
    445     }
    446 
    447     if (ws) {
    448         printf("%7s  %7s  %7s  ", "", "------", "------");
    449     } else {
    450         printf("%8s  %7s  %7s  %7s  ", "", "", "------", "------");
    451     }
    452 
    453     if (has_swap) {
    454         printf("%7s  %7s  %7s  ", "------", "------", "------");
    455         if (has_zram) {
    456             printf("%7s  ", "------");
    457         }
    458     }
    459 
    460     printf("%s\n", "------");
    461 
    462     // Print the total line
    463     printf("%5s  ", "");
    464 
    465     if (oomadj) {
    466         printf("%5s  ", "");
    467     }
    468 
    469     if (ws) {
    470         printf("%7s  %6" PRIu64 "K  %6" PRIu64 "K  ",
    471             "", total_pss / 1024, total_uss / 1024);
    472     } else {
    473         printf("%8s  %7s  %6" PRIu64 "K  %6" PRIu64 "K  ",
    474             "", "", total_pss / 1024, total_uss / 1024);
    475     }
    476 
    477     if (has_swap) {
    478         printf("%6" PRIu64 "K  ", total_swap / 1024);
    479         printf("%6" PRIu64 "K  ", total_pswap / 1024);
    480         printf("%6" PRIu64 "K  ", total_uswap / 1024);
    481         if (has_zram) {
    482             printf("%6" PRIu64 "K  ", total_zswap / 1024);
    483         }
    484     }
    485 
    486     printf("TOTAL\n");
    487 
    488     printf("\n");
    489 
    490     if (has_swap) {
    491         printf("ZRAM: %" PRIu64 "K physical used for %" PRIu64 "K in swap "
    492                 "(%" PRIu64 "K total swap)\n",
    493                 mem[MEMINFO_ZRAM_TOTAL], (mem[MEMINFO_SWAP_TOTAL] - mem[MEMINFO_SWAP_FREE]),
    494                 mem[MEMINFO_SWAP_TOTAL]);
    495     }
    496     printf(" RAM: %" PRIu64 "K total, %" PRIu64 "K free, %" PRIu64 "K buffers, "
    497             "%" PRIu64 "K cached, %" PRIu64 "K shmem, %" PRIu64 "K slab\n",
    498             mem[MEMINFO_TOTAL], mem[MEMINFO_FREE], mem[MEMINFO_BUFFERS],
    499             mem[MEMINFO_CACHED], mem[MEMINFO_SHMEM], mem[MEMINFO_SLAB]);
    500 
    501     return 0;
    502 }
    503 
    504 static void usage(char *myname) {
    505     fprintf(stderr, "Usage: %s [ -W ] [ -v | -r | -p | -u | -s | -h ]\n"
    506                     "    -v  Sort by VSS.\n"
    507                     "    -r  Sort by RSS.\n"
    508                     "    -p  Sort by PSS.\n"
    509                     "    -u  Sort by USS.\n"
    510                     "    -s  Sort by swap.\n"
    511                     "        (Default sort order is PSS.)\n"
    512                     "    -R  Reverse sort order (default is descending).\n"
    513                     "    -c  Only show cached (storage backed) pages\n"
    514                     "    -C  Only show non-cached (ram/swap backed) pages\n"
    515                     "    -k  Only show pages collapsed by KSM\n"
    516                     "    -w  Display statistics for working set only.\n"
    517                     "    -W  Reset working set of all processes.\n"
    518                     "    -o  Show and sort by oom score against lowmemorykiller thresholds.\n"
    519                     "    -h  Display this help screen.\n",
    520     myname);
    521 }
    522 
    523 // Get the process name for a given PID.
    524 static std::string getprocname(pid_t pid) {
    525     std::string filename = android::base::StringPrintf("/proc/%d/cmdline", pid);
    526 
    527     std::string procname;
    528 
    529     if (!android::base::ReadFileToString(filename, &procname)) {
    530         // The process went away before we could read its process name.
    531         procname = "<unknown>";
    532     }
    533 
    534     return procname;
    535 }
    536 
    537 static int getoomadj(pid_t pid) {
    538     std::string filename = android::base::StringPrintf("/proc/%d/oom_score_adj", pid);
    539     std::string oomadj;
    540 
    541     if (!android::base::ReadFileToString(filename, &oomadj)) {
    542         return -1001;
    543     }
    544 
    545     return strtol(oomadj.c_str(), NULL, 10);
    546 }
    547 
    548 static bool getminfree(std::vector<uint64_t>* minfree, std::vector<int>* adj) {
    549     std::string minfree_str;
    550     std::string adj_str;
    551 
    552     if (!android::base::ReadFileToString("/sys/module/lowmemorykiller/parameters/minfree", &minfree_str)) {
    553         return false;
    554     }
    555 
    556     if (!android::base::ReadFileToString("/sys/module/lowmemorykiller/parameters/adj", &adj_str)) {
    557         return false;
    558     }
    559 
    560     std::vector<std::string> minfree_vec = android::base::Split(minfree_str, ",");
    561     std::vector<std::string> adj_vec = android::base::Split(adj_str, ",");
    562 
    563     minfree->clear();
    564     minfree->resize(minfree_vec.size());
    565     adj->clear();
    566     adj->resize(adj_vec.size());
    567 
    568     std::transform(minfree_vec.begin(), minfree_vec.end(), minfree->begin(),
    569                    [](const std::string& s) -> uint64_t {
    570                        return strtoull(s.c_str(), NULL, 10) * PAGE_SIZE;
    571                    });
    572 
    573     std::transform(adj_vec.begin(), adj_vec.end(), adj->begin(),
    574                    [](const std::string& s) -> int {
    575                        return strtol(s.c_str(), NULL, 10);
    576                    });
    577 
    578     return true;
    579 }
    580 
    581 static int numcmp(uint64_t a, uint64_t b) {
    582     if (a < b) return -1;
    583     if (a > b) return 1;
    584     return 0;
    585 }
    586 
    587 static int snumcmp(int64_t a, int64_t b) {
    588     if (a < b) return -1;
    589     if (a > b) return 1;
    590     return 0;
    591 }
    592 
    593 #define create_sort(field, compfn) \
    594     static int sort_by_ ## field (const void *a, const void *b) { \
    595         return order * compfn( \
    596             ((struct proc_info*)(a))->usage.field, \
    597             ((struct proc_info*)(b))->usage.field  \
    598         ); \
    599     }
    600 
    601 create_sort(vss, numcmp)
    602 create_sort(rss, numcmp)
    603 create_sort(pss, numcmp)
    604 create_sort(uss, numcmp)
    605 create_sort(swap, numcmp)
    606 
    607 static int sort_by_oomadj (const void *a, const void *b) {
    608     // Negative oomadj is higher priority, reverse the sort order
    609     return -1 * order * snumcmp(
    610         ((struct proc_info*)a)->oomadj,
    611         ((struct proc_info*)b)->oomadj
    612         );
    613 }
    614