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