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