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 int mappings_count; 46 int 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; 72int libraries_count; 73int libraries_size; 74 75struct library_info *get_library(const char *name, bool all) { 76 int 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 int 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 int i, j, error; 219 int perm; 220 bool all; 221 uint64_t required_flags; 222 uint64_t flags_mask; 223 224 bool has_swap = false; 225 226 signal(SIGPIPE, SIG_IGN); 227 compfn = &sort_by_pss; 228 order = -1; 229 prefix = NULL; 230 prefix_len = 0; 231 opterr = 0; 232 perm = 0; 233 all = false; 234 required_flags = 0; 235 flags_mask = 0; 236 237 while (1) { 238 int c; 239 const struct option longopts[] = { 240 {"all", 0, 0, 'a'}, 241 {"cached", 0, 0, 'c'}, 242 {"nocached", 0, 0, 'C'}, 243 {"ksm", 0, 0, 'k'}, 244 {"help", 0, 0, 'h'}, 245 {"pss", 0, 0, 'p'}, 246 {"uss", 0, 0, 'u'}, 247 {"vss", 0, 0, 'v'}, 248 {"rss", 0, 0, 'r'}, 249 {"swap", 0, 0, 's'}, 250 {"reverse", 0, 0, 'R'}, 251 {"path", required_argument, 0, 'P'}, 252 {"perm", required_argument, 0, 'm'}, 253 {0, 0, 0, 0} 254 }; 255 c = getopt_long(argc, argv, "acChkm:pP:uvrsR", longopts, NULL); 256 if (c < 0) { 257 break; 258 } 259 /* Alphabetical cases */ 260 switch (c) { 261 case 'a': 262 all = true; 263 break; 264 case 'c': 265 required_flags = 0; 266 flags_mask = PM_PAGE_SWAPBACKED; 267 break; 268 case 'C': 269 required_flags = PM_PAGE_SWAPBACKED; 270 flags_mask = PM_PAGE_SWAPBACKED; 271 break; 272 case 'k': 273 required_flags = PM_PAGE_KSM; 274 flags_mask = PM_PAGE_KSM; 275 break; 276 case 'h': 277 usage(argv[0]); 278 exit(EXIT_SUCCESS); 279 case 'm': 280 perm = parse_perm(optarg); 281 break; 282 case 'p': 283 compfn = &sort_by_pss; 284 break; 285 case 'P': 286 prefix = optarg; 287 prefix_len = strlen(prefix); 288 break; 289 case 'u': 290 compfn = &sort_by_uss; 291 break; 292 case 'v': 293 compfn = &sort_by_vss; 294 break; 295 case 'r': 296 compfn = &sort_by_rss; 297 break; 298 case 's': 299 compfn = &sort_by_swap; 300 break; 301 case 'R': 302 order *= -1; 303 break; 304 case '?': 305 fprintf(stderr, "Invalid argument \"%s\".\n", argv[optind - 1]); 306 usage(argv[0]); 307 exit(EXIT_FAILURE); 308 default: 309 abort(); 310 } 311 } 312 313 argc -= optind; 314 argv += optind; 315 316 libraries = malloc(INIT_LIBRARIES * sizeof(struct library_info *)); 317 libraries_count = 0; libraries_size = INIT_LIBRARIES; 318 319 error = pm_kernel_create(&ker); 320 if (error) { 321 fprintf(stderr, "Error initializing kernel interface -- " 322 "does this kernel have pagemap?\n"); 323 exit(EXIT_FAILURE); 324 } 325 326 error = pm_kernel_pids(ker, &pids, &num_procs); 327 if (error) { 328 fprintf(stderr, "Error listing processes.\n"); 329 exit(EXIT_FAILURE); 330 } 331 332 for (i = 0; i < num_procs; i++) { 333 error = pm_process_create(ker, pids[i], &proc); 334 if (error) { 335 fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]); 336 continue; 337 } 338 339 pi = get_process(pids[i]); 340 341 error = pm_process_maps(proc, &maps, &num_maps); 342 if (error) { 343 fprintf(stderr, "Error listing maps for process %d.\n", proc->pid); 344 exit(EXIT_FAILURE); 345 } 346 347 for (j = 0; j < num_maps; j++) { 348 if (prefix && (strncmp(pm_map_name(maps[j]), prefix, prefix_len))) 349 continue; 350 351 if (perm && (pm_map_flags(maps[j]) & PM_MAP_PERMISSIONS) != perm) 352 continue; 353 354 li = get_library(pm_map_name(maps[j]), all); 355 if (!li) 356 continue; 357 358 mi = get_mapping(li, pi); 359 360 error = pm_map_usage_flags(maps[j], &map_usage, flags_mask, 361 required_flags); 362 if (error) { 363 fprintf(stderr, "Error getting map memory usage of " 364 "map %s in process %d.\n", 365 pm_map_name(maps[j]), proc->pid); 366 exit(EXIT_FAILURE); 367 } 368 369 if (map_usage.swap) { 370 has_swap = true; 371 } 372 373 pm_memusage_add(&mi->usage, &map_usage); 374 pm_memusage_add(&li->total_usage, &map_usage); 375 } 376 } 377 378 printf(" %6s %6s %6s %6s %6s ", "RSStot", "VSS", "RSS", "PSS", "USS"); 379 380 if (has_swap) { 381 printf(" %6s ", "Swap"); 382 } 383 384 printf("Name/PID\n"); 385 fflush(stdout); 386 387 qsort(libraries, libraries_count, sizeof(libraries[0]), &licmp); 388 389 for (i = 0; i < libraries_count; i++) { 390 li = libraries[i]; 391 392 printf("%6dK %6s %6s %6s %6s ", li->total_usage.pss / 1024, "", "", "", ""); 393 if (has_swap) { 394 printf(" %6s ", ""); 395 } 396 printf("%s\n", li->name); 397 fflush(stdout); 398 399 qsort(li->mappings, li->mappings_count, sizeof(li->mappings[0]), compfn); 400 401 for (j = 0; j < li->mappings_count; j++) { 402 mi = li->mappings[j]; 403 pi = mi->proc; 404 printf( " %6s %6dK %6dK %6dK %6dK ", "", 405 mi->usage.vss / 1024, 406 mi->usage.rss / 1024, 407 mi->usage.pss / 1024, 408 mi->usage.uss / 1024); 409 if (has_swap) { 410 printf("%6dK ", mi->usage.swap / 1024); 411 } 412 printf(" %s [%d]\n", 413 pi->cmdline, 414 pi->pid); 415 } 416 printf("\n"); 417 fflush(stdout); 418 } 419 420 return 0; 421} 422 423static void usage(char *myname) { 424 fprintf(stderr, "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -s | -h ]\n" 425 "\n" 426 "Sort options:\n" 427 " -v Sort processes by VSS.\n" 428 " -r Sort processes by RSS.\n" 429 " -p Sort processes by PSS.\n" 430 " -u Sort processes by USS.\n" 431 " -s Sort processes by swap.\n" 432 " (Default sort order is PSS.)\n" 433 " -a Show all mappings, including stack, heap and anon.\n" 434 " -P /path Limit libraries displayed to those in path.\n" 435 " -R Reverse sort order (default is descending).\n" 436 " -m [r][w][x] Only list pages that exactly match permissions\n" 437 " -c Only show cached (storage backed) pages\n" 438 " -C Only show non-cached (ram/swap backed) pages\n" 439 " -k Only show pages collapsed by KSM\n" 440 " -h Display this help screen.\n", 441 myname); 442} 443 444static int getprocname(pid_t pid, char *buf, size_t len) { 445 char filename[20]; 446 FILE *f; 447 448 sprintf(filename, "/proc/%d/cmdline", pid); 449 f = fopen(filename, "r"); 450 if (!f) { *buf = '\0'; return 1; } 451 if (!fgets(buf, len, f)) { *buf = '\0'; return 2; } 452 fclose(f); 453 return 0; 454} 455 456static int numcmp(long long a, long long b) { 457 if (a < b) return -1; 458 if (a > b) return 1; 459 return 0; 460} 461 462static int licmp(const void *a, const void *b) { 463 return order * numcmp( 464 (*((struct library_info**)a))->total_usage.pss, 465 (*((struct library_info**)b))->total_usage.pss 466 ); 467} 468 469#define create_sort(field, compfn) \ 470 static int sort_by_ ## field (const void *a, const void *b) { \ 471 return order * compfn( \ 472 (*((struct mapping_info**)a))->usage.field, \ 473 (*((struct mapping_info**)b))->usage.field \ 474 ); \ 475 } 476 477create_sort(vss, numcmp) 478create_sort(rss, numcmp) 479create_sort(pss, numcmp) 480create_sort(uss, numcmp) 481create_sort(swap, numcmp) 482