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 <stdlib.h> 21#include <string.h> 22#include <sys/types.h> 23#include <unistd.h> 24 25#include <pagemap/pagemap.h> 26 27#define MAX_CMDLINE 256 28 29struct process_info { 30 pid_t pid; 31 char cmdline[MAX_CMDLINE]; 32}; 33 34struct mapping_info { 35 struct process_info *proc; 36 pm_memusage_t usage; 37}; 38 39struct library_info { 40 struct library_info *next; 41 char *name; 42 struct mapping_info **mappings; 43 int mappings_count; 44 int mappings_size; 45 pm_memusage_t total_usage; 46}; 47 48static void usage(char *myname); 49static int getprocname(pid_t pid, char *buf, size_t len); 50static int numcmp(long long a, long long b); 51static int licmp(const void *a, const void *b); 52 53char *library_name_blacklist[] = { "[heap]", "[stack]", "", NULL }; 54 55#define declare_sort(field) \ 56 static int sort_by_ ## field (const void *a, const void *b) 57 58declare_sort(vss); 59declare_sort(rss); 60declare_sort(pss); 61declare_sort(uss); 62 63#define INIT_LIBRARIES 16 64#define INIT_MAPPINGS 4 65 66static int order; 67 68struct library_info **libraries; 69int libraries_count; 70int libraries_size; 71 72struct library_info *get_library(char *name) { 73 int i; 74 struct library_info *library; 75 76 for (i = 0; library_name_blacklist[i]; i++) 77 if (!strcmp(name, library_name_blacklist[i])) 78 return NULL; 79 80 for (i = 0; i < libraries_count; i++) { 81 if (!strcmp(libraries[i]->name, name)) 82 return libraries[i]; 83 } 84 85 if (libraries_count >= libraries_size) { 86 libraries = realloc(libraries, 2 * libraries_size * sizeof(struct library_info *)); 87 if (!libraries) { 88 fprintf(stderr, "Couldn't resize libraries array: %s\n", strerror(errno)); 89 exit(EXIT_FAILURE); 90 } 91 libraries_size = 2 * libraries_size; 92 } 93 94 library = calloc(1, sizeof(*library)); 95 if (!library) { 96 fprintf(stderr, "Couldn't allocate space for library struct: %s\n", strerror(errno)); 97 exit(EXIT_FAILURE); 98 } 99 library->name = malloc(strlen(name) + 1); 100 if (!library->name) { 101 fprintf(stderr, "Couldn't allocate space for library name: %s\n", strerror(errno)); 102 exit(EXIT_FAILURE); 103 } 104 strcpy(library->name, name); 105 library->mappings = malloc(INIT_MAPPINGS * sizeof(struct mapping_info *)); 106 if (!library->mappings) { 107 fprintf(stderr, "Couldn't allocate space for library mappings array: %s\n", strerror(errno)); 108 exit(EXIT_FAILURE); 109 } 110 library->mappings_count = 0; library->mappings_size = INIT_MAPPINGS; 111 pm_memusage_zero(&library->total_usage); 112 113 libraries[libraries_count++] = library; 114 115 return library; 116} 117 118struct mapping_info *get_mapping(struct library_info *library, struct process_info *proc) { 119 struct mapping_info *mapping; 120 int i; 121 122 for (i = 0; i < library->mappings_count; i++) { 123 if (library->mappings[i]->proc == proc) 124 return library->mappings[i]; 125 } 126 127 if (library->mappings_count >= library->mappings_size) { 128 library->mappings = realloc(library->mappings, 129 2 * library->mappings_size * sizeof(struct mapping*)); 130 if (!library->mappings) { 131 fprintf(stderr, "Couldn't resize mappings array: %s\n", strerror(errno)); 132 exit(EXIT_FAILURE); 133 } 134 library->mappings_size = 2 * library->mappings_size; 135 } 136 137 mapping = calloc(1, sizeof(*mapping)); 138 if (!mapping) { 139 fprintf(stderr, "Couldn't allocate space for mapping struct: %s\n", strerror(errno)); 140 exit(EXIT_FAILURE); 141 } 142 mapping->proc = proc; 143 pm_memusage_zero(&mapping->usage); 144 145 library->mappings[library->mappings_count++] = mapping; 146 147 return mapping; 148} 149 150struct process_info *get_process(pid_t pid) { 151 struct process_info *process; 152 153 process = calloc(1, sizeof(*process)); 154 if (!process) { 155 fprintf(stderr, "Couldn't allocate space for process struct: %s\n", strerror(errno)); 156 exit(EXIT_FAILURE); 157 } 158 159 process->pid = pid; 160 getprocname(pid, process->cmdline, sizeof(process->cmdline)); 161 162 return process; 163} 164 165int main(int argc, char *argv[]) { 166 char cmdline[256]; 167 char *prefix; 168 size_t prefix_len; 169 int (*compfn)(const void *a, const void *b); 170 171 pm_kernel_t *ker; 172 pm_process_t *proc; 173 174 pid_t *pids; 175 size_t num_procs; 176 177 pm_map_t **maps; 178 size_t num_maps; 179 pm_memusage_t map_usage; 180 181 struct library_info *li, **lis; 182 struct mapping_info *mi, **mis; 183 struct process_info *pi; 184 185 int i, j, error; 186 187 signal(SIGPIPE, SIG_IGN); 188 compfn = &sort_by_pss; 189 order = -1; 190 prefix = NULL; 191 prefix_len = 0; 192 193 for (i = 1; i < argc; i++) { 194 if (!strcmp(argv[i], "-P")) { 195 if (i + 1 >= argc) { 196 fprintf(stderr, "Option -P requires an argument.\n"); 197 usage(argv[0]); 198 exit(EXIT_FAILURE); 199 } 200 prefix = argv[++i]; 201 prefix_len = strlen(prefix); 202 continue; 203 } 204 if (!strcmp(argv[i], "-v")) { compfn = &sort_by_vss; continue; } 205 if (!strcmp(argv[i], "-r")) { compfn = &sort_by_rss; continue; } 206 if (!strcmp(argv[i], "-p")) { compfn = &sort_by_pss; continue; } 207 if (!strcmp(argv[i], "-u")) { compfn = &sort_by_uss; continue; } 208 if (!strcmp(argv[i], "-R")) { order *= -1; continue; } 209 if (!strcmp(argv[i], "-h")) { usage(argv[0]); exit(0); } 210 fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]); 211 usage(argv[0]); 212 exit(EXIT_FAILURE); 213 } 214 215 libraries = malloc(INIT_LIBRARIES * sizeof(struct library_info *)); 216 libraries_count = 0; libraries_size = INIT_LIBRARIES; 217 218 error = pm_kernel_create(&ker); 219 if (error) { 220 fprintf(stderr, "Error initializing kernel interface -- " 221 "does this kernel have pagemap?\n"); 222 exit(EXIT_FAILURE); 223 } 224 225 error = pm_kernel_pids(ker, &pids, &num_procs); 226 if (error) { 227 fprintf(stderr, "Error listing processes.\n"); 228 exit(EXIT_FAILURE); 229 } 230 231 for (i = 0; i < num_procs; i++) { 232 error = pm_process_create(ker, pids[i], &proc); 233 if (error) { 234 fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]); 235 continue; 236 } 237 238 pi = get_process(pids[i]); 239 240 error = pm_process_maps(proc, &maps, &num_maps); 241 if (error) { 242 fprintf(stderr, "Error listing maps for process %d.\n", proc->pid); 243 exit(EXIT_FAILURE); 244 } 245 246 for (j = 0; j < num_maps; j++) { 247 if (prefix && (strncmp(pm_map_name(maps[j]), prefix, prefix_len))) 248 continue; 249 250 li = get_library(pm_map_name(maps[j])); 251 if (!li) 252 continue; 253 254 mi = get_mapping(li, pi); 255 256 error = pm_map_usage(maps[j], &map_usage); 257 if (error) { 258 fprintf(stderr, "Error getting map memory usage of " 259 "map %s in process %d.\n", 260 pm_map_name(maps[j]), proc->pid); 261 exit(EXIT_FAILURE); 262 } 263 pm_memusage_add(&mi->usage, &map_usage); 264 pm_memusage_add(&li->total_usage, &map_usage); 265 } 266 } 267 268 printf( " %6s %6s %6s %6s %6s %s\n", "RSStot", "VSS", "RSS", "PSS", "USS", "Name/PID"); 269 fflush(stdout); 270 271 qsort(libraries, libraries_count, sizeof(libraries[0]), &licmp); 272 273 for (i = 0; i < libraries_count; i++) { 274 li = libraries[i]; 275 276 printf("%6dK %6s %6s %6s %6s %s\n", li->total_usage.pss / 1024, "", "", "", "", li->name); 277 fflush(stdout); 278 279 qsort(li->mappings, li->mappings_count, sizeof(li->mappings[0]), compfn); 280 281 for (j = 0; j < li->mappings_count; j++) { 282 mi = li->mappings[j]; 283 pi = mi->proc; 284 printf( " %6s %6dK %6dK %6dK %6dK %s [%d]\n", "", 285 mi->usage.vss / 1024, 286 mi->usage.rss / 1024, 287 mi->usage.pss / 1024, 288 mi->usage.uss / 1024, 289 pi->cmdline, 290 pi->pid); 291 } 292 printf("\n"); 293 fflush(stdout); 294 } 295 296 return 0; 297} 298 299static void usage(char *myname) { 300 fprintf(stderr, "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -h ]\n" 301 "\n" 302 "Sort options:\n" 303 " -v Sort processes by VSS.\n" 304 " -r Sort processes by RSS.\n" 305 " -p Sort processes by PSS.\n" 306 " -u Sort processes by USS.\n" 307 " (Default sort order is PSS.)\n" 308 " -P /path Limit libraries displayed to those in path.\n" 309 " -R Reverse sort order (default is descending).\n" 310 " -h Display this help screen.\n", 311 myname); 312} 313 314static int getprocname(pid_t pid, char *buf, size_t len) { 315 char filename[20]; 316 FILE *f; 317 318 sprintf(filename, "/proc/%d/cmdline", pid); 319 f = fopen(filename, "r"); 320 if (!f) { *buf = '\0'; return 1; } 321 if (!fgets(buf, len, f)) { *buf = '\0'; return 2; } 322 fclose(f); 323 return 0; 324} 325 326static int numcmp(long long a, long long b) { 327 if (a < b) return -1; 328 if (a > b) return 1; 329 return 0; 330} 331 332static int licmp(const void *a, const void *b) { 333 return order * numcmp( 334 (*((struct library_info**)a))->total_usage.pss, 335 (*((struct library_info**)b))->total_usage.pss 336 ); 337} 338 339#define create_sort(field, compfn) \ 340 static int sort_by_ ## field (const void *a, const void *b) { \ 341 return order * compfn( \ 342 (*((struct mapping_info**)a))->usage.field, \ 343 (*((struct mapping_info**)b))->usage.field \ 344 ); \ 345 } 346 347create_sort(vss, numcmp) 348create_sort(rss, numcmp) 349create_sort(pss, numcmp) 350create_sort(uss, numcmp) 351