• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2008, The Android Open Source Project
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *  * Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in
12  *    the documentation and/or other materials provided with the
13  *    distribution.
14  *  * Neither the name of Google, Inc. nor the names of its contributors
15  *    may be used to endorse or promote products derived from this
16  *    software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <ctype.h>
33 #include <dirent.h>
34 #include <errno.h>
35 #include <grp.h>
36 #include <inttypes.h>
37 #include <pwd.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/types.h>
42 #include <time.h>
43 #include <unistd.h>
44 
45 #include <cutils/sched_policy.h>
46 
47 struct cpu_info {
48     long unsigned utime, ntime, stime, itime;
49     long unsigned iowtime, irqtime, sirqtime;
50 };
51 
52 #define PROC_NAME_LEN 64
53 #define THREAD_NAME_LEN 32
54 #define POLICY_NAME_LEN 4
55 
56 struct proc_info {
57     struct proc_info *next;
58     pid_t pid;
59     pid_t tid;
60     uid_t uid;
61     gid_t gid;
62     char name[PROC_NAME_LEN];
63     char tname[THREAD_NAME_LEN];
64     char state;
65     uint64_t utime;
66     uint64_t stime;
67     char pr[3];
68     long ni;
69     uint64_t delta_utime;
70     uint64_t delta_stime;
71     uint64_t delta_time;
72     uint64_t vss;
73     uint64_t rss;
74     int num_threads;
75     char policy[POLICY_NAME_LEN];
76 };
77 
78 struct proc_list {
79     struct proc_info **array;
80     int size;
81 };
82 
83 #define die(...) { fprintf(stderr, __VA_ARGS__); exit(EXIT_FAILURE); }
84 
85 #define INIT_PROCS 50
86 #define THREAD_MULT 8
87 static struct proc_info **old_procs, **new_procs;
88 static int num_old_procs, num_new_procs;
89 static struct proc_info *free_procs;
90 static int num_used_procs, num_free_procs;
91 
92 static int max_procs, delay, iterations, threads;
93 
94 static struct cpu_info old_cpu, new_cpu;
95 
96 static struct proc_info *alloc_proc(void);
97 static void free_proc(struct proc_info *proc);
98 static void read_procs(void);
99 static int read_stat(char *filename, struct proc_info *proc);
100 static void read_policy(int pid, struct proc_info *proc);
101 static void add_proc(int proc_num, struct proc_info *proc);
102 static int read_cmdline(char *filename, struct proc_info *proc);
103 static int read_status(char *filename, struct proc_info *proc);
104 static void print_procs(void);
105 static struct proc_info *find_old_proc(pid_t pid, pid_t tid);
106 static void free_old_procs(void);
107 static int (*proc_cmp)(const void *a, const void *b);
108 static int proc_cpu_cmp(const void *a, const void *b);
109 static int proc_vss_cmp(const void *a, const void *b);
110 static int proc_rss_cmp(const void *a, const void *b);
111 static int proc_thr_cmp(const void *a, const void *b);
112 static int numcmp(long long a, long long b);
113 static void usage(char *cmd);
114 
top_main(int argc,char * argv[])115 int top_main(int argc, char *argv[]) {
116     num_used_procs = num_free_procs = 0;
117 
118     max_procs = 0;
119     delay = 3;
120     iterations = -1;
121     proc_cmp = &proc_cpu_cmp;
122     for (int i = 1; i < argc; i++) {
123         if (!strcmp(argv[i], "-m")) {
124             if (i + 1 >= argc) {
125                 fprintf(stderr, "Option -m expects an argument.\n");
126                 usage(argv[0]);
127                 exit(EXIT_FAILURE);
128             }
129             max_procs = atoi(argv[++i]);
130             continue;
131         }
132         if (!strcmp(argv[i], "-n")) {
133             if (i + 1 >= argc) {
134                 fprintf(stderr, "Option -n expects an argument.\n");
135                 usage(argv[0]);
136                 exit(EXIT_FAILURE);
137             }
138             iterations = atoi(argv[++i]);
139             continue;
140         }
141         if (!strcmp(argv[i], "-d")) {
142             if (i + 1 >= argc) {
143                 fprintf(stderr, "Option -d expects an argument.\n");
144                 usage(argv[0]);
145                 exit(EXIT_FAILURE);
146             }
147             delay = atoi(argv[++i]);
148             continue;
149         }
150         if (!strcmp(argv[i], "-s")) {
151             if (i + 1 >= argc) {
152                 fprintf(stderr, "Option -s expects an argument.\n");
153                 usage(argv[0]);
154                 exit(EXIT_FAILURE);
155             }
156             ++i;
157             if (!strcmp(argv[i], "cpu")) { proc_cmp = &proc_cpu_cmp; continue; }
158             if (!strcmp(argv[i], "vss")) { proc_cmp = &proc_vss_cmp; continue; }
159             if (!strcmp(argv[i], "rss")) { proc_cmp = &proc_rss_cmp; continue; }
160             if (!strcmp(argv[i], "thr")) { proc_cmp = &proc_thr_cmp; continue; }
161             fprintf(stderr, "Invalid argument \"%s\" for option -s.\n", argv[i]);
162             exit(EXIT_FAILURE);
163         }
164         if (!strcmp(argv[i], "-H") || !strcmp(argv[i], "-t")) { threads = 1; continue; }
165         if (!strcmp(argv[i], "-h")) {
166             usage(argv[0]);
167             exit(EXIT_SUCCESS);
168         }
169         fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]);
170         usage(argv[0]);
171         exit(EXIT_FAILURE);
172     }
173 
174     if (threads && proc_cmp == &proc_thr_cmp) {
175         fprintf(stderr, "Sorting by threads per thread makes no sense!\n");
176         exit(EXIT_FAILURE);
177     }
178 
179     free_procs = NULL;
180 
181     num_new_procs = num_old_procs = 0;
182     new_procs = old_procs = NULL;
183 
184     read_procs();
185 
186     // Pause 250ms to get better data and avoid divide by zero later (http://b/32478213).
187     struct timespec ts = { .tv_sec = 0, .tv_nsec = 250000000 };
188     TEMP_FAILURE_RETRY(nanosleep(&ts, &ts));
189 
190     while ((iterations == -1) || (iterations-- > 0)) {
191         old_procs = new_procs;
192         num_old_procs = num_new_procs;
193         memcpy(&old_cpu, &new_cpu, sizeof(old_cpu));
194         read_procs();
195         print_procs();
196         free_old_procs();
197         fflush(stdout);
198         if (iterations != 0) sleep(delay);
199     }
200 
201     return 0;
202 }
203 
alloc_proc(void)204 static struct proc_info *alloc_proc(void) {
205     struct proc_info *proc;
206 
207     if (free_procs) {
208         proc = free_procs;
209         free_procs = free_procs->next;
210         num_free_procs--;
211     } else {
212         proc = malloc(sizeof(*proc));
213         if (!proc) die("Could not allocate struct process_info.\n");
214     }
215 
216     num_used_procs++;
217 
218     return proc;
219 }
220 
free_proc(struct proc_info * proc)221 static void free_proc(struct proc_info *proc) {
222     proc->next = free_procs;
223     free_procs = proc;
224 
225     num_used_procs--;
226     num_free_procs++;
227 }
228 
229 #define MAX_LINE 256
230 
read_procs(void)231 static void read_procs(void) {
232     DIR *proc_dir, *task_dir;
233     struct dirent *pid_dir, *tid_dir;
234     char filename[64];
235     FILE *file;
236     int proc_num;
237     struct proc_info *proc;
238     pid_t pid, tid;
239 
240     int i;
241 
242     proc_dir = opendir("/proc");
243     if (!proc_dir) die("Could not open /proc.\n");
244 
245     new_procs = calloc(INIT_PROCS * (threads ? THREAD_MULT : 1), sizeof(struct proc_info *));
246     num_new_procs = INIT_PROCS * (threads ? THREAD_MULT : 1);
247 
248     file = fopen("/proc/stat", "r");
249     if (!file) die("Could not open /proc/stat.\n");
250     fscanf(file, "cpu  %lu %lu %lu %lu %lu %lu %lu", &new_cpu.utime, &new_cpu.ntime, &new_cpu.stime,
251             &new_cpu.itime, &new_cpu.iowtime, &new_cpu.irqtime, &new_cpu.sirqtime);
252     fclose(file);
253 
254     proc_num = 0;
255     while ((pid_dir = readdir(proc_dir))) {
256         if (!isdigit(pid_dir->d_name[0]))
257             continue;
258 
259         pid = atoi(pid_dir->d_name);
260 
261         struct proc_info cur_proc;
262 
263         if (!threads) {
264             proc = alloc_proc();
265 
266             proc->pid = proc->tid = pid;
267 
268             sprintf(filename, "/proc/%d/stat", pid);
269             read_stat(filename, proc);
270 
271             sprintf(filename, "/proc/%d/cmdline", pid);
272             read_cmdline(filename, proc);
273 
274             sprintf(filename, "/proc/%d/status", pid);
275             read_status(filename, proc);
276 
277             read_policy(pid, proc);
278 
279             proc->num_threads = 0;
280         } else {
281             sprintf(filename, "/proc/%d/cmdline", pid);
282             read_cmdline(filename, &cur_proc);
283 
284             sprintf(filename, "/proc/%d/status", pid);
285             read_status(filename, &cur_proc);
286 
287             proc = NULL;
288         }
289 
290         sprintf(filename, "/proc/%d/task", pid);
291         task_dir = opendir(filename);
292         if (!task_dir) continue;
293 
294         while ((tid_dir = readdir(task_dir))) {
295             if (!isdigit(tid_dir->d_name[0]))
296                 continue;
297 
298             if (threads) {
299                 tid = atoi(tid_dir->d_name);
300 
301                 proc = alloc_proc();
302 
303                 proc->pid = pid; proc->tid = tid;
304 
305                 sprintf(filename, "/proc/%d/task/%d/stat", pid, tid);
306                 read_stat(filename, proc);
307 
308                 read_policy(tid, proc);
309 
310                 strcpy(proc->name, cur_proc.name);
311                 proc->uid = cur_proc.uid;
312                 proc->gid = cur_proc.gid;
313 
314                 add_proc(proc_num++, proc);
315             } else {
316                 proc->num_threads++;
317             }
318         }
319 
320         closedir(task_dir);
321 
322         if (!threads)
323             add_proc(proc_num++, proc);
324     }
325 
326     for (i = proc_num; i < num_new_procs; i++)
327         new_procs[i] = NULL;
328 
329     closedir(proc_dir);
330 }
331 
read_stat(char * filename,struct proc_info * proc)332 static int read_stat(char *filename, struct proc_info *proc) {
333     FILE *file;
334     char buf[MAX_LINE], *open_paren, *close_paren;
335 
336     file = fopen(filename, "r");
337     if (!file) return 1;
338     fgets(buf, MAX_LINE, file);
339     fclose(file);
340 
341     /* Split at first '(' and last ')' to get process name. */
342     open_paren = strchr(buf, '(');
343     close_paren = strrchr(buf, ')');
344     if (!open_paren || !close_paren) return 1;
345 
346     *open_paren = *close_paren = '\0';
347     strncpy(proc->tname, open_paren + 1, THREAD_NAME_LEN);
348     proc->tname[THREAD_NAME_LEN-1] = 0;
349 
350     // Scan rest of string.
351     long pr;
352     sscanf(close_paren + 1,
353            " %c "
354            "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
355            "%" SCNu64 // utime %lu (14)
356            "%" SCNu64 // stime %lu (15)
357            "%*d %*d "
358            "%ld " // priority %ld (18)
359            "%ld " // nice %ld (19)
360            "%*d %*d %*d "
361            "%" SCNu64 // vsize %lu (23)
362            "%" SCNu64, // rss %ld (24)
363            &proc->state,
364            &proc->utime,
365            &proc->stime,
366            &pr,
367            &proc->ni,
368            &proc->vss,
369            &proc->rss);
370 
371     // Translate the PR field.
372     if (pr < -9) strcpy(proc->pr, "RT");
373     else snprintf(proc->pr, sizeof(proc->pr), "%ld", pr);
374 
375     return 0;
376 }
377 
add_proc(int proc_num,struct proc_info * proc)378 static void add_proc(int proc_num, struct proc_info *proc) {
379     int i;
380 
381     if (proc_num >= num_new_procs) {
382         new_procs = realloc(new_procs, 2 * num_new_procs * sizeof(struct proc_info *));
383         if (!new_procs) die("Could not expand procs array.\n");
384         for (i = num_new_procs; i < 2 * num_new_procs; i++)
385             new_procs[i] = NULL;
386         num_new_procs = 2 * num_new_procs;
387     }
388     new_procs[proc_num] = proc;
389 }
390 
read_cmdline(char * filename,struct proc_info * proc)391 static int read_cmdline(char *filename, struct proc_info *proc) {
392     FILE *file;
393     char line[MAX_LINE];
394 
395     line[0] = '\0';
396     file = fopen(filename, "r");
397     if (!file) return 1;
398     fgets(line, MAX_LINE, file);
399     fclose(file);
400     if (strlen(line) > 0) {
401         strncpy(proc->name, line, PROC_NAME_LEN);
402         proc->name[PROC_NAME_LEN-1] = 0;
403     } else
404         proc->name[0] = 0;
405     return 0;
406 }
407 
read_policy(int pid,struct proc_info * proc)408 static void read_policy(int pid, struct proc_info *proc) {
409     SchedPolicy p;
410     if (get_sched_policy(pid, &p) < 0)
411         strlcpy(proc->policy, "unk", POLICY_NAME_LEN);
412     else {
413         strlcpy(proc->policy, get_sched_policy_name(p), POLICY_NAME_LEN);
414         proc->policy[2] = '\0';
415     }
416 }
417 
read_status(char * filename,struct proc_info * proc)418 static int read_status(char *filename, struct proc_info *proc) {
419     FILE *file;
420     char line[MAX_LINE];
421     unsigned int uid, gid;
422 
423     file = fopen(filename, "r");
424     if (!file) return 1;
425     while (fgets(line, MAX_LINE, file)) {
426         sscanf(line, "Uid: %u", &uid);
427         sscanf(line, "Gid: %u", &gid);
428     }
429     fclose(file);
430     proc->uid = uid; proc->gid = gid;
431     return 0;
432 }
433 
print_procs(void)434 static void print_procs(void) {
435     static int call = 0;
436     int i;
437     struct proc_info *old_proc, *proc;
438     long unsigned total_delta_time;
439 
440     for (i = 0; i < num_new_procs; i++) {
441         if (new_procs[i]) {
442             old_proc = find_old_proc(new_procs[i]->pid, new_procs[i]->tid);
443             if (old_proc) {
444                 new_procs[i]->delta_utime = new_procs[i]->utime - old_proc->utime;
445                 new_procs[i]->delta_stime = new_procs[i]->stime - old_proc->stime;
446             } else {
447                 new_procs[i]->delta_utime = 0;
448                 new_procs[i]->delta_stime = 0;
449             }
450             new_procs[i]->delta_time = new_procs[i]->delta_utime + new_procs[i]->delta_stime;
451         }
452     }
453 
454     total_delta_time = (new_cpu.utime + new_cpu.ntime + new_cpu.stime + new_cpu.itime
455                         + new_cpu.iowtime + new_cpu.irqtime + new_cpu.sirqtime)
456                      - (old_cpu.utime + old_cpu.ntime + old_cpu.stime + old_cpu.itime
457                         + old_cpu.iowtime + old_cpu.irqtime + old_cpu.sirqtime);
458 
459     qsort(new_procs, num_new_procs, sizeof(struct proc_info *), proc_cmp);
460 
461     if (call++ > 0) printf("\n\n\n");
462     printf("User %ld%%, System %ld%%, IOW %ld%%, IRQ %ld%%\n",
463             ((new_cpu.utime + new_cpu.ntime) - (old_cpu.utime + old_cpu.ntime)) * 100  / total_delta_time,
464             ((new_cpu.stime ) - (old_cpu.stime)) * 100 / total_delta_time,
465             ((new_cpu.iowtime) - (old_cpu.iowtime)) * 100 / total_delta_time,
466             ((new_cpu.irqtime + new_cpu.sirqtime)
467                     - (old_cpu.irqtime + old_cpu.sirqtime)) * 100 / total_delta_time);
468     printf("User %ld + Nice %ld + Sys %ld + Idle %ld + IOW %ld + IRQ %ld + SIRQ %ld = %ld\n",
469             new_cpu.utime - old_cpu.utime,
470             new_cpu.ntime - old_cpu.ntime,
471             new_cpu.stime - old_cpu.stime,
472             new_cpu.itime - old_cpu.itime,
473             new_cpu.iowtime - old_cpu.iowtime,
474             new_cpu.irqtime - old_cpu.irqtime,
475             new_cpu.sirqtime - old_cpu.sirqtime,
476             total_delta_time);
477     printf("\n");
478     if (!threads)
479         printf("%5s %-8s %2s %3s %4s %1s %5s %7s %7s %3s %s\n", "PID", "USER", "PR", "NI", "CPU%", "S", "#THR", "VSS", "RSS", "PCY", "Name");
480     else
481         printf("%5s %5s %-8s %2s %3s %4s %1s %7s %7s %3s %-15s %s\n", "PID", "TID", "USER", "PR", "NI", "CPU%", "S", "VSS", "RSS", "PCY", "Thread", "Proc");
482 
483     for (i = 0; i < num_new_procs; i++) {
484         proc = new_procs[i];
485 
486         if (!proc || (max_procs && (i >= max_procs)))
487             break;
488         struct passwd* user = getpwuid(proc->uid);
489         char user_buf[20];
490         char* user_str;
491         if (user && user->pw_name) {
492             user_str = user->pw_name;
493         } else {
494             snprintf(user_buf, 20, "%d", proc->uid);
495             user_str = user_buf;
496         }
497         if (!threads) {
498             printf("%5d %-8.8s %2s %3ld %3" PRIu64 "%% %c %5d %6" PRIu64 "K %6" PRIu64 "K %3s %s\n",
499                    proc->pid, user_str, proc->pr, proc->ni,
500                    proc->delta_time * 100 / total_delta_time, proc->state, proc->num_threads,
501                    proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy,
502                    proc->name[0] != 0 ? proc->name : proc->tname);
503         } else {
504             printf("%5d %5d %-8.8s %2s %3ld %3" PRIu64 "%% %c %6" PRIu64 "K %6" PRIu64 "K %3s %-15s %s\n",
505                    proc->pid, proc->tid, user_str, proc->pr, proc->ni,
506                    proc->delta_time * 100 / total_delta_time, proc->state,
507                    proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy,
508                    proc->tname, proc->name);
509         }
510     }
511 }
512 
find_old_proc(pid_t pid,pid_t tid)513 static struct proc_info *find_old_proc(pid_t pid, pid_t tid) {
514     int i;
515 
516     for (i = 0; i < num_old_procs; i++)
517         if (old_procs[i] && (old_procs[i]->pid == pid) && (old_procs[i]->tid == tid))
518             return old_procs[i];
519 
520     return NULL;
521 }
522 
free_old_procs(void)523 static void free_old_procs(void) {
524     int i;
525 
526     for (i = 0; i < num_old_procs; i++)
527         if (old_procs[i])
528             free_proc(old_procs[i]);
529 
530     free(old_procs);
531 }
532 
proc_cpu_cmp(const void * a,const void * b)533 static int proc_cpu_cmp(const void *a, const void *b) {
534     struct proc_info *pa, *pb;
535 
536     pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
537 
538     if (!pa && !pb) return 0;
539     if (!pa) return 1;
540     if (!pb) return -1;
541 
542     return -numcmp(pa->delta_time, pb->delta_time);
543 }
544 
proc_vss_cmp(const void * a,const void * b)545 static int proc_vss_cmp(const void *a, const void *b) {
546     struct proc_info *pa, *pb;
547 
548     pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
549 
550     if (!pa && !pb) return 0;
551     if (!pa) return 1;
552     if (!pb) return -1;
553 
554     return -numcmp(pa->vss, pb->vss);
555 }
556 
proc_rss_cmp(const void * a,const void * b)557 static int proc_rss_cmp(const void *a, const void *b) {
558     struct proc_info *pa, *pb;
559 
560     pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
561 
562     if (!pa && !pb) return 0;
563     if (!pa) return 1;
564     if (!pb) return -1;
565 
566     return -numcmp(pa->rss, pb->rss);
567 }
568 
proc_thr_cmp(const void * a,const void * b)569 static int proc_thr_cmp(const void *a, const void *b) {
570     struct proc_info *pa, *pb;
571 
572     pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
573 
574     if (!pa && !pb) return 0;
575     if (!pa) return 1;
576     if (!pb) return -1;
577 
578     return -numcmp(pa->num_threads, pb->num_threads);
579 }
580 
numcmp(long long a,long long b)581 static int numcmp(long long a, long long b) {
582     if (a < b) return -1;
583     if (a > b) return 1;
584     return 0;
585 }
586 
usage(char * cmd)587 static void usage(char *cmd) {
588     fprintf(stderr, "Usage: %s [ -m max_procs ] [ -n iterations ] [ -d delay ] [ -s sort_column ] [ -t ] [ -h ]\n"
589                     "    -m num  Maximum number of processes to display.\n"
590                     "    -n num  Updates to show before exiting.\n"
591                     "    -d num  Seconds to wait between updates.\n"
592                     "    -s col  Column to sort by (cpu,vss,rss,thr).\n"
593                     "    -H      Show threads instead of processes.\n"
594                     "    -h      Display this help screen.\n",
595         cmd);
596 }
597