2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 #include <sys/types.h>
26 #include <pagemap/pagemap.h>
34 static void usage(char *myname);
35 static int getprocname(pid_t pid, char *buf, int len);
36 static int numcmp(long long a, long long b);
38 #define declare_sort(field) \
39 static int sort_by_ ## field (const void *a, const void *b)
47 int (*compfn)(const void *a, const void *b);
50 void print_mem_info() {
54 int fd = open("/proc/meminfo", O_RDONLY);
57 printf("Unable to open /proc/meminfo: %s\n", strerror(errno));
61 const int len = read(fd, buffer, sizeof(buffer)-1);
65 printf("Empty /proc/meminfo");
70 static const char* const tags[] = {
79 static const int tagsLen[] = {
88 long mem[] = { 0, 0, 0, 0, 0, 0 };
91 while (*p && numFound < 6) {
94 if (strncmp(p, tags[i], tagsLen[i]) == 0) {
96 while (*p == ' ') p++;
98 while (*p >= '0' && *p <= '9') p++;
109 while (*p && *p != '\n') {
115 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]);
119 int main(int argc, char *argv[]) {
123 struct proc_info **procs;
125 unsigned long total_pss;
126 unsigned long total_uss;
127 unsigned long total_swap;
128 char cmdline[256]; // this must be within the range of int
130 bool has_swap = false;
131 uint64_t required_flags = 0;
132 uint64_t flags_mask = 0;
142 signal(SIGPIPE, SIG_IGN);
143 compfn = &sort_by_pss;
147 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; }
152 if (!strcmp(argv[arg], "-s")) { compfn = &sort_by_swap; continue; }
153 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; }
156 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]);
165 error = pm_kernel_create(&ker);
167 fprintf(stderr, "Error creating kernel interface -- "
168 "does this kernel have pagemap?\n");
172 error = pm_kernel_pids(ker, &pids, &num_procs);
174 fprintf(stderr, "Error listing processes.\n");
178 procs = calloc(num_procs, sizeof(struct proc_info*));
180 fprintf(stderr, "calloc: %s", strerror(errno));
184 for (i = 0; i < num_procs; i++) {
185 procs[i] = malloc(sizeof(struct proc_info));
186 if (procs[i] == NULL) {
187 fprintf(stderr, "malloc: %s\n", strerror(errno));
190 procs[i]->pid = pids[i];
191 pm_memusage_zero(&procs[i]->usage);
192 error = pm_process_create(ker, pids[i], &proc);
194 fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]);
200 error = pm_process_usage_flags(proc, &procs[i]->usage, flags_mask,
204 error = pm_process_workingset(proc, &procs[i]->usage, 0);
207 error = pm_process_workingset(proc, NULL, 1);
212 fprintf(stderr, "warning: could not read usage for %d\n", pids[i]);
215 if (ws != WS_RESET && procs[i]->usage.swap) {
219 pm_process_destroy(proc);
224 if (ws == WS_RESET) exit(0);
227 for (i = 0; i < num_procs; i++) {
228 if (procs[i]->usage.vss) {
229 procs[j++] = procs[i];
236 qsort(procs, num_procs, sizeof(procs[0]), compfn);
238 printf("%5s ", "PID");
240 printf("%s %7s %7s ", "WRss", "WPss", "WUss");
242 printf("%7s ", "WSwap");
245 printf("%8s %7s %7s %7s ", "Vss", "Rss", "Pss", "Uss");
247 printf("%7s ", "Swap");
251 printf("%s\n", "cmdline");
257 for (i = 0; i < num_procs; i++) {
258 if (getprocname(procs[i]->pid, cmdline, (int)sizeof(cmdline)) < 0) {
260 * Something is probably seriously wrong if writing to the stack
267 total_pss += procs[i]->usage.pss;
268 total_uss += procs[i]->usage.uss;
269 total_swap += procs[i]->usage.swap;
271 printf("%5d ", procs[i]->pid);
274 printf("%6dK %6dK %6dK ",
275 procs[i]->usage.rss / 1024,
276 procs[i]->usage.pss / 1024,
277 procs[i]->usage.uss / 1024
280 printf("%7dK %6dK %6dK %6dK ",
281 procs[i]->usage.vss / 1024,
282 procs[i]->usage.rss / 1024,
283 procs[i]->usage.pss / 1024,
284 procs[i]->usage.uss / 1024
289 printf("%6dK ", procs[i]->usage.swap / 1024);
292 printf("%s\n", cmdline);
299 /* Print the separator line */
303 printf("%7s %7s %7s ", "", "------", "------");
305 printf("%8s %7s %7s %7s ", "", "", "------", "------");
309 printf("%7s ", "------");
312 printf("%s\n", "------");
314 /* Print the total line */
317 printf("%7s %6ldK %6ldK ",
318 "", total_pss / 1024, total_uss / 1024);
320 printf("%8s %7s %6ldK %6ldK ",
321 "", "", total_pss / 1024, total_uss / 1024);
325 printf("%6ldK ", total_swap);
336 static void usage(char *myname) {
337 fprintf(stderr, "Usage: %s [ -W ] [ -v | -r | -p | -u | -s | -h ]\n"
342 " -s Sort by swap.\n"
343 " (Default sort order is PSS.)\n"
344 " -R Reverse sort order (default is descending).\n"
345 " -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"
348 " -w Display statistics for working set only.\n"
349 " -W Reset working set of all processes.\n"
350 " -h Display this help screen.\n",
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
359 * Note that fgets(3) only declares length as an int, so our buffer size is
360 * also declared as an int.
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
368 static int getprocname(pid_t pid, char *buf, int len) {
372 static const char* unknown_cmdline = "<unknown>";
378 if (asprintf(&filename, "/proc/%zd/cmdline", pid) < 0) {
383 f = fopen(filename, "r");
386 goto releasefilename;
389 if (fgets(buf, len, f) == NULL) {
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
405 if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) {
413 static int numcmp(long long a, long long b) {
414 if (a < b) return -1;
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 \
427 create_sort(vss, numcmp)
428 create_sort(rss, numcmp)
429 create_sort(pss, numcmp)
430 create_sort(uss, numcmp)
431 create_sort(swap, numcmp)