OSDN Git Service

Merge "simpleperf: add test for recording cpu-clock." am: 9dc8dd6de2 am: 4f5d2f4357...
[android-x86/system-extras.git] / procrank / procrank.cpp
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 }