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