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