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 <dirent.h> 18 #include <errno.h> 19 #include <fcntl.h> 20 #include <inttypes.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 struct proc_info { 30 pid_t pid; 31 pm_memusage_t usage; 32 uint64_t wss; 33 }; 34 35 static void usage(char *myname); 36 static int getprocname(pid_t pid, char *buf, int len); 37 static int numcmp(uint64_t a, uint64_t b); 38 39 #define declare_sort(field) \ 40 static int sort_by_ ## field (const void *a, const void *b) 41 42 declare_sort(vss); 43 declare_sort(rss); 44 declare_sort(pss); 45 declare_sort(uss); 46 declare_sort(swap); 47 48 int (*compfn)(const void *a, const void *b); 49 static int order; 50 51 void print_mem_info() { 52 char buffer[1024]; 53 int numFound = 0; 54 55 int fd = open("/proc/meminfo", O_RDONLY); 56 57 if (fd < 0) { 58 printf("Unable to open /proc/meminfo: %s\n", strerror(errno)); 59 return; 60 } 61 62 const int len = read(fd, buffer, sizeof(buffer)-1); 63 close(fd); 64 65 if (len < 0) { 66 printf("Empty /proc/meminfo"); 67 return; 68 } 69 buffer[len] = 0; 70 71 static const char* const tags[] = { 72 "MemTotal:", 73 "MemFree:", 74 "Buffers:", 75 "Cached:", 76 "Shmem:", 77 "Slab:", 78 NULL 79 }; 80 static const int tagsLen[] = { 81 9, 82 8, 83 8, 84 7, 85 6, 86 5, 87 0 88 }; 89 uint64_t mem[] = { 0, 0, 0, 0, 0, 0 }; 90 91 char* p = buffer; 92 while (*p && numFound < 6) { 93 int i = 0; 94 while (tags[i]) { 95 if (strncmp(p, tags[i], tagsLen[i]) == 0) { 96 p += tagsLen[i]; 97 while (*p == ' ') p++; 98 char* num = p; 99 while (*p >= '0' && *p <= '9') p++; 100 if (*p != 0) { 101 *p = 0; 102 p++; 103 } 104 mem[i] = atoll(num); 105 numFound++; 106 break; 107 } 108 i++; 109 } 110 while (*p && *p != '\n') { 111 p++; 112 } 113 if (*p) p++; 114 } 115 116 printf("RAM: %" PRIu64 "K total, %" PRIu64 "K free, %" PRIu64 "K buffers, " 117 "%" PRIu64 "K cached, %" PRIu64 "K shmem, %" PRIu64 "K slab\n", 118 mem[0], mem[1], mem[2], mem[3], mem[4], mem[5]); 119 } 120 121 int main(int argc, char *argv[]) { 122 pm_kernel_t *ker; 123 pm_process_t *proc; 124 pid_t *pids; 125 struct proc_info **procs; 126 size_t num_procs; 127 uint64_t total_pss; 128 uint64_t total_uss; 129 uint64_t total_swap; 130 char cmdline[256]; // this must be within the range of int 131 int error; 132 bool has_swap = false; 133 uint64_t required_flags = 0; 134 uint64_t flags_mask = 0; 135 136 #define WS_OFF 0 137 #define WS_ONLY 1 138 #define WS_RESET 2 139 int ws; 140 141 int arg; 142 size_t i, j; 143 144 signal(SIGPIPE, SIG_IGN); 145 compfn = &sort_by_pss; 146 order = -1; 147 ws = WS_OFF; 148 149 for (arg = 1; arg < argc; arg++) { 150 if (!strcmp(argv[arg], "-v")) { compfn = &sort_by_vss; continue; } 151 if (!strcmp(argv[arg], "-r")) { compfn = &sort_by_rss; continue; } 152 if (!strcmp(argv[arg], "-p")) { compfn = &sort_by_pss; continue; } 153 if (!strcmp(argv[arg], "-u")) { compfn = &sort_by_uss; continue; } 154 if (!strcmp(argv[arg], "-s")) { compfn = &sort_by_swap; continue; } 155 if (!strcmp(argv[arg], "-c")) { required_flags = 0; flags_mask = PM_PAGE_SWAPBACKED; continue; } 156 if (!strcmp(argv[arg], "-C")) { required_flags = flags_mask = PM_PAGE_SWAPBACKED; continue; } 157 if (!strcmp(argv[arg], "-k")) { required_flags = flags_mask = PM_PAGE_KSM; continue; } 158 if (!strcmp(argv[arg], "-w")) { ws = WS_ONLY; continue; } 159 if (!strcmp(argv[arg], "-W")) { ws = WS_RESET; continue; } 160 if (!strcmp(argv[arg], "-R")) { order *= -1; continue; } 161 if (!strcmp(argv[arg], "-h")) { usage(argv[0]); exit(0); } 162 fprintf(stderr, "Invalid argument \"%s\".\n", argv[arg]); 163 usage(argv[0]); 164 exit(EXIT_FAILURE); 165 } 166 167 error = pm_kernel_create(&ker); 168 if (error) { 169 fprintf(stderr, "Error creating kernel interface -- " 170 "does this kernel have pagemap?\n"); 171 exit(EXIT_FAILURE); 172 } 173 174 error = pm_kernel_pids(ker, &pids, &num_procs); 175 if (error) { 176 fprintf(stderr, "Error listing processes.\n"); 177 exit(EXIT_FAILURE); 178 } 179 180 procs = calloc(num_procs, sizeof(struct proc_info*)); 181 if (procs == NULL) { 182 fprintf(stderr, "calloc: %s", strerror(errno)); 183 exit(EXIT_FAILURE); 184 } 185 186 for (i = 0; i < num_procs; i++) { 187 procs[i] = malloc(sizeof(struct proc_info)); 188 if (procs[i] == NULL) { 189 fprintf(stderr, "malloc: %s\n", strerror(errno)); 190 exit(EXIT_FAILURE); 191 } 192 procs[i]->pid = pids[i]; 193 pm_memusage_zero(&procs[i]->usage); 194 error = pm_process_create(ker, pids[i], &proc); 195 if (error) { 196 fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]); 197 continue; 198 } 199 200 switch (ws) { 201 case WS_OFF: 202 error = pm_process_usage_flags(proc, &procs[i]->usage, flags_mask, 203 required_flags); 204 break; 205 case WS_ONLY: 206 error = pm_process_workingset(proc, &procs[i]->usage, 0); 207 break; 208 case WS_RESET: 209 error = pm_process_workingset(proc, NULL, 1); 210 break; 211 } 212 213 if (error) { 214 fprintf(stderr, "warning: could not read usage for %d\n", pids[i]); 215 } 216 217 if (ws != WS_RESET && procs[i]->usage.swap) { 218 has_swap = true; 219 } 220 221 pm_process_destroy(proc); 222 } 223 224 free(pids); 225 226 if (ws == WS_RESET) exit(0); 227 228 j = 0; 229 for (i = 0; i < num_procs; i++) { 230 if (procs[i]->usage.vss) { 231 procs[j++] = procs[i]; 232 } else { 233 free(procs[i]); 234 } 235 } 236 num_procs = j; 237 238 qsort(procs, num_procs, sizeof(procs[0]), compfn); 239 240 printf("%5s ", "PID"); 241 if (ws) { 242 printf("%s %7s %7s ", "WRss", "WPss", "WUss"); 243 if (has_swap) { 244 printf("%7s ", "WSwap"); 245 } 246 } else { 247 printf("%8s %7s %7s %7s ", "Vss", "Rss", "Pss", "Uss"); 248 if (has_swap) { 249 printf("%7s ", "Swap"); 250 } 251 } 252 253 printf("%s\n", "cmdline"); 254 255 total_pss = 0; 256 total_uss = 0; 257 total_swap = 0; 258 259 for (i = 0; i < num_procs; i++) { 260 if (getprocname(procs[i]->pid, cmdline, (int)sizeof(cmdline)) < 0) { 261 /* 262 * Something is probably seriously wrong if writing to the stack 263 * failed. 264 */ 265 free(procs[i]); 266 continue; 267 } 268 269 total_pss += procs[i]->usage.pss; 270 total_uss += procs[i]->usage.uss; 271 total_swap += procs[i]->usage.swap; 272 273 printf("%5d ", procs[i]->pid); 274 275 if (ws) { 276 printf("%6zuK %6zuK %6zuK ", 277 procs[i]->usage.rss / 1024, 278 procs[i]->usage.pss / 1024, 279 procs[i]->usage.uss / 1024 280 ); 281 } else { 282 printf("%7zuK %6zuK %6zuK %6zuK ", 283 procs[i]->usage.vss / 1024, 284 procs[i]->usage.rss / 1024, 285 procs[i]->usage.pss / 1024, 286 procs[i]->usage.uss / 1024 287 ); 288 } 289 290 if (has_swap) { 291 printf("%6zuK ", procs[i]->usage.swap / 1024); 292 } 293 294 printf("%s\n", cmdline); 295 296 free(procs[i]); 297 } 298 299 free(procs); 300 301 /* Print the separator line */ 302 printf("%5s ", ""); 303 304 if (ws) { 305 printf("%7s %7s %7s ", "", "------", "------"); 306 } else { 307 printf("%8s %7s %7s %7s ", "", "", "------", "------"); 308 } 309 310 if (has_swap) { 311 printf("%7s ", "------"); 312 } 313 314 printf("%s\n", "------"); 315 316 /* Print the total line */ 317 printf("%5s ", ""); 318 if (ws) { 319 printf("%7s %6" PRIu64 "K %" PRIu64 "K ", 320 "", total_pss / 1024, total_uss / 1024); 321 } else { 322 printf("%8s %7s %6" PRIu64 "K %6" PRIu64 "K ", 323 "", "", total_pss / 1024, total_uss / 1024); 324 } 325 326 if (has_swap) { 327 printf("%6" PRIu64 "K ", total_swap); 328 } 329 330 printf("TOTAL\n"); 331 332 printf("\n"); 333 print_mem_info(); 334 335 return 0; 336 } 337 338 static void usage(char *myname) { 339 fprintf(stderr, "Usage: %s [ -W ] [ -v | -r | -p | -u | -s | -h ]\n" 340 " -v Sort by VSS.\n" 341 " -r Sort by RSS.\n" 342 " -p Sort by PSS.\n" 343 " -u Sort by USS.\n" 344 " -s Sort by swap.\n" 345 " (Default sort order is PSS.)\n" 346 " -R Reverse sort order (default is descending).\n" 347 " -c Only show cached (storage backed) pages\n" 348 " -C Only show non-cached (ram/swap backed) pages\n" 349 " -k Only show pages collapsed by KSM\n" 350 " -w Display statistics for working set only.\n" 351 " -W Reset working set of all processes.\n" 352 " -h Display this help screen.\n", 353 myname); 354 } 355 356 /* 357 * Get the process name for a given PID. Inserts the process name into buffer 358 * buf of length len. The size of the buffer must be greater than zero to get 359 * any useful output. 360 * 361 * Note that fgets(3) only declares length as an int, so our buffer size is 362 * also declared as an int. 363 * 364 * Returns 0 on success, a positive value on partial success, and -1 on 365 * failure. Other interesting values: 366 * 1 on failure to create string to examine proc cmdline entry 367 * 2 on failure to open proc cmdline entry 368 * 3 on failure to read proc cmdline entry 369 */ 370 static int getprocname(pid_t pid, char *buf, int len) { 371 char *filename; 372 FILE *f; 373 int rc = 0; 374 static const char* unknown_cmdline = "<unknown>"; 375 376 if (len <= 0) { 377 return -1; 378 } 379 380 if (asprintf(&filename, "/proc/%d/cmdline", pid) < 0) { 381 rc = 1; 382 goto exit; 383 } 384 385 f = fopen(filename, "r"); 386 if (f == NULL) { 387 rc = 2; 388 goto releasefilename; 389 } 390 391 if (fgets(buf, len, f) == NULL) { 392 rc = 3; 393 goto closefile; 394 } 395 396 closefile: 397 (void) fclose(f); 398 releasefilename: 399 free(filename); 400 exit: 401 if (rc != 0) { 402 /* 403 * The process went away before we could read its process name. Try 404 * to give the user "<unknown>" here, but otherwise they get to look 405 * at a blank. 406 */ 407 if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) { 408 rc = 4; 409 } 410 } 411 412 return rc; 413 } 414 415 static int numcmp(uint64_t a, uint64_t b) { 416 if (a < b) return -1; 417 if (a > b) return 1; 418 return 0; 419 } 420 421 #define create_sort(field, compfn) \ 422 static int sort_by_ ## field (const void *a, const void *b) { \ 423 return order * compfn( \ 424 (*((struct proc_info**)a))->usage.field, \ 425 (*((struct proc_info**)b))->usage.field \ 426 ); \ 427 } 428 429 create_sort(vss, numcmp) 430 create_sort(rss, numcmp) 431 create_sort(pss, numcmp) 432 create_sort(uss, numcmp) 433 create_sort(swap, numcmp) 434