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