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 <assert.h> 18#include <dirent.h> 19#include <errno.h> 20#include <getopt.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 <pagemap/pagemap.h> 28 29#define MAX_CMDLINE 256 30 31struct process_info { 32 pid_t pid; 33 char cmdline[MAX_CMDLINE]; 34}; 35 36struct mapping_info { 37 struct process_info *proc; 38 pm_memusage_t usage; 39}; 40 41struct library_info { 42 struct library_info *next; 43 char *name; 44 struct mapping_info **mappings; 45 size_t mappings_count; 46 size_t mappings_size; 47 pm_memusage_t total_usage; 48}; 49 50static void usage(char *myname); 51static int getprocname(pid_t pid, char *buf, size_t len); 52static int numcmp(long long a, long long b); 53static int licmp(const void *a, const void *b); 54 55char *library_name_blacklist[] = { "[heap]", "[stack]", "", NULL }; 56 57#define declare_sort(field) \ 58 static int sort_by_ ## field (const void *a, const void *b) 59 60declare_sort(vss); 61declare_sort(rss); 62declare_sort(pss); 63declare_sort(uss); 64declare_sort(swap); 65 66#define INIT_LIBRARIES 16 67#define INIT_MAPPINGS 4 68 69static int order; 70 71struct library_info **libraries; 72size_t libraries_count; 73size_t libraries_size; 74 75struct library_info *get_library(const char *name, bool all) { 76 size_t i; 77 struct library_info *library; 78 79 if (!all) { 80 for (i = 0; library_name_blacklist[i]; i++) 81 if (!strcmp(name, library_name_blacklist[i])) 82 return NULL; 83 } else { 84 if (name[0] == 0) { 85 name = "[anon]"; 86 } 87 } 88 89 for (i = 0; i < libraries_count; i++) { 90 if (!strcmp(libraries[i]->name, name)) 91 return libraries[i]; 92 } 93 94 if (libraries_count >= libraries_size) { 95 libraries = realloc(libraries, 2 * libraries_size * sizeof(struct library_info *)); 96 if (!libraries) { 97 fprintf(stderr, "Couldn't resize libraries array: %s\n", strerror(errno)); 98 exit(EXIT_FAILURE); 99 } 100 libraries_size = 2 * libraries_size; 101 } 102 103 library = calloc(1, sizeof(*library)); 104 if (!library) { 105 fprintf(stderr, "Couldn't allocate space for library struct: %s\n", strerror(errno)); 106 exit(EXIT_FAILURE); 107 } 108 library->name = malloc(strlen(name) + 1); 109 if (!library->name) { 110 fprintf(stderr, "Couldn't allocate space for library name: %s\n", strerror(errno)); 111 exit(EXIT_FAILURE); 112 } 113 strcpy(library->name, name); 114 library->mappings = malloc(INIT_MAPPINGS * sizeof(struct mapping_info *)); 115 if (!library->mappings) { 116 fprintf(stderr, "Couldn't allocate space for library mappings array: %s\n", strerror(errno)); 117 exit(EXIT_FAILURE); 118 } 119 library->mappings_count = 0; library->mappings_size = INIT_MAPPINGS; 120 pm_memusage_zero(&library->total_usage); 121 122 libraries[libraries_count++] = library; 123 124 return library; 125} 126 127struct mapping_info *get_mapping(struct library_info *library, struct process_info *proc) { 128 struct mapping_info *mapping; 129 size_t i; 130 131 for (i = 0; i < library->mappings_count; i++) { 132 if (library->mappings[i]->proc == proc) 133 return library->mappings[i]; 134 } 135 136 if (library->mappings_count >= library->mappings_size) { 137 library->mappings = realloc(library->mappings, 138 2 * library->mappings_size * sizeof(struct mapping*)); 139 if (!library->mappings) { 140 fprintf(stderr, "Couldn't resize mappings array: %s\n", strerror(errno)); 141 exit(EXIT_FAILURE); 142 } 143 library->mappings_size = 2 * library->mappings_size; 144 } 145 146 mapping = calloc(1, sizeof(*mapping)); 147 if (!mapping) { 148 fprintf(stderr, "Couldn't allocate space for mapping struct: %s\n", strerror(errno)); 149 exit(EXIT_FAILURE); 150 } 151 mapping->proc = proc; 152 pm_memusage_zero(&mapping->usage); 153 154 library->mappings[library->mappings_count++] = mapping; 155 156 return mapping; 157} 158 159struct process_info *get_process(pid_t pid) { 160 struct process_info *process; 161 162 process = calloc(1, sizeof(*process)); 163 if (!process) { 164 fprintf(stderr, "Couldn't allocate space for process struct: %s\n", strerror(errno)); 165 exit(EXIT_FAILURE); 166 } 167 168 process->pid = pid; 169 getprocname(pid, process->cmdline, sizeof(process->cmdline)); 170 171 return process; 172} 173 174static int parse_perm(const char *perm) 175{ 176 int ret = 0; 177 178 while (*perm) { 179 switch(*perm) { 180 case 'r': 181 ret |= PM_MAP_READ; 182 break; 183 case 'w': 184 ret |= PM_MAP_WRITE; 185 break; 186 case 'x': 187 ret |= PM_MAP_EXEC; 188 break; 189 default: 190 fprintf(stderr, "Unknown permission '%c'\n", *perm); 191 exit(EXIT_FAILURE); 192 } 193 perm++; 194 } 195 return ret; 196} 197 198int main(int argc, char *argv[]) { 199 char cmdline[256]; 200 char *prefix; 201 size_t prefix_len; 202 int (*compfn)(const void *a, const void *b); 203 204 pm_kernel_t *ker; 205 pm_process_t *proc; 206 207 pid_t *pids; 208 size_t num_procs; 209 210 pm_map_t **maps; 211 size_t num_maps; 212 pm_memusage_t map_usage; 213 214 struct library_info *li, **lis; 215 struct mapping_info *mi, **mis; 216 struct process_info *pi; 217 218 size_t i, j; 219 int error; 220 int perm; 221 bool all; 222 uint64_t required_flags; 223 uint64_t flags_mask; 224 225 bool has_swap = false; 226 227 signal(SIGPIPE, SIG_IGN); 228 compfn = &sort_by_pss; 229 order = -1; 230 prefix = NULL; 231 prefix_len = 0; 232 opterr = 0; 233 perm = 0; 234 all = false; 235 required_flags = 0; 236 flags_mask = 0; 237 238 while (1) { 239 int c; 240 const struct option longopts[] = { 241 {"all", 0, 0, 'a'}, 242 {"cached", 0, 0, 'c'}, 243 {"nocached", 0, 0, 'C'}, 244 {"ksm", 0, 0, 'k'}, 245 {"help", 0, 0, 'h'}, 246 {"pss", 0, 0, 'p'}, 247 {"uss", 0, 0, 'u'}, 248 {"vss", 0, 0, 'v'}, 249 {"rss", 0, 0, 'r'}, 250 {"swap", 0, 0, 's'}, 251 {"reverse", 0, 0, 'R'}, 252 {"path", required_argument, 0, 'P'}, 253 {"perm", required_argument, 0, 'm'}, 254 {0, 0, 0, 0} 255 }; 256 c = getopt_long(argc, argv, "acChkm:pP:uvrsR", longopts, NULL); 257 if (c < 0) { 258 break; 259 } 260 /* Alphabetical cases */ 261 switch (c) { 262 case 'a': 263 all = true; 264 break; 265 case 'c': 266 required_flags = 0; 267 flags_mask = PM_PAGE_SWAPBACKED; 268 break; 269 case 'C': 270 required_flags = PM_PAGE_SWAPBACKED; 271 flags_mask = PM_PAGE_SWAPBACKED; 272 break; 273 case 'k': 274 required_flags = PM_PAGE_KSM; 275 flags_mask = PM_PAGE_KSM; 276 break; 277 case 'h': 278 usage(argv[0]); 279 exit(EXIT_SUCCESS); 280 case 'm': 281 perm = parse_perm(optarg); 282 break; 283 case 'p': 284 compfn = &sort_by_pss; 285 break; 286 case 'P': 287 prefix = optarg; 288 prefix_len = strlen(prefix); 289 break; 290 case 'u': 291 compfn = &sort_by_uss; 292 break; 293 case 'v': 294 compfn = &sort_by_vss; 295 break; 296 case 'r': 297 compfn = &sort_by_rss; 298 break; 299 case 's': 300 compfn = &sort_by_swap; 301 break; 302 case 'R': 303 order *= -1; 304 break; 305 case '?': 306 fprintf(stderr, "Invalid argument \"%s\".\n", argv[optind - 1]); 307 usage(argv[0]); 308 exit(EXIT_FAILURE); 309 default: 310 abort(); 311 } 312 } 313 314 argc -= optind; 315 argv += optind; 316 317 libraries = malloc(INIT_LIBRARIES * sizeof(struct library_info *)); 318 libraries_count = 0; libraries_size = INIT_LIBRARIES; 319 pm_memusage_zero(&map_usage); 320 321 error = pm_kernel_create(&ker); 322 if (error) { 323 fprintf(stderr, "Error initializing kernel interface -- " 324 "does this kernel have pagemap?\n"); 325 exit(EXIT_FAILURE); 326 } 327 328 error = pm_kernel_pids(ker, &pids, &num_procs); 329 if (error) { 330 fprintf(stderr, "Error listing processes.\n"); 331 exit(EXIT_FAILURE); 332 } 333 334 for (i = 0; i < num_procs; i++) { 335 error = pm_process_create(ker, pids[i], &proc); 336 if (error) { 337 fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]); 338 continue; 339 } 340 341 pi = get_process(pids[i]); 342 343 error = pm_process_maps(proc, &maps, &num_maps); 344 if (error) { 345 fprintf(stderr, "Error listing maps for process %d.\n", proc->pid); 346 exit(EXIT_FAILURE); 347 } 348 349 for (j = 0; j < num_maps; j++) { 350 if (prefix && (strncmp(pm_map_name(maps[j]), prefix, prefix_len))) 351 continue; 352 353 if (perm && (pm_map_flags(maps[j]) & PM_MAP_PERMISSIONS) != perm) 354 continue; 355 356 li = get_library(pm_map_name(maps[j]), all); 357 if (!li) 358 continue; 359 360 mi = get_mapping(li, pi); 361 362 error = pm_map_usage_flags(maps[j], &map_usage, flags_mask, 363 required_flags); 364 if (error) { 365 fprintf(stderr, "Error getting map memory usage of " 366 "map %s in process %d.\n", 367 pm_map_name(maps[j]), proc->pid); 368 exit(EXIT_FAILURE); 369 } 370 371 if (map_usage.swap) { 372 has_swap = true; 373 } 374 375 pm_memusage_add(&mi->usage, &map_usage); 376 pm_memusage_add(&li->total_usage, &map_usage); 377 } 378 } 379 380 printf(" %6s %7s %6s %6s %6s ", "RSStot", "VSS", "RSS", "PSS", "USS"); 381 382 if (has_swap) { 383 printf(" %6s ", "Swap"); 384 } 385 386 printf("Name/PID\n"); 387 fflush(stdout); 388 389 qsort(libraries, libraries_count, sizeof(libraries[0]), &licmp); 390 391 for (i = 0; i < libraries_count; i++) { 392 li = libraries[i]; 393 394 printf("%6zdK %7s %6s %6s %6s ", li->total_usage.pss / 1024, "", "", "", ""); 395 if (has_swap) { 396 printf(" %6s ", ""); 397 } 398 printf("%s\n", li->name); 399 fflush(stdout); 400 401 qsort(li->mappings, li->mappings_count, sizeof(li->mappings[0]), compfn); 402 403 for (j = 0; j < li->mappings_count; j++) { 404 mi = li->mappings[j]; 405 pi = mi->proc; 406 printf( " %6s %7zdK %6zdK %6zdK %6zdK ", "", 407 mi->usage.vss / 1024, 408 mi->usage.rss / 1024, 409 mi->usage.pss / 1024, 410 mi->usage.uss / 1024); 411 if (has_swap) { 412 printf("%6zdK ", mi->usage.swap / 1024); 413 } 414 printf(" %s [%d]\n", 415 pi->cmdline, 416 pi->pid); 417 } 418 printf("\n"); 419 fflush(stdout); 420 } 421 422 return 0; 423} 424 425static void usage(char *myname) { 426 fprintf(stderr, "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -s | -h ]\n" 427 "\n" 428 "Sort options:\n" 429 " -v Sort processes by VSS.\n" 430 " -r Sort processes by RSS.\n" 431 " -p Sort processes by PSS.\n" 432 " -u Sort processes by USS.\n" 433 " -s Sort processes by swap.\n" 434 " (Default sort order is PSS.)\n" 435 " -a Show all mappings, including stack, heap and anon.\n" 436 " -P /path Limit libraries displayed to those in path.\n" 437 " -R Reverse sort order (default is descending).\n" 438 " -m [r][w][x] Only list pages that exactly match permissions\n" 439 " -c Only show cached (storage backed) pages\n" 440 " -C Only show non-cached (ram/swap backed) pages\n" 441 " -k Only show pages collapsed by KSM\n" 442 " -h Display this help screen.\n", 443 myname); 444} 445 446static int getprocname(pid_t pid, char *buf, size_t len) { 447 char filename[32]; 448 FILE *f; 449 450 snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid); 451 f = fopen(filename, "r"); 452 if (!f) { 453 *buf = '\0'; 454 return 1; 455 } 456 if (!fgets(buf, len, f)) { 457 *buf = '\0'; 458 fclose(f); 459 return 2; 460 } 461 fclose(f); 462 return 0; 463} 464 465static int numcmp(long long a, long long b) { 466 if (a < b) return -1; 467 if (a > b) return 1; 468 return 0; 469} 470 471static int licmp(const void *a, const void *b) { 472 return order * numcmp( 473 (*((struct library_info**)a))->total_usage.pss, 474 (*((struct library_info**)b))->total_usage.pss 475 ); 476} 477 478#define create_sort(field, compfn) \ 479 static int sort_by_ ## field (const void *a, const void *b) { \ 480 return order * compfn( \ 481 (*((struct mapping_info**)a))->usage.field, \ 482 (*((struct mapping_info**)b))->usage.field \ 483 ); \ 484 } 485 486create_sort(vss, numcmp) 487create_sort(rss, numcmp) 488create_sort(pss, numcmp) 489create_sort(uss, numcmp) 490create_sort(swap, numcmp) 491