blob: 945e94022e50a5530ca5eb730e55c0230c57cd9f [file] [log] [blame]
The Android Open Source Projecte16cb842009-03-03 19:32:58 -08001/*
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>
Colin Cross6f5b13c2013-06-21 12:54:13 -070019#include <stdbool.h>
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080020#include <stdlib.h>
21#include <sys/types.h>
22#include <unistd.h>
Olivier Baillyb5603472010-11-16 19:18:59 -080023#include <string.h>
Dianne Hackborne9eeec82011-07-18 12:41:50 -070024#include <fcntl.h>
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080025
26#include <pagemap/pagemap.h>
27
28struct proc_info {
29 pid_t pid;
30 pm_memusage_t usage;
31 unsigned long wss;
32};
33
34static void usage(char *myname);
Kenny Root16abe7a2010-09-21 10:46:57 -070035static int getprocname(pid_t pid, char *buf, int len);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080036static int numcmp(long long a, long long b);
37
38#define declare_sort(field) \
39 static int sort_by_ ## field (const void *a, const void *b)
40
41declare_sort(vss);
42declare_sort(rss);
43declare_sort(pss);
44declare_sort(uss);
Colin Cross6f5b13c2013-06-21 12:54:13 -070045declare_sort(swap);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -080046
47int (*compfn)(const void *a, const void *b);
48static int order;
49
Dianne Hackborne9eeec82011-07-18 12:41:50 -070050void print_mem_info() {
Dianne Hackborn83b0b0a2011-11-04 20:08:19 -070051 char buffer[1024];
Dianne Hackborne9eeec82011-07-18 12:41:50 -070052 int numFound = 0;
53
54 int fd = open("/proc/meminfo", O_RDONLY);
55
56 if (fd < 0) {
57 printf("Unable to open /proc/meminfo: %s\n", strerror(errno));
58 return;
59 }
60
61 const int len = read(fd, buffer, sizeof(buffer)-1);
62 close(fd);
63
64 if (len < 0) {
65 printf("Empty /proc/meminfo");
66 return;
67 }
68 buffer[len] = 0;
69
70 static const char* const tags[] = {
71 "MemTotal:",
72 "MemFree:",
73 "Buffers:",
74 "Cached:",
Dianne Hackborn83b0b0a2011-11-04 20:08:19 -070075 "Shmem:",
76 "Slab:",
Dianne Hackborne9eeec82011-07-18 12:41:50 -070077 NULL
78 };
79 static const int tagsLen[] = {
80 9,
81 8,
82 8,
83 7,
Dianne Hackborn83b0b0a2011-11-04 20:08:19 -070084 6,
85 5,
Dianne Hackborne9eeec82011-07-18 12:41:50 -070086 0
87 };
Dianne Hackborn83b0b0a2011-11-04 20:08:19 -070088 long mem[] = { 0, 0, 0, 0, 0, 0 };
Dianne Hackborne9eeec82011-07-18 12:41:50 -070089
90 char* p = buffer;
Dianne Hackborn83b0b0a2011-11-04 20:08:19 -070091 while (*p && numFound < 6) {
Dianne Hackborne9eeec82011-07-18 12:41:50 -070092 int i = 0;
93 while (tags[i]) {
94 if (strncmp(p, tags[i], tagsLen[i]) == 0) {
95 p += tagsLen[i];
96 while (*p == ' ') p++;
97 char* num = p;
98 while (*p >= '0' && *p <= '9') p++;
99 if (*p != 0) {
100 *p = 0;
101 p++;
Dianne Hackborne9eeec82011-07-18 12:41:50 -0700102 }
103 mem[i] = atoll(num);
104 numFound++;
105 break;
106 }
107 i++;
108 }
Dianne Hackborn83b0b0a2011-11-04 20:08:19 -0700109 while (*p && *p != '\n') {
110 p++;
111 }
112 if (*p) p++;
Dianne Hackborne9eeec82011-07-18 12:41:50 -0700113 }
114
Dianne Hackborn83b0b0a2011-11-04 20:08:19 -0700115 printf("RAM: %ldK total, %ldK free, %ldK buffers, %ldK cached, %ldK shmem, %ldK slab\n",
116 mem[0], mem[1], mem[2], mem[3], mem[4], mem[5]);
Dianne Hackborne9eeec82011-07-18 12:41:50 -0700117}
118
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800119int main(int argc, char *argv[]) {
120 pm_kernel_t *ker;
121 pm_process_t *proc;
122 pid_t *pids;
Kenny Root16abe7a2010-09-21 10:46:57 -0700123 struct proc_info **procs;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800124 size_t num_procs;
Dianne Hackborne9eeec82011-07-18 12:41:50 -0700125 unsigned long total_pss;
126 unsigned long total_uss;
Colin Cross6f5b13c2013-06-21 12:54:13 -0700127 unsigned long total_swap;
Kenny Root16abe7a2010-09-21 10:46:57 -0700128 char cmdline[256]; // this must be within the range of int
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800129 int error;
Colin Cross6f5b13c2013-06-21 12:54:13 -0700130 bool has_swap = false;
Colin Cross8a807c32013-06-21 17:02:05 -0700131 uint64_t required_flags = 0;
132 uint64_t flags_mask = 0;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800133
134 #define WS_OFF 0
135 #define WS_ONLY 1
136 #define WS_RESET 2
137 int ws;
138
Kenny Root16abe7a2010-09-21 10:46:57 -0700139 int arg;
140 size_t i, j;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800141
JP Abgrall80cb1552012-05-11 14:09:59 -0700142 signal(SIGPIPE, SIG_IGN);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800143 compfn = &sort_by_pss;
144 order = -1;
145 ws = WS_OFF;
146
Kenny Root16abe7a2010-09-21 10:46:57 -0700147 for (arg = 1; arg < argc; arg++) {
148 if (!strcmp(argv[arg], "-v")) { compfn = &sort_by_vss; continue; }
149 if (!strcmp(argv[arg], "-r")) { compfn = &sort_by_rss; continue; }
150 if (!strcmp(argv[arg], "-p")) { compfn = &sort_by_pss; continue; }
151 if (!strcmp(argv[arg], "-u")) { compfn = &sort_by_uss; continue; }
Colin Cross6f5b13c2013-06-21 12:54:13 -0700152 if (!strcmp(argv[arg], "-s")) { compfn = &sort_by_swap; continue; }
Colin Cross8a807c32013-06-21 17:02:05 -0700153 if (!strcmp(argv[arg], "-c")) { required_flags = 0; flags_mask = PM_PAGE_SWAPBACKED; continue; }
154 if (!strcmp(argv[arg], "-C")) { required_flags = flags_mask = PM_PAGE_SWAPBACKED; continue; }
155 if (!strcmp(argv[arg], "-k")) { required_flags = flags_mask = PM_PAGE_KSM; continue; }
Kenny Root16abe7a2010-09-21 10:46:57 -0700156 if (!strcmp(argv[arg], "-w")) { ws = WS_ONLY; continue; }
157 if (!strcmp(argv[arg], "-W")) { ws = WS_RESET; continue; }
158 if (!strcmp(argv[arg], "-R")) { order *= -1; continue; }
159 if (!strcmp(argv[arg], "-h")) { usage(argv[0]); exit(0); }
160 fprintf(stderr, "Invalid argument \"%s\".\n", argv[arg]);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800161 usage(argv[0]);
162 exit(EXIT_FAILURE);
163 }
164
165 error = pm_kernel_create(&ker);
166 if (error) {
167 fprintf(stderr, "Error creating kernel interface -- "
168 "does this kernel have pagemap?\n");
169 exit(EXIT_FAILURE);
170 }
171
172 error = pm_kernel_pids(ker, &pids, &num_procs);
173 if (error) {
174 fprintf(stderr, "Error listing processes.\n");
175 exit(EXIT_FAILURE);
176 }
177
Kenny Root16abe7a2010-09-21 10:46:57 -0700178 procs = calloc(num_procs, sizeof(struct proc_info*));
179 if (procs == NULL) {
180 fprintf(stderr, "calloc: %s", strerror(errno));
181 exit(EXIT_FAILURE);
182 }
183
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800184 for (i = 0; i < num_procs; i++) {
185 procs[i] = malloc(sizeof(struct proc_info));
Kenny Root16abe7a2010-09-21 10:46:57 -0700186 if (procs[i] == NULL) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800187 fprintf(stderr, "malloc: %s\n", strerror(errno));
188 exit(EXIT_FAILURE);
189 }
190 procs[i]->pid = pids[i];
Colin Crosseff78882011-07-12 22:30:14 -0700191 pm_memusage_zero(&procs[i]->usage);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800192 error = pm_process_create(ker, pids[i], &proc);
Colin Crosseff78882011-07-12 22:30:14 -0700193 if (error) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800194 fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]);
Colin Crosseff78882011-07-12 22:30:14 -0700195 continue;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800196 }
Colin Crosseff78882011-07-12 22:30:14 -0700197
198 switch (ws) {
199 case WS_OFF:
Colin Cross8a807c32013-06-21 17:02:05 -0700200 error = pm_process_usage_flags(proc, &procs[i]->usage, flags_mask,
201 required_flags);
Colin Crosseff78882011-07-12 22:30:14 -0700202 break;
203 case WS_ONLY:
204 error = pm_process_workingset(proc, &procs[i]->usage, 0);
205 break;
206 case WS_RESET:
207 error = pm_process_workingset(proc, NULL, 1);
208 break;
209 }
210
211 if (error) {
212 fprintf(stderr, "warning: could not read usage for %d\n", pids[i]);
213 }
214
Colin Cross6f5b13c2013-06-21 12:54:13 -0700215 if (ws != WS_RESET && procs[i]->usage.swap) {
216 has_swap = true;
217 }
218
Colin Crosseff78882011-07-12 22:30:14 -0700219 pm_process_destroy(proc);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800220 }
221
222 free(pids);
223
224 if (ws == WS_RESET) exit(0);
225
226 j = 0;
227 for (i = 0; i < num_procs; i++) {
Kenny Root16abe7a2010-09-21 10:46:57 -0700228 if (procs[i]->usage.vss) {
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800229 procs[j++] = procs[i];
Kenny Root16abe7a2010-09-21 10:46:57 -0700230 } else {
231 free(procs[i]);
232 }
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800233 }
234 num_procs = j;
235
236 qsort(procs, num_procs, sizeof(procs[0]), compfn);
237
Colin Cross6f5b13c2013-06-21 12:54:13 -0700238 printf("%5s ", "PID");
239 if (ws) {
240 printf("%s %7s %7s ", "WRss", "WPss", "WUss");
241 if (has_swap) {
242 printf("%7s ", "WSwap");
243 }
244 } else {
245 printf("%8s %7s %7s %7s ", "Vss", "Rss", "Pss", "Uss");
246 if (has_swap) {
247 printf("%7s ", "Swap");
248 }
249 }
250
251 printf("%s\n", "cmdline");
Kenny Root16abe7a2010-09-21 10:46:57 -0700252
Dianne Hackborne9eeec82011-07-18 12:41:50 -0700253 total_pss = 0;
254 total_uss = 0;
Colin Cross6f5b13c2013-06-21 12:54:13 -0700255 total_swap = 0;
Dianne Hackborne9eeec82011-07-18 12:41:50 -0700256
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800257 for (i = 0; i < num_procs; i++) {
Kenny Root16abe7a2010-09-21 10:46:57 -0700258 if (getprocname(procs[i]->pid, cmdline, (int)sizeof(cmdline)) < 0) {
259 /*
260 * Something is probably seriously wrong if writing to the stack
261 * failed.
262 */
263 free(procs[i]);
264 continue;
265 }
266
Dianne Hackborne9eeec82011-07-18 12:41:50 -0700267 total_pss += procs[i]->usage.pss;
268 total_uss += procs[i]->usage.uss;
Colin Cross6f5b13c2013-06-21 12:54:13 -0700269 total_swap += procs[i]->usage.swap;
Dianne Hackborne9eeec82011-07-18 12:41:50 -0700270
Colin Cross6f5b13c2013-06-21 12:54:13 -0700271 printf("%5d ", procs[i]->pid);
272
273 if (ws) {
Ashok Bhataf3263f2013-01-10 15:52:55 +0000274 printf("%6zuK %6zuK %6zuK ",
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800275 procs[i]->usage.rss / 1024,
276 procs[i]->usage.pss / 1024,
Colin Cross6f5b13c2013-06-21 12:54:13 -0700277 procs[i]->usage.uss / 1024
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800278 );
Colin Cross6f5b13c2013-06-21 12:54:13 -0700279 } else {
Ashok Bhataf3263f2013-01-10 15:52:55 +0000280 printf("%7zuK %6zuK %6zuK %6zuK ",
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800281 procs[i]->usage.vss / 1024,
282 procs[i]->usage.rss / 1024,
283 procs[i]->usage.pss / 1024,
Colin Cross6f5b13c2013-06-21 12:54:13 -0700284 procs[i]->usage.uss / 1024
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800285 );
Colin Cross6f5b13c2013-06-21 12:54:13 -0700286 }
287
288 if (has_swap) {
Ashok Bhataf3263f2013-01-10 15:52:55 +0000289 printf("%6zuK ", procs[i]->usage.swap / 1024);
Colin Cross6f5b13c2013-06-21 12:54:13 -0700290 }
291
292 printf("%s\n", cmdline);
Kenny Root16abe7a2010-09-21 10:46:57 -0700293
294 free(procs[i]);
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800295 }
296
Kenny Root16abe7a2010-09-21 10:46:57 -0700297 free(procs);
Dianne Hackborne9eeec82011-07-18 12:41:50 -0700298
Colin Cross6f5b13c2013-06-21 12:54:13 -0700299 /* Print the separator line */
300 printf("%5s ", "");
301
Dianne Hackborne9eeec82011-07-18 12:41:50 -0700302 if (ws) {
Colin Cross6f5b13c2013-06-21 12:54:13 -0700303 printf("%7s %7s %7s ", "", "------", "------");
Dianne Hackborne9eeec82011-07-18 12:41:50 -0700304 } else {
Colin Cross6f5b13c2013-06-21 12:54:13 -0700305 printf("%8s %7s %7s %7s ", "", "", "------", "------");
Dianne Hackborne9eeec82011-07-18 12:41:50 -0700306 }
307
Colin Cross6f5b13c2013-06-21 12:54:13 -0700308 if (has_swap) {
309 printf("%7s ", "------");
310 }
311
312 printf("%s\n", "------");
313
314 /* Print the total line */
315 printf("%5s ", "");
316 if (ws) {
317 printf("%7s %6ldK %6ldK ",
318 "", total_pss / 1024, total_uss / 1024);
319 } else {
320 printf("%8s %7s %6ldK %6ldK ",
321 "", "", total_pss / 1024, total_uss / 1024);
322 }
323
324 if (has_swap) {
325 printf("%6ldK ", total_swap);
326 }
327
328 printf("TOTAL\n");
329
Dianne Hackborne9eeec82011-07-18 12:41:50 -0700330 printf("\n");
331 print_mem_info();
332
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800333 return 0;
334}
335
336static void usage(char *myname) {
Colin Cross6f5b13c2013-06-21 12:54:13 -0700337 fprintf(stderr, "Usage: %s [ -W ] [ -v | -r | -p | -u | -s | -h ]\n"
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800338 " -v Sort by VSS.\n"
339 " -r Sort by RSS.\n"
340 " -p Sort by PSS.\n"
341 " -u Sort by USS.\n"
Colin Cross6f5b13c2013-06-21 12:54:13 -0700342 " -s Sort by swap.\n"
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800343 " (Default sort order is PSS.)\n"
344 " -R Reverse sort order (default is descending).\n"
Colin Cross8a807c32013-06-21 17:02:05 -0700345 " -c Only show cached (storage backed) pages\n"
346 " -C Only show non-cached (ram/swap backed) pages\n"
347 " -k Only show pages collapsed by KSM\n"
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800348 " -w Display statistics for working set only.\n"
349 " -W Reset working set of all processes.\n"
350 " -h Display this help screen.\n",
351 myname);
352}
353
Kenny Root16abe7a2010-09-21 10:46:57 -0700354/*
355 * Get the process name for a given PID. Inserts the process name into buffer
356 * buf of length len. The size of the buffer must be greater than zero to get
357 * any useful output.
358 *
359 * Note that fgets(3) only declares length as an int, so our buffer size is
360 * also declared as an int.
361 *
362 * Returns 0 on success, a positive value on partial success, and -1 on
363 * failure. Other interesting values:
364 * 1 on failure to create string to examine proc cmdline entry
365 * 2 on failure to open proc cmdline entry
366 * 3 on failure to read proc cmdline entry
367 */
368static int getprocname(pid_t pid, char *buf, int len) {
369 char *filename;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800370 FILE *f;
Kenny Root16abe7a2010-09-21 10:46:57 -0700371 int rc = 0;
372 static const char* unknown_cmdline = "<unknown>";
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800373
Kenny Root16abe7a2010-09-21 10:46:57 -0700374 if (len <= 0) {
375 return -1;
376 }
377
Ashok Bhataf3263f2013-01-10 15:52:55 +0000378 if (asprintf(&filename, "/proc/%d/cmdline", pid) < 0) {
Kenny Root16abe7a2010-09-21 10:46:57 -0700379 rc = 1;
380 goto exit;
381 }
382
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800383 f = fopen(filename, "r");
Kenny Root16abe7a2010-09-21 10:46:57 -0700384 if (f == NULL) {
385 rc = 2;
386 goto releasefilename;
387 }
388
389 if (fgets(buf, len, f) == NULL) {
390 rc = 3;
391 goto closefile;
392 }
393
394closefile:
395 (void) fclose(f);
396releasefilename:
397 free(filename);
398exit:
399 if (rc != 0) {
400 /*
401 * The process went away before we could read its process name. Try
402 * to give the user "<unknown>" here, but otherwise they get to look
403 * at a blank.
404 */
405 if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) {
406 rc = 4;
407 }
408 }
409
410 return rc;
The Android Open Source Projecte16cb842009-03-03 19:32:58 -0800411}
412
413static int numcmp(long long a, long long b) {
414 if (a < b) return -1;
415 if (a > b) return 1;
416 return 0;
417}
418
419#define create_sort(field, compfn) \
420 static int sort_by_ ## field (const void *a, const void *b) { \
421 return order * compfn( \
422 (*((struct proc_info**)a))->usage.field, \
423 (*((struct proc_info**)b))->usage.field \
424 ); \
425 }
426
427create_sort(vss, numcmp)
428create_sort(rss, numcmp)
429create_sort(pss, numcmp)
430create_sort(uss, numcmp)
Colin Cross6f5b13c2013-06-21 12:54:13 -0700431create_sort(swap, numcmp)