• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 
7 #include <stdint.h>
8 #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 
17 struct 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 
26 struct thread_table {
27     size_t allocated;
28     size_t active;
29     struct thread_info *data;
30 };
31 
32 enum {
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 
39 static int time_dp = 9;
40 static 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 
44 struct thread_table processes;
45 struct thread_table last_processes;
46 struct thread_table threads;
47 struct thread_table last_threads;
48 
grow_table(struct thread_table * table)49 static 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 
get_item(struct thread_table * table)67 static 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 
commit_item(struct thread_table * table)74 static void commit_item(struct thread_table *table)
75 {
76     table->active++;
77 }
78 
read_line(char * line,size_t line_size)79 static 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 
add_thread(int pid,int tid,struct thread_info * proc_info)94 static 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 
add_threads(int pid,struct thread_info * proc_info)155 static 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 
print_threads(int pid,uint32_t flags)172 static 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 
update_table(DIR * d,uint32_t flags)195 static 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 
263 void
sig_abort(int signum)264 sig_abort(int signum)
265 {
266     printf("\e[?47l");
267     exit(0);
268 }
269 
270 
schedtop_main(int argc,char ** argv)271 int 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 }
337