blob: 6859b50bce0922fbdf015c2134890faa79ededdf [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001#include <stdio.h>
2#include <stdlib.h>
3#include <ctype.h>
4#include <fcntl.h>
Olivier Baillyb93e5812010-11-17 11:47:23 -08005#include <unistd.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08006
Olivier Baillyb93e5812010-11-17 11:47:23 -08007#include <stdint.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08008#include <string.h>
9
10#include <sys/stat.h>
11#include <sys/types.h>
12#include <dirent.h>
13#include <signal.h>
14
15#include <pwd.h>
16
17struct thread_info {
18 int pid;
19 int tid;
20 char name[64];
21 uint64_t exec_time;
22 uint64_t delay_time;
23 uint32_t run_count;
24};
25
26struct thread_table {
27 size_t allocated;
28 size_t active;
29 struct thread_info *data;
30};
31
32enum {
33 FLAG_BATCH = 1U << 0,
34 FLAG_HIDE_IDLE = 1U << 1,
35 FLAG_SHOW_THREADS = 1U << 2,
36 FLAG_USE_ALTERNATE_SCREEN = 1U << 3,
37};
38
39static int time_dp = 9;
40static int time_div = 1;
41#define NS_TO_S_D(ns) \
42 (uint32_t)((ns) / 1000000000), time_dp, ((uint32_t)((ns) % 1000000000) / time_div)
43
44struct thread_table processes;
45struct thread_table last_processes;
46struct thread_table threads;
47struct thread_table last_threads;
48
49static void grow_table(struct thread_table *table)
50{
51 size_t size = table->allocated;
52 struct thread_info *new_table;
53 if (size < 128)
54 size = 128;
55 else
56 size *= 2;
57
58 new_table = realloc(table->data, size * sizeof(*table->data));
59 if (new_table == NULL) {
60 fprintf(stderr, "out of memory\n");
61 exit(1);
62 }
63 table->data = new_table;
64 table->allocated = size;
65}
66
67static struct thread_info *get_item(struct thread_table *table)
68{
69 if (table->active >= table->allocated)
70 grow_table(table);
71 return table->data + table->active;
72}
73
74static void commit_item(struct thread_table *table)
75{
76 table->active++;
77}
78
79static int read_line(char *line, size_t line_size)
80{
81 int fd;
82 int len;
83 fd = open(line, O_RDONLY);
84 if(fd == 0)
85 return -1;
86 len = read(fd, line, line_size - 1);
87 close(fd);
88 if (len <= 0)
89 return -1;
90 line[len] = '\0';
91 return 0;
92}
93
94static void add_thread(int pid, int tid, struct thread_info *proc_info)
95{
96 char line[1024];
97 char *name, *name_end;
98 size_t name_len;
99 struct thread_info *info;
100 if(tid == 0)
101 info = get_item(&processes);
102 else
103 info = get_item(&threads);
104 info->pid = pid;
105 info->tid = tid;
106
107 if(tid)
108 sprintf(line, "/proc/%d/task/%d/schedstat", pid, tid);
109 else
110 sprintf(line, "/proc/%d/schedstat", pid);
111 if (read_line(line, sizeof(line)))
112 return;
113 if(sscanf(line, "%llu %llu %u", &info->exec_time, &info->delay_time, &info->run_count) != 3)
114 return;
115 if (proc_info) {
116 proc_info->exec_time += info->exec_time;
117 proc_info->delay_time += info->delay_time;
118 proc_info->run_count += info->run_count;
119 }
120
121 name = NULL;
122 if (!tid) {
123 sprintf(line, "/proc/%d/cmdline", pid);
124 if (read_line(line, sizeof(line)) == 0 && line[0]) {
125 name = line;
126 name_len = strlen(name);
127 }
128 }
129 if (!name) {
130 if (tid)
131 sprintf(line, "/proc/%d/task/%d/stat", pid, tid);
132 else
133 sprintf(line, "/proc/%d/stat", pid);
134 if (read_line(line, sizeof(line)))
135 return;
136 name = strchr(line, '(');
137 if (name == NULL)
138 return;
139 name_end = strchr(name, ')');
140 if (name_end == NULL)
141 return;
142 name++;
143 name_len = name_end - name;
144 }
145 if (name_len >= sizeof(info->name))
146 name_len = sizeof(info->name) - 1;
147 memcpy(info->name, name, name_len);
148 info->name[name_len] = '\0';
149 if(tid == 0)
150 commit_item(&processes);
151 else
152 commit_item(&threads);
153}
154
155static void add_threads(int pid, struct thread_info *proc_info)
156{
157 char path[1024];
158 DIR *d;
159 struct dirent *de;
160 sprintf(path, "/proc/%d/task", pid);
161 d = opendir(path);
162 if(d == 0) return;
163 while((de = readdir(d)) != 0){
164 if(isdigit(de->d_name[0])){
165 int tid = atoi(de->d_name);
166 add_thread(pid, tid, proc_info);
167 }
168 }
169 closedir(d);
170}
171
172static void print_threads(int pid, uint32_t flags)
173{
174 size_t i, j;
175 for (i = 0; i < last_threads.active; i++) {
176 int epid = last_threads.data[i].pid;
177 int tid = last_threads.data[i].tid;
178 if (epid != pid)
179 continue;
180 for (j = 0; j < threads.active; j++)
181 if (tid == threads.data[j].tid)
182 break;
183 if (j == threads.active)
184 printf(" %5u died\n", tid);
185 else if (!(flags & FLAG_HIDE_IDLE) || threads.data[j].run_count - last_threads.data[i].run_count)
186 printf(" %5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", tid,
187 NS_TO_S_D(threads.data[j].exec_time - last_threads.data[i].exec_time),
188 NS_TO_S_D(threads.data[j].delay_time - last_threads.data[i].delay_time),
189 threads.data[j].run_count - last_threads.data[i].run_count,
190 NS_TO_S_D(threads.data[j].exec_time), NS_TO_S_D(threads.data[j].delay_time),
191 threads.data[j].run_count, threads.data[j].name);
192 }
193}
194
195static void update_table(DIR *d, uint32_t flags)
196{
197 size_t i, j;
198 struct dirent *de;
199
200 rewinddir(d);
201 while((de = readdir(d)) != 0){
202 if(isdigit(de->d_name[0])){
203 int pid = atoi(de->d_name);
204 struct thread_info *proc_info;
205 add_thread(pid, 0, NULL);
206 proc_info = &processes.data[processes.active - 1];
207 proc_info->exec_time = 0;
208 proc_info->delay_time = 0;
209 proc_info->run_count = 0;
210 add_threads(pid, proc_info);
211 }
212 }
213 if (!(flags & FLAG_BATCH))
214 printf("\e[H\e[0J");
215 printf("Processes: %d, Threads %d\n", processes.active, threads.active);
216 switch (time_dp) {
217 case 3:
218 printf(" TID --- SINCE LAST ---- ---------- TOTAL ----------\n");
219 printf(" PID EXEC_T DELAY SCHED EXEC_TIME DELAY_TIM SCHED NAME\n");
220 break;
221 case 6:
222 printf(" TID ------ SINCE LAST ------- ------------ TOTAL -----------\n");
223 printf(" PID EXEC_TIME DELAY_TIM SCHED EXEC_TIME DELAY_TIME SCHED NAME\n");
224 break;
225 default:
226 printf(" TID -------- SINCE LAST -------- ------------- TOTAL -------------\n");
227 printf(" PID EXEC_TIME DELAY_TIME SCHED EXEC_TIME DELAY_TIME SCHED NAME\n");
228 break;
229 }
230 for (i = 0; i < last_processes.active; i++) {
231 int pid = last_processes.data[i].pid;
232 int tid = last_processes.data[i].tid;
233 for (j = 0; j < processes.active; j++)
234 if (pid == processes.data[j].pid)
235 break;
236 if (j == processes.active)
237 printf("%5u died\n", pid);
238 else if (!(flags & FLAG_HIDE_IDLE) || processes.data[j].run_count - last_processes.data[i].run_count) {
239 printf("%5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", pid,
240 NS_TO_S_D(processes.data[j].exec_time - last_processes.data[i].exec_time),
241 NS_TO_S_D(processes.data[j].delay_time - last_processes.data[i].delay_time),
242 processes.data[j].run_count - last_processes.data[i].run_count,
243 NS_TO_S_D(processes.data[j].exec_time), NS_TO_S_D(processes.data[j].delay_time),
244 processes.data[j].run_count, processes.data[j].name);
245 if (flags & FLAG_SHOW_THREADS)
246 print_threads(pid, flags);
247 }
248 }
249
250 {
251 struct thread_table tmp;
252 tmp = last_processes;
253 last_processes = processes;
254 processes = tmp;
255 processes.active = 0;
256 tmp = last_threads;
257 last_threads = threads;
258 threads = tmp;
259 threads.active = 0;
260 }
261}
262
263void
264sig_abort(int signum)
265{
266 printf("\e[?47l");
267 exit(0);
268}
269
270
271int schedtop_main(int argc, char **argv)
272{
273 int c;
274 DIR *d;
275 struct dirent *de;
276 char *namefilter = 0;
277 int pidfilter = 0;
278 uint32_t flags = 0;
279 int delay = 3000000;
280 float delay_f;
281
282 while(1) {
283 c = getopt(argc, argv, "d:ibtamun");
284 if (c == EOF)
285 break;
286 switch (c) {
287 case 'd':
288 delay_f = atof(optarg);
289 delay = delay_f * 1000000;
290 break;
291 case 'b':
292 flags |= FLAG_BATCH;
293 break;
294 case 'i':
295 flags |= FLAG_HIDE_IDLE;
296 break;
297 case 't':
298 flags |= FLAG_SHOW_THREADS;
299 break;
300 case 'a':
301 flags |= FLAG_USE_ALTERNATE_SCREEN;
302 break;
303 case 'm':
304 time_dp = 3;
305 time_div = 1000000;
306 break;
307 case 'u':
308 time_dp = 6;
309 time_div = 1000;
310 break;
311 case 'n':
312 time_dp = 9;
313 time_div = 1;
314 break;
315 }
316 }
317
318 d = opendir("/proc");
319 if(d == 0) return -1;
320
321 if (!(flags & FLAG_BATCH)) {
322 if(flags & FLAG_USE_ALTERNATE_SCREEN) {
323 signal(SIGINT, sig_abort);
324 signal(SIGPIPE, sig_abort);
325 signal(SIGTERM, sig_abort);
326 printf("\e7\e[?47h");
327 }
328 printf("\e[2J");
329 }
330 while (1) {
331 update_table(d, flags);
332 usleep(delay);
333 }
334 closedir(d);
335 return 0;
336}