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