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 31 struct process_info { 32 pid_t pid; 33 char cmdline[MAX_CMDLINE]; 34 }; 35 36 struct mapping_info { 37 struct process_info *proc; 38 pm_memusage_t usage; 39 }; 40 41 struct 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 50 static void usage(char *myname); 51 static int getprocname(pid_t pid, char *buf, size_t len); 52 static int numcmp(long long a, long long b); 53 static int licmp(const void *a, const void *b); 54 55 char *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 60 declare_sort(vss); 61 declare_sort(rss); 62 declare_sort(pss); 63 declare_sort(uss); 64 declare_sort(swap); 65 66 #define INIT_LIBRARIES 16 67 #define INIT_MAPPINGS 4 68 69 static int order; 70 71 struct library_info **libraries; 72 size_t libraries_count; 73 size_t libraries_size; 74 75 struct 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 127 struct 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 159 struct 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 174 static 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 198 int 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 320 error = pm_kernel_create(&ker); 321 if (error) { 322 fprintf(stderr, "Error initializing kernel interface -- " 323 "does this kernel have pagemap?\n"); 324 exit(EXIT_FAILURE); 325 } 326 327 error = pm_kernel_pids(ker, &pids, &num_procs); 328 if (error) { 329 fprintf(stderr, "Error listing processes.\n"); 330 exit(EXIT_FAILURE); 331 } 332 333 for (i = 0; i < num_procs; i++) { 334 error = pm_process_create(ker, pids[i], &proc); 335 if (error) { 336 fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]); 337 continue; 338 } 339 340 pi = get_process(pids[i]); 341 342 error = pm_process_maps(proc, &maps, &num_maps); 343 if (error) { 344 fprintf(stderr, "Error listing maps for process %d.\n", proc->pid); 345 exit(EXIT_FAILURE); 346 } 347 348 for (j = 0; j < num_maps; j++) { 349 if (prefix && (strncmp(pm_map_name(maps[j]), prefix, prefix_len))) 350 continue; 351 352 if (perm && (pm_map_flags(maps[j]) & PM_MAP_PERMISSIONS) != perm) 353 continue; 354 355 li = get_library(pm_map_name(maps[j]), all); 356 if (!li) 357 continue; 358 359 mi = get_mapping(li, pi); 360 361 error = pm_map_usage_flags(maps[j], &map_usage, flags_mask, 362 required_flags); 363 if (error) { 364 fprintf(stderr, "Error getting map memory usage of " 365 "map %s in process %d.\n", 366 pm_map_name(maps[j]), proc->pid); 367 exit(EXIT_FAILURE); 368 } 369 370 if (map_usage.swap) { 371 has_swap = true; 372 } 373 374 pm_memusage_add(&mi->usage, &map_usage); 375 pm_memusage_add(&li->total_usage, &map_usage); 376 } 377 } 378 379 printf(" %6s %6s %6s %6s %6s ", "RSStot", "VSS", "RSS", "PSS", "USS"); 380 381 if (has_swap) { 382 printf(" %6s ", "Swap"); 383 } 384 385 printf("Name/PID\n"); 386 fflush(stdout); 387 388 qsort(libraries, libraries_count, sizeof(libraries[0]), &licmp); 389 390 for (i = 0; i < libraries_count; i++) { 391 li = libraries[i]; 392 393 printf("%6zdK %6s %6s %6s %6s ", li->total_usage.pss / 1024, "", "", "", ""); 394 if (has_swap) { 395 printf(" %6s ", ""); 396 } 397 printf("%s\n", li->name); 398 fflush(stdout); 399 400 qsort(li->mappings, li->mappings_count, sizeof(li->mappings[0]), compfn); 401 402 for (j = 0; j < li->mappings_count; j++) { 403 mi = li->mappings[j]; 404 pi = mi->proc; 405 printf( " %6s %6zdK %6zdK %6zdK %6zdK ", "", 406 mi->usage.vss / 1024, 407 mi->usage.rss / 1024, 408 mi->usage.pss / 1024, 409 mi->usage.uss / 1024); 410 if (has_swap) { 411 printf("%6zdK ", mi->usage.swap / 1024); 412 } 413 printf(" %s [%d]\n", 414 pi->cmdline, 415 pi->pid); 416 } 417 printf("\n"); 418 fflush(stdout); 419 } 420 421 return 0; 422 } 423 424 static void usage(char *myname) { 425 fprintf(stderr, "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -s | -h ]\n" 426 "\n" 427 "Sort options:\n" 428 " -v Sort processes by VSS.\n" 429 " -r Sort processes by RSS.\n" 430 " -p Sort processes by PSS.\n" 431 " -u Sort processes by USS.\n" 432 " -s Sort processes by swap.\n" 433 " (Default sort order is PSS.)\n" 434 " -a Show all mappings, including stack, heap and anon.\n" 435 " -P /path Limit libraries displayed to those in path.\n" 436 " -R Reverse sort order (default is descending).\n" 437 " -m [r][w][x] Only list pages that exactly match permissions\n" 438 " -c Only show cached (storage backed) pages\n" 439 " -C Only show non-cached (ram/swap backed) pages\n" 440 " -k Only show pages collapsed by KSM\n" 441 " -h Display this help screen.\n", 442 myname); 443 } 444 445 static int getprocname(pid_t pid, char *buf, size_t len) { 446 char filename[20]; 447 FILE *f; 448 449 sprintf(filename, "/proc/%d/cmdline", pid); 450 f = fopen(filename, "r"); 451 if (!f) { *buf = '\0'; return 1; } 452 if (!fgets(buf, len, f)) { *buf = '\0'; return 2; } 453 fclose(f); 454 return 0; 455 } 456 457 static int numcmp(long long a, long long b) { 458 if (a < b) return -1; 459 if (a > b) return 1; 460 return 0; 461 } 462 463 static int licmp(const void *a, const void *b) { 464 return order * numcmp( 465 (*((struct library_info**)a))->total_usage.pss, 466 (*((struct library_info**)b))->total_usage.pss 467 ); 468 } 469 470 #define create_sort(field, compfn) \ 471 static int sort_by_ ## field (const void *a, const void *b) { \ 472 return order * compfn( \ 473 (*((struct mapping_info**)a))->usage.field, \ 474 (*((struct mapping_info**)b))->usage.field \ 475 ); \ 476 } 477 478 create_sort(vss, numcmp) 479 create_sort(rss, numcmp) 480 create_sort(pss, numcmp) 481 create_sort(uss, numcmp) 482 create_sort(swap, numcmp) 483