librank.c revision 7341494707810f709855ea85ce03a8ec3ac8dbaf
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 compfn = &sort_by_pss; 188 order = -1; 189 prefix = NULL; 190 prefix_len = 0; 191 192 for (i = 1; i < argc; i++) { 193 if (!strcmp(argv[i], "-P")) { 194 if (i + 1 >= argc) { 195 fprintf(stderr, "Option -P requires an argument.\n"); 196 usage(argv[0]); 197 exit(EXIT_FAILURE); 198 } 199 prefix = argv[++i]; 200 prefix_len = strlen(prefix); 201 continue; 202 } 203 if (!strcmp(argv[i], "-v")) { compfn = &sort_by_vss; continue; } 204 if (!strcmp(argv[i], "-r")) { compfn = &sort_by_rss; continue; } 205 if (!strcmp(argv[i], "-p")) { compfn = &sort_by_pss; continue; } 206 if (!strcmp(argv[i], "-u")) { compfn = &sort_by_uss; continue; } 207 if (!strcmp(argv[i], "-R")) { order *= -1; continue; } 208 if (!strcmp(argv[i], "-h")) { usage(argv[0]); exit(0); } 209 fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]); 210 usage(argv[0]); 211 exit(EXIT_FAILURE); 212 } 213 214 libraries = malloc(INIT_LIBRARIES * sizeof(struct library_info *)); 215 libraries_count = 0; libraries_size = INIT_LIBRARIES; 216 217 error = pm_kernel_create(&ker); 218 if (error) { 219 fprintf(stderr, "Error initializing kernel interface -- " 220 "does this kernel have pagemap?\n"); 221 exit(EXIT_FAILURE); 222 } 223 224 error = pm_kernel_pids(ker, &pids, &num_procs); 225 if (error) { 226 fprintf(stderr, "Error listing processes.\n"); 227 exit(EXIT_FAILURE); 228 } 229 230 for (i = 0; i < num_procs; i++) { 231 error = pm_process_create(ker, pids[i], &proc); 232 if (error) { 233 fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]); 234 continue; 235 } 236 237 pi = get_process(pids[i]); 238 239 error = pm_process_maps(proc, &maps, &num_maps); 240 if (error) { 241 fprintf(stderr, "Error listing maps for process %d.\n", proc->pid); 242 exit(EXIT_FAILURE); 243 } 244 245 for (j = 0; j < num_maps; j++) { 246 if (prefix && (strncmp(pm_map_name(maps[j]), prefix, prefix_len))) 247 continue; 248 249 li = get_library(pm_map_name(maps[j])); 250 if (!li) 251 continue; 252 253 mi = get_mapping(li, pi); 254 255 error = pm_map_usage(maps[j], &map_usage); 256 if (error) { 257 fprintf(stderr, "Error getting map memory usage of " 258 "map %s in process %d.\n", 259 pm_map_name(maps[j]), proc->pid); 260 exit(EXIT_FAILURE); 261 } 262 pm_memusage_add(&mi->usage, &map_usage); 263 pm_memusage_add(&li->total_usage, &map_usage); 264 } 265 } 266 267 printf( " %6s %6s %6s %6s %6s %s\n", "RSStot", "VSS", "RSS", "PSS", "USS", "Name/PID"); 268 fflush(stdout); 269 270 qsort(libraries, libraries_count, sizeof(libraries[0]), &licmp); 271 272 for (i = 0; i < libraries_count; i++) { 273 li = libraries[i]; 274 275 printf("%6dK %6s %6s %6s %6s %s\n", li->total_usage.pss / 1024, "", "", "", "", li->name); 276 fflush(stdout); 277 278 qsort(li->mappings, li->mappings_count, sizeof(li->mappings[0]), compfn); 279 280 for (j = 0; j < li->mappings_count; j++) { 281 mi = li->mappings[j]; 282 pi = mi->proc; 283 printf( " %6s %6dK %6dK %6dK %6dK %s [%d]\n", "", 284 mi->usage.vss / 1024, 285 mi->usage.rss / 1024, 286 mi->usage.pss / 1024, 287 mi->usage.uss / 1024, 288 pi->cmdline, 289 pi->pid); 290 } 291 printf("\n"); 292 fflush(stdout); 293 } 294 295 return 0; 296} 297 298static void usage(char *myname) { 299 fprintf(stderr, "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -h ]\n" 300 "\n" 301 "Sort options:\n" 302 " -v Sort processes by VSS.\n" 303 " -r Sort processes by RSS.\n" 304 " -p Sort processes by PSS.\n" 305 " -u Sort processes by USS.\n" 306 " (Default sort order is PSS.)\n" 307 " -P /path Limit libraries displayed to those in path.\n" 308 " -R Reverse sort order (default is descending).\n" 309 " -h Display this help screen.\n", 310 myname); 311} 312 313static int getprocname(pid_t pid, char *buf, size_t len) { 314 char filename[20]; 315 FILE *f; 316 317 sprintf(filename, "/proc/%d/cmdline", pid); 318 f = fopen(filename, "r"); 319 if (!f) { *buf = '\0'; return 1; } 320 if (!fgets(buf, len, f)) { *buf = '\0'; return 2; } 321 fclose(f); 322 return 0; 323} 324 325static int numcmp(long long a, long long b) { 326 if (a < b) return -1; 327 if (a > b) return 1; 328 return 0; 329} 330 331static int licmp(const void *a, const void *b) { 332 return order * numcmp( 333 (*((struct library_info**)a))->total_usage.pss, 334 (*((struct library_info**)b))->total_usage.pss 335 ); 336} 337 338#define create_sort(field, compfn) \ 339 static int sort_by_ ## field (const void *a, const void *b) { \ 340 return order * compfn( \ 341 (*((struct mapping_info**)a))->usage.field, \ 342 (*((struct mapping_info**)b))->usage.field \ 343 ); \ 344 } 345 346create_sort(vss, numcmp) 347create_sort(rss, numcmp) 348create_sort(pss, numcmp) 349create_sort(uss, numcmp) 350