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