OSDN Git Service

Add strlen tests. DO NOT MERGE
[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 <stdbool.h>
20 #include <stdlib.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <fcntl.h>
25
26 #include <pagemap/pagemap.h>
27
28 struct proc_info {
29     pid_t pid;
30     pm_memusage_t usage;
31     unsigned long wss;
32 };
33
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);
37
38 #define declare_sort(field) \
39     static int sort_by_ ## field (const void *a, const void *b)
40
41 declare_sort(vss);
42 declare_sort(rss);
43 declare_sort(pss);
44 declare_sort(uss);
45 declare_sort(swap);
46
47 int (*compfn)(const void *a, const void *b);
48 static int order;
49
50 void print_mem_info() {
51     char buffer[1024];
52     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:",
75             "Shmem:",
76             "Slab:",
77             NULL
78     };
79     static const int tagsLen[] = {
80             9,
81             8,
82             8,
83             7,
84             6,
85             5,
86             0
87     };
88     long mem[] = { 0, 0, 0, 0, 0, 0 };
89
90     char* p = buffer;
91     while (*p && numFound < 6) {
92         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++;
102                 }
103                 mem[i] = atoll(num);
104                 numFound++;
105                 break;
106             }
107             i++;
108         }
109         while (*p && *p != '\n') {
110             p++;
111         }
112         if (*p) p++;
113     }
114
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]);
117 }
118
119 int main(int argc, char *argv[]) {
120     pm_kernel_t *ker;
121     pm_process_t *proc;
122     pid_t *pids;
123     struct proc_info **procs;
124     size_t num_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
129     int error;
130     bool has_swap = false;
131     uint64_t required_flags = 0;
132     uint64_t flags_mask = 0;
133
134     #define WS_OFF   0
135     #define WS_ONLY  1
136     #define WS_RESET 2
137     int ws;
138
139     int arg;
140     size_t i, j;
141
142     signal(SIGPIPE, SIG_IGN);
143     compfn = &sort_by_pss;
144     order = -1;
145     ws = WS_OFF;
146
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]);
161         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
178     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
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));
188             exit(EXIT_FAILURE);
189         }
190         procs[i]->pid = pids[i];
191         pm_memusage_zero(&procs[i]->usage);
192         error = pm_process_create(ker, pids[i], &proc);
193         if (error) {
194             fprintf(stderr, "warning: could not create process interface for %d\n", pids[i]);
195             continue;
196         }
197
198         switch (ws) {
199         case WS_OFF:
200             error = pm_process_usage_flags(proc, &procs[i]->usage, flags_mask,
201                                            required_flags);
202             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
215         if (ws != WS_RESET && procs[i]->usage.swap) {
216             has_swap = true;
217         }
218
219         pm_process_destroy(proc);
220     }
221
222     free(pids);
223
224     if (ws == WS_RESET) exit(0);
225
226     j = 0;
227     for (i = 0; i < num_procs; i++) {
228         if (procs[i]->usage.vss) {
229             procs[j++] = procs[i];
230         } else {
231             free(procs[i]);
232         }
233     }
234     num_procs = j;
235
236     qsort(procs, num_procs, sizeof(procs[0]), compfn);
237
238     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");
252
253     total_pss = 0;
254     total_uss = 0;
255     total_swap = 0;
256
257     for (i = 0; i < num_procs; i++) {
258         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
267         total_pss += procs[i]->usage.pss;
268         total_uss += procs[i]->usage.uss;
269         total_swap += procs[i]->usage.swap;
270
271         printf("%5d  ", procs[i]->pid);
272
273         if (ws) {
274             printf("%6dK  %6dK  %6dK  ",
275                 procs[i]->usage.rss / 1024,
276                 procs[i]->usage.pss / 1024,
277                 procs[i]->usage.uss / 1024
278             );
279         } else {
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
285             );
286         }
287
288         if (has_swap) {
289             printf("%6dK  ", procs[i]->usage.swap / 1024);
290         }
291
292         printf("%s\n", cmdline);
293
294         free(procs[i]);
295     }
296
297     free(procs);
298
299     /* Print the separator line */
300     printf("%5s  ", "");
301
302     if (ws) {
303         printf("%7s  %7s  %7s  ", "", "------", "------");
304     } else {
305         printf("%8s  %7s  %7s  %7s  ", "", "", "------", "------");
306     }
307
308     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
330     printf("\n");
331     print_mem_info();
332
333     return 0;
334 }
335
336 static void usage(char *myname) {
337     fprintf(stderr, "Usage: %s [ -W ] [ -v | -r | -p | -u | -s | -h ]\n"
338                     "    -v  Sort by VSS.\n"
339                     "    -r  Sort by RSS.\n"
340                     "    -p  Sort by PSS.\n"
341                     "    -u  Sort by USS.\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",
351     myname);
352 }
353
354 /*
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  */
368 static int getprocname(pid_t pid, char *buf, int len) {
369     char *filename;
370     FILE *f;
371     int rc = 0;
372     static const char* unknown_cmdline = "<unknown>";
373
374     if (len <= 0) {
375         return -1;
376     }
377
378     if (asprintf(&filename, "/proc/%zd/cmdline", pid) < 0) {
379         rc = 1;
380         goto exit;
381     }
382
383     f = fopen(filename, "r");
384     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
394 closefile:
395     (void) fclose(f);
396 releasefilename:
397     free(filename);
398 exit:
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;
411 }
412
413 static 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
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)