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