OSDN Git Service

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