procrank.c revision b56034796a7cadee89c4cd5e3c0f0730193231de
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 24#include <pagemap/pagemap.h> 25 26struct proc_info { 27 pid_t pid; 28 pm_memusage_t usage; 29 unsigned long wss; 30}; 31 32static void usage(char *myname); 33static int getprocname(pid_t pid, char *buf, int len); 34static int numcmp(long long a, long long b); 35 36#define declare_sort(field) \ 37 static int sort_by_ ## field (const void *a, const void *b) 38 39declare_sort(vss); 40declare_sort(rss); 41declare_sort(pss); 42declare_sort(uss); 43 44int (*compfn)(const void *a, const void *b); 45static int order; 46 47int main(int argc, char *argv[]) { 48 pm_kernel_t *ker; 49 pm_process_t *proc; 50 pid_t *pids; 51 struct proc_info **procs; 52 size_t num_procs; 53 char cmdline[256]; // this must be within the range of int 54 int error; 55 56 #define WS_OFF 0 57 #define WS_ONLY 1 58 #define WS_RESET 2 59 int ws; 60 61 int arg; 62 size_t i, j; 63 64 compfn = &sort_by_pss; 65 order = -1; 66 ws = WS_OFF; 67 68 for (arg = 1; arg < argc; arg++) { 69 if (!strcmp(argv[arg], "-v")) { compfn = &sort_by_vss; continue; } 70 if (!strcmp(argv[arg], "-r")) { compfn = &sort_by_rss; continue; } 71 if (!strcmp(argv[arg], "-p")) { compfn = &sort_by_pss; continue; } 72 if (!strcmp(argv[arg], "-u")) { compfn = &sort_by_uss; continue; } 73 if (!strcmp(argv[arg], "-w")) { ws = WS_ONLY; continue; } 74 if (!strcmp(argv[arg], "-W")) { ws = WS_RESET; continue; } 75 if (!strcmp(argv[arg], "-R")) { order *= -1; continue; } 76 if (!strcmp(argv[arg], "-h")) { usage(argv[0]); exit(0); } 77 fprintf(stderr, "Invalid argument \"%s\".\n", argv[arg]); 78 usage(argv[0]); 79 exit(EXIT_FAILURE); 80 } 81 82 error = pm_kernel_create(&ker); 83 if (error) { 84 fprintf(stderr, "Error creating kernel interface -- " 85 "does this kernel have pagemap?\n"); 86 exit(EXIT_FAILURE); 87 } 88 89 error = pm_kernel_pids(ker, &pids, &num_procs); 90 if (error) { 91 fprintf(stderr, "Error listing processes.\n"); 92 exit(EXIT_FAILURE); 93 } 94 95 procs = calloc(num_procs, sizeof(struct proc_info*)); 96 if (procs == NULL) { 97 fprintf(stderr, "calloc: %s", strerror(errno)); 98 exit(EXIT_FAILURE); 99 } 100 101 for (i = 0; i < num_procs; i++) { 102 procs[i] = malloc(sizeof(struct proc_info)); 103 if (procs[i] == NULL) { 104 fprintf(stderr, "malloc: %s\n", strerror(errno)); 105 exit(EXIT_FAILURE); 106 } 107 procs[i]->pid = pids[i]; 108 error = pm_process_create(ker, pids[i], &proc); 109 if (!error) { 110 switch (ws) { 111 case WS_OFF: 112 pm_process_usage(proc, &procs[i]->usage); 113 break; 114 case WS_ONLY: 115 pm_process_workingset(proc, &procs[i]->usage, 0); 116 break; 117 case WS_RESET: 118 pm_process_workingset(proc, NULL, 1); 119 break; 120 } 121 pm_process_destroy(proc); 122 } else { 123 fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]); 124 pm_memusage_zero(&procs[i]->usage); 125 } 126 } 127 128 free(pids); 129 130 if (ws == WS_RESET) exit(0); 131 132 j = 0; 133 for (i = 0; i < num_procs; i++) { 134 if (procs[i]->usage.vss) { 135 procs[j++] = procs[i]; 136 } else { 137 free(procs[i]); 138 } 139 } 140 num_procs = j; 141 142 qsort(procs, num_procs, sizeof(procs[0]), compfn); 143 144 if (ws) 145 printf("%5s %7s %7s %7s %s\n", "PID", "WRss", "WPss", "WUss", "cmdline"); 146 else 147 printf("%5s %7s %7s %7s %7s %s\n", "PID", "Vss", "Rss", "Pss", "Uss", "cmdline"); 148 149 for (i = 0; i < num_procs; i++) { 150 if (getprocname(procs[i]->pid, cmdline, (int)sizeof(cmdline)) < 0) { 151 /* 152 * Something is probably seriously wrong if writing to the stack 153 * failed. 154 */ 155 free(procs[i]); 156 continue; 157 } 158 159 if (ws) 160 printf("%5d %6dK %6dK %6dK %s\n", 161 procs[i]->pid, 162 procs[i]->usage.rss / 1024, 163 procs[i]->usage.pss / 1024, 164 procs[i]->usage.uss / 1024, 165 cmdline 166 ); 167 else 168 printf("%5d %6dK %6dK %6dK %6dK %s\n", 169 procs[i]->pid, 170 procs[i]->usage.vss / 1024, 171 procs[i]->usage.rss / 1024, 172 procs[i]->usage.pss / 1024, 173 procs[i]->usage.uss / 1024, 174 cmdline 175 ); 176 177 free(procs[i]); 178 } 179 180 free(procs); 181 return 0; 182} 183 184static void usage(char *myname) { 185 fprintf(stderr, "Usage: %s [ -W ] [ -v | -r | -p | -u | -h ]\n" 186 " -v Sort by VSS.\n" 187 " -r Sort by RSS.\n" 188 " -p Sort by PSS.\n" 189 " -u Sort by USS.\n" 190 " (Default sort order is PSS.)\n" 191 " -R Reverse sort order (default is descending).\n" 192 " -w Display statistics for working set only.\n" 193 " -W Reset working set of all processes.\n" 194 " -h Display this help screen.\n", 195 myname); 196} 197 198/* 199 * Get the process name for a given PID. Inserts the process name into buffer 200 * buf of length len. The size of the buffer must be greater than zero to get 201 * any useful output. 202 * 203 * Note that fgets(3) only declares length as an int, so our buffer size is 204 * also declared as an int. 205 * 206 * Returns 0 on success, a positive value on partial success, and -1 on 207 * failure. Other interesting values: 208 * 1 on failure to create string to examine proc cmdline entry 209 * 2 on failure to open proc cmdline entry 210 * 3 on failure to read proc cmdline entry 211 */ 212static int getprocname(pid_t pid, char *buf, int len) { 213 char *filename; 214 FILE *f; 215 int rc = 0; 216 static const char* unknown_cmdline = "<unknown>"; 217 218 if (len <= 0) { 219 return -1; 220 } 221 222 if (asprintf(&filename, "/proc/%zd/cmdline", pid) < 0) { 223 rc = 1; 224 goto exit; 225 } 226 227 f = fopen(filename, "r"); 228 if (f == NULL) { 229 rc = 2; 230 goto releasefilename; 231 } 232 233 if (fgets(buf, len, f) == NULL) { 234 rc = 3; 235 goto closefile; 236 } 237 238closefile: 239 (void) fclose(f); 240releasefilename: 241 free(filename); 242exit: 243 if (rc != 0) { 244 /* 245 * The process went away before we could read its process name. Try 246 * to give the user "<unknown>" here, but otherwise they get to look 247 * at a blank. 248 */ 249 if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) { 250 rc = 4; 251 } 252 } 253 254 return rc; 255} 256 257static int numcmp(long long a, long long b) { 258 if (a < b) return -1; 259 if (a > b) return 1; 260 return 0; 261} 262 263#define create_sort(field, compfn) \ 264 static int sort_by_ ## field (const void *a, const void *b) { \ 265 return order * compfn( \ 266 (*((struct proc_info**)a))->usage.field, \ 267 (*((struct proc_info**)b))->usage.field \ 268 ); \ 269 } 270 271create_sort(vss, numcmp) 272create_sort(rss, numcmp) 273create_sort(pss, numcmp) 274create_sort(uss, numcmp) 275