OSDN Git Service

Merge "Add code and tests for inet_diag bytecode."
[android-x86/system-extras.git] / procrank / procrank.c
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 <fcntl.h>
20 #include <inttypes.h>
21 #include <stdbool.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26
27 #include <pagemap/pagemap.h>
28
29 struct proc_info {
30     pid_t pid;
31     pm_memusage_t usage;
32     uint64_t wss;
33 };
34
35 static void usage(char *myname);
36 static int getprocname(pid_t pid, char *buf, int len);
37 static int numcmp(uint64_t a, uint64_t b);
38
39 #define declare_sort(field) \
40     static int sort_by_ ## field (const void *a, const void *b)
41
42 declare_sort(vss);
43 declare_sort(rss);
44 declare_sort(pss);
45 declare_sort(uss);
46 declare_sort(swap);
47
48 int (*compfn)(const void *a, const void *b);
49 static int order;
50
51 enum {
52     MEMINFO_TOTAL,
53     MEMINFO_FREE,
54     MEMINFO_BUFFERS,
55     MEMINFO_CACHED,
56     MEMINFO_SHMEM,
57     MEMINFO_SLAB,
58     MEMINFO_SWAP_TOTAL,
59     MEMINFO_SWAP_FREE,
60     MEMINFO_ZRAM_TOTAL,
61     MEMINFO_MAPPED,
62     MEMINFO_VMALLOC_USED,
63     MEMINFO_PAGE_TABLES,
64     MEMINFO_KERNEL_STACK,
65     MEMINFO_COUNT
66 };
67
68 void get_mem_info(uint64_t mem[]) {
69     char buffer[1024];
70     unsigned int numFound = 0;
71
72     int fd = open("/proc/meminfo", O_RDONLY);
73
74     if (fd < 0) {
75         printf("Unable to open /proc/meminfo: %s\n", strerror(errno));
76         return;
77     }
78
79     const int len = read(fd, buffer, sizeof(buffer)-1);
80     close(fd);
81
82     if (len < 0) {
83         printf("Empty /proc/meminfo");
84         return;
85     }
86     buffer[len] = 0;
87
88     static const char* const tags[] = {
89             "MemTotal:",
90             "MemFree:",
91             "Buffers:",
92             "Cached:",
93             "Shmem:",
94             "Slab:",
95             "SwapTotal:",
96             "SwapFree:",
97             "ZRam:",            /* not read from meminfo but from /sys/block/zram0 */
98             "Mapped:",
99             "VmallocUsed:",
100             "PageTables:",
101             "KernelStack:",
102             NULL
103     };
104     static const int tagsLen[] = {
105             9,
106             8,
107             8,
108             7,
109             6,
110             5,
111             10,
112             9,
113             5,
114             7,
115             12,
116             11,
117             12,
118             0
119     };
120
121     char* p = buffer;
122     while (*p && (numFound < (sizeof(tagsLen) / sizeof(tagsLen[0])))) {
123         int i = 0;
124         while (tags[i]) {
125             if (strncmp(p, tags[i], tagsLen[i]) == 0) {
126                 p += tagsLen[i];
127                 while (*p == ' ') p++;
128                 char* num = p;
129                 while (*p >= '0' && *p <= '9') p++;
130                 if (*p != 0) {
131                     *p = 0;
132                     p++;
133                 }
134                 mem[i] = atoll(num);
135                 numFound++;
136                 break;
137             }
138             i++;
139         }
140         while (*p && *p != '\n') {
141             p++;
142         }
143         if (*p) p++;
144     }
145 }
146
147 int main(int argc, char *argv[]) {
148     pm_kernel_t *ker;
149     pm_process_t *proc;
150     pid_t *pids;
151     struct proc_info **procs;
152     size_t num_procs;
153     uint64_t total_pss;
154     uint64_t total_uss;
155     uint64_t total_swap;
156     uint64_t total_pswap;
157     uint64_t total_uswap;
158     uint64_t total_zswap;
159     char cmdline[256]; // this must be within the range of int
160     int error;
161     bool has_swap = false, has_zram = false;
162     uint64_t required_flags = 0;
163     uint64_t flags_mask = 0;
164
165     #define WS_OFF   0
166     #define WS_ONLY  1
167     #define WS_RESET 2
168     int ws;
169
170     int arg;
171     size_t i, j;
172
173     uint64_t mem[MEMINFO_COUNT] = { };
174     pm_proportional_swap_t *p_swap;
175     int fd, len;
176     char buffer[1024];
177     float zram_cr = 0.0;
178
179     signal(SIGPIPE, SIG_IGN);
180     compfn = &sort_by_pss;
181     order = -1;
182     ws = WS_OFF;
183
184     for (arg = 1; arg < argc; arg++) {
185         if (!strcmp(argv[arg], "-v")) { compfn = &sort_by_vss; continue; }
186         if (!strcmp(argv[arg], "-r")) { compfn = &sort_by_rss; continue; }
187         if (!strcmp(argv[arg], "-p")) { compfn = &sort_by_pss; continue; }
188         if (!strcmp(argv[arg], "-u")) { compfn = &sort_by_uss; continue; }
189         if (!strcmp(argv[arg], "-s")) { compfn = &sort_by_swap; continue; }
190         if (!strcmp(argv[arg], "-c")) { required_flags = 0; flags_mask = PM_PAGE_SWAPBACKED; continue; }
191         if (!strcmp(argv[arg], "-C")) { required_flags = flags_mask = PM_PAGE_SWAPBACKED; continue; }
192         if (!strcmp(argv[arg], "-k")) { required_flags = flags_mask = PM_PAGE_KSM; continue; }
193         if (!strcmp(argv[arg], "-w")) { ws = WS_ONLY; continue; }
194         if (!strcmp(argv[arg], "-W")) { ws = WS_RESET; continue; }
195         if (!strcmp(argv[arg], "-R")) { order *= -1; continue; }
196         if (!strcmp(argv[arg], "-h")) { usage(argv[0]); exit(0); }
197         fprintf(stderr, "Invalid argument \"%s\".\n", argv[arg]);
198         usage(argv[0]);
199         exit(EXIT_FAILURE);
200     }
201
202     get_mem_info(mem);
203     p_swap = pm_memusage_pswap_create(mem[MEMINFO_SWAP_TOTAL] * 1024);
204
205     error = pm_kernel_create(&ker);
206     if (error) {
207         fprintf(stderr, "Error creating kernel interface -- "
208                         "does this kernel have pagemap?\n");
209         exit(EXIT_FAILURE);
210     }
211
212     error = pm_kernel_pids(ker, &pids, &num_procs);
213     if (error) {
214         fprintf(stderr, "Error listing processes.\n");
215         exit(EXIT_FAILURE);
216     }
217
218     procs = calloc(num_procs, sizeof(struct proc_info*));
219     if (procs == NULL) {
220         fprintf(stderr, "calloc: %s", strerror(errno));
221         exit(EXIT_FAILURE);
222     }
223
224     for (i = 0; i < num_procs; i++) {
225         procs[i] = malloc(sizeof(struct proc_info));
226         if (procs[i] == NULL) {
227             fprintf(stderr, "malloc: %s\n", strerror(errno));
228             exit(EXIT_FAILURE);
229         }
230         procs[i]->pid = pids[i];
231         pm_memusage_zero(&procs[i]->usage);
232         pm_memusage_pswap_init_handle(&procs[i]->usage, p_swap);
233         error = pm_process_create(ker, pids[i], &proc);
234         if (error) {
235             fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]);
236             continue;
237         }
238
239         switch (ws) {
240         case WS_OFF:
241             error = pm_process_usage_flags(proc, &procs[i]->usage, flags_mask,
242                                            required_flags);
243             break;
244         case WS_ONLY:
245             error = pm_process_workingset(proc, &procs[i]->usage, 0);
246             break;
247         case WS_RESET:
248             error = pm_process_workingset(proc, NULL, 1);
249             break;
250         }
251
252         if (error) {
253             fprintf(stderr, "warning: could not read usage for %d\n", pids[i]);
254         }
255
256         if (ws != WS_RESET && procs[i]->usage.swap) {
257             has_swap = true;
258         }
259
260         pm_process_destroy(proc);
261     }
262
263     free(pids);
264
265     if (ws == WS_RESET) exit(0);
266
267     j = 0;
268     for (i = 0; i < num_procs; i++) {
269         if (procs[i]->usage.vss) {
270             procs[j++] = procs[i];
271         } else {
272             free(procs[i]);
273         }
274     }
275     num_procs = j;
276
277     qsort(procs, num_procs, sizeof(procs[0]), compfn);
278
279     if (has_swap) {
280         fd = open("/sys/block/zram0/mem_used_total", O_RDONLY);
281         if (fd >= 0) {
282             len = read(fd, buffer, sizeof(buffer)-1);
283             close(fd);
284             if (len > 0) {
285                 buffer[len] = 0;
286                 mem[MEMINFO_ZRAM_TOTAL] = atoll(buffer)/1024;
287                 zram_cr = (float) mem[MEMINFO_ZRAM_TOTAL] /
288                         (mem[MEMINFO_SWAP_TOTAL] - mem[MEMINFO_SWAP_FREE]);
289                 has_zram = true;
290             }
291         }
292     }
293
294     printf("%5s  ", "PID");
295     if (ws) {
296         printf("%7s  %7s  %7s  ", "WRss", "WPss", "WUss");
297         if (has_swap) {
298             printf("%7s  %7s  %7s  ", "WSwap", "WPSwap", "WUSwap");
299             if (has_zram) {
300                 printf("%7s  ", "WZSwap");
301             }
302         }
303     } else {
304         printf("%8s  %7s  %7s  %7s  ", "Vss", "Rss", "Pss", "Uss");
305         if (has_swap) {
306             printf("%7s  %7s  %7s  ", "Swap", "PSwap", "USwap");
307             if (has_zram) {
308                 printf("%7s  ", "ZSwap");
309             }
310         }
311     }
312
313     printf("%s\n", "cmdline");
314
315     total_pss = 0;
316     total_uss = 0;
317     total_swap = 0;
318     total_pswap = 0;
319     total_uswap = 0;
320     total_zswap = 0;
321
322     for (i = 0; i < num_procs; i++) {
323         if (getprocname(procs[i]->pid, cmdline, (int)sizeof(cmdline)) < 0) {
324             /*
325              * Something is probably seriously wrong if writing to the stack
326              * failed.
327              */
328             free(procs[i]);
329             continue;
330         }
331
332         total_pss += procs[i]->usage.pss;
333         total_uss += procs[i]->usage.uss;
334         total_swap += procs[i]->usage.swap;
335
336         printf("%5d  ", procs[i]->pid);
337
338         if (ws) {
339             printf("%6zuK  %6zuK  %6zuK  ",
340                 procs[i]->usage.rss / 1024,
341                 procs[i]->usage.pss / 1024,
342                 procs[i]->usage.uss / 1024
343             );
344         } else {
345             printf("%7zuK  %6zuK  %6zuK  %6zuK  ",
346                 procs[i]->usage.vss / 1024,
347                 procs[i]->usage.rss / 1024,
348                 procs[i]->usage.pss / 1024,
349                 procs[i]->usage.uss / 1024
350             );
351         }
352
353         if (has_swap) {
354             pm_swapusage_t su;
355
356             pm_memusage_pswap_get_usage(&procs[i]->usage, &su);
357             printf("%6zuK  ", procs[i]->usage.swap / 1024);
358             printf("%6zuK  ", su.proportional / 1024);
359             printf("%6zuK  ", su.unique / 1024);
360             total_pswap += su.proportional;
361             total_uswap += su.unique;
362             pm_memusage_pswap_free(&procs[i]->usage);
363             if (has_zram) {
364                 size_t zpswap = su.proportional * zram_cr;
365                 printf("%6zuK  ", zpswap / 1024);
366                 total_zswap += zpswap;
367             }
368         }
369
370         printf("%s\n", cmdline);
371
372         free(procs[i]);
373     }
374
375     free(procs);
376     pm_memusage_pswap_destroy(p_swap);
377
378     /* Print the separator line */
379     printf("%5s  ", "");
380
381     if (ws) {
382         printf("%7s  %7s  %7s  ", "", "------", "------");
383     } else {
384         printf("%8s  %7s  %7s  %7s  ", "", "", "------", "------");
385     }
386
387     if (has_swap) {
388         printf("%7s  %7s  %7s  ", "------", "------", "------");
389         if (has_zram) {
390             printf("%7s  ", "------");
391         }
392     }
393
394     printf("%s\n", "------");
395
396     /* Print the total line */
397     printf("%5s  ", "");
398     if (ws) {
399         printf("%7s  %6" PRIu64 "K  %6" PRIu64 "K  ",
400             "", total_pss / 1024, total_uss / 1024);
401     } else {
402         printf("%8s  %7s  %6" PRIu64 "K  %6" PRIu64 "K  ",
403             "", "", total_pss / 1024, total_uss / 1024);
404     }
405
406     if (has_swap) {
407         printf("%6" PRIu64 "K  ", total_swap / 1024);
408         printf("%6" PRIu64 "K  ", total_pswap / 1024);
409         printf("%6" PRIu64 "K  ", total_uswap / 1024);
410         if (has_zram) {
411             printf("%6" PRIu64 "K  ", total_zswap / 1024);
412         }
413     }
414
415     printf("TOTAL\n");
416
417     printf("\n");
418
419     if (has_swap) {
420         printf("ZRAM: %" PRIu64 "K physical used for %" PRIu64 "K in swap "
421                 "(%" PRIu64 "K total swap)\n",
422                 mem[MEMINFO_ZRAM_TOTAL], (mem[MEMINFO_SWAP_TOTAL] - mem[MEMINFO_SWAP_FREE]),
423                 mem[MEMINFO_SWAP_TOTAL]);
424     }
425     printf(" RAM: %" PRIu64 "K total, %" PRIu64 "K free, %" PRIu64 "K buffers, "
426             "%" PRIu64 "K cached, %" PRIu64 "K shmem, %" PRIu64 "K slab\n",
427             mem[MEMINFO_TOTAL], mem[MEMINFO_FREE], mem[MEMINFO_BUFFERS],
428             mem[MEMINFO_CACHED], mem[MEMINFO_SHMEM], mem[MEMINFO_SLAB]);
429
430     return 0;
431 }
432
433 static void usage(char *myname) {
434     fprintf(stderr, "Usage: %s [ -W ] [ -v | -r | -p | -u | -s | -h ]\n"
435                     "    -v  Sort by VSS.\n"
436                     "    -r  Sort by RSS.\n"
437                     "    -p  Sort by PSS.\n"
438                     "    -u  Sort by USS.\n"
439                     "    -s  Sort by swap.\n"
440                     "        (Default sort order is PSS.)\n"
441                     "    -R  Reverse sort order (default is descending).\n"
442                     "    -c  Only show cached (storage backed) pages\n"
443                     "    -C  Only show non-cached (ram/swap backed) pages\n"
444                     "    -k  Only show pages collapsed by KSM\n"
445                     "    -w  Display statistics for working set only.\n"
446                     "    -W  Reset working set of all processes.\n"
447                     "    -h  Display this help screen.\n",
448     myname);
449 }
450
451 /*
452  * Get the process name for a given PID. Inserts the process name into buffer
453  * buf of length len. The size of the buffer must be greater than zero to get
454  * any useful output.
455  *
456  * Note that fgets(3) only declares length as an int, so our buffer size is
457  * also declared as an int.
458  *
459  * Returns 0 on success, a positive value on partial success, and -1 on
460  * failure. Other interesting values:
461  *   1 on failure to create string to examine proc cmdline entry
462  *   2 on failure to open proc cmdline entry
463  *   3 on failure to read proc cmdline entry
464  */
465 static int getprocname(pid_t pid, char *buf, int len) {
466     char *filename;
467     FILE *f;
468     int rc = 0;
469     static const char* unknown_cmdline = "<unknown>";
470
471     if (len <= 0) {
472         return -1;
473     }
474
475     if (asprintf(&filename, "/proc/%d/cmdline", pid) < 0) {
476         rc = 1;
477         goto exit;
478     }
479
480     f = fopen(filename, "r");
481     if (f == NULL) {
482         rc = 2;
483         goto releasefilename;
484     }
485
486     if (fgets(buf, len, f) == NULL) {
487         rc = 3;
488         goto closefile;
489     }
490
491 closefile:
492     (void) fclose(f);
493 releasefilename:
494     free(filename);
495 exit:
496     if (rc != 0) {
497         /*
498          * The process went away before we could read its process name. Try
499          * to give the user "<unknown>" here, but otherwise they get to look
500          * at a blank.
501          */
502         if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) {
503             rc = 4;
504         }
505     }
506
507     return rc;
508 }
509
510 static int numcmp(uint64_t a, uint64_t b) {
511     if (a < b) return -1;
512     if (a > b) return 1;
513     return 0;
514 }
515
516 #define create_sort(field, compfn) \
517     static int sort_by_ ## field (const void *a, const void *b) { \
518         return order * compfn( \
519             (*((struct proc_info**)a))->usage.field, \
520             (*((struct proc_info**)b))->usage.field \
521         ); \
522     }
523
524 create_sort(vss, numcmp)
525 create_sort(rss, numcmp)
526 create_sort(pss, numcmp)
527 create_sort(uss, numcmp)
528 create_sort(swap, numcmp)