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