• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include <fcntl.h>
5 
6 #include <string.h>
7 
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <dirent.h>
11 #include <signal.h>
12 
13 #include <pwd.h>
14 
15 struct thread_info {
16     int pid;
17     int tid;
18     char name[64];
19     uint64_t exec_time;
20     uint64_t delay_time;
21     uint32_t run_count;
22 };
23 
24 struct thread_table {
25     size_t allocated;
26     size_t active;
27     struct thread_info *data;
28 };
29 
30 enum {
31     FLAG_BATCH = 1U << 0,
32     FLAG_HIDE_IDLE = 1U << 1,
33     FLAG_SHOW_THREADS = 1U << 2,
34     FLAG_USE_ALTERNATE_SCREEN = 1U << 3,
35 };
36 
37 static int time_dp = 9;
38 static int time_div = 1;
39 #define NS_TO_S_D(ns) \
40     (uint32_t)((ns) / 1000000000), time_dp, ((uint32_t)((ns) % 1000000000) / time_div)
41 
42 struct thread_table processes;
43 struct thread_table last_processes;
44 struct thread_table threads;
45 struct thread_table last_threads;
46 
grow_table(struct thread_table * table)47 static void grow_table(struct thread_table *table)
48 {
49     size_t size = table->allocated;
50     struct thread_info *new_table;
51     if (size < 128)
52         size = 128;
53     else
54         size *= 2;
55 
56     new_table = realloc(table->data, size * sizeof(*table->data));
57     if (new_table == NULL) {
58         fprintf(stderr, "out of memory\n");
59         exit(1);
60     }
61     table->data = new_table;
62     table->allocated = size;
63 }
64 
get_item(struct thread_table * table)65 static struct thread_info *get_item(struct thread_table *table)
66 {
67     if (table->active >= table->allocated)
68         grow_table(table);
69     return table->data + table->active;
70 }
71 
commit_item(struct thread_table * table)72 static void commit_item(struct thread_table *table)
73 {
74     table->active++;
75 }
76 
read_line(char * line,size_t line_size)77 static int read_line(char *line, size_t line_size)
78 {
79     int fd;
80     int len;
81     fd = open(line, O_RDONLY);
82     if(fd == 0)
83         return -1;
84     len = read(fd, line, line_size - 1);
85     close(fd);
86     if (len <= 0)
87         return -1;
88     line[len] = '\0';
89     return 0;
90 }
91 
add_thread(int pid,int tid,struct thread_info * proc_info)92 static void add_thread(int pid, int tid, struct thread_info *proc_info)
93 {
94     char line[1024];
95     char *name, *name_end;
96     size_t name_len;
97     struct thread_info *info;
98     if(tid == 0)
99         info = get_item(&processes);
100     else
101         info = get_item(&threads);
102     info->pid = pid;
103     info->tid = tid;
104 
105     if(tid)
106         sprintf(line, "/proc/%d/task/%d/schedstat", pid, tid);
107     else
108         sprintf(line, "/proc/%d/schedstat", pid);
109     if (read_line(line, sizeof(line)))
110         return;
111     if(sscanf(line, "%llu %llu %u", &info->exec_time, &info->delay_time, &info->run_count) != 3)
112         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 
add_threads(int pid,struct thread_info * proc_info)153 static 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 
print_threads(int pid,uint32_t flags)170 static 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)
184             printf(" %5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u  %s\n", tid,
185                 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 
update_table(DIR * d,uint32_t flags)193 static 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");
213     printf("Processes: %d, Threads %d\n", processes.active, threads.active);
214     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;
230         int tid = last_processes.data[i].tid;
231         for (j = 0; j < processes.active; j++)
232             if (pid == processes.data[j].pid)
233                 break;
234         if (j == processes.active)
235             printf("%5u died\n", pid);
236         else if (!(flags & FLAG_HIDE_IDLE) || processes.data[j].run_count - last_processes.data[i].run_count) {
237             printf("%5u  %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", pid,
238                 NS_TO_S_D(processes.data[j].exec_time - last_processes.data[i].exec_time),
239                 NS_TO_S_D(processes.data[j].delay_time - last_processes.data[i].delay_time),
240                 processes.data[j].run_count - last_processes.data[i].run_count,
241                 NS_TO_S_D(processes.data[j].exec_time), NS_TO_S_D(processes.data[j].delay_time),
242                 processes.data[j].run_count, processes.data[j].name);
243             if (flags & FLAG_SHOW_THREADS)
244                 print_threads(pid, flags);
245         }
246     }
247 
248     {
249         struct thread_table tmp;
250         tmp = last_processes;
251         last_processes = processes;
252         processes = tmp;
253         processes.active = 0;
254         tmp = last_threads;
255         last_threads = threads;
256         threads = tmp;
257         threads.active = 0;
258     }
259 }
260 
261 void
sig_abort(int signum)262 sig_abort(int signum)
263 {
264     printf("\e[?47l");
265     exit(0);
266 }
267 
268 
schedtop_main(int argc,char ** argv)269 int schedtop_main(int argc, char **argv)
270 {
271     int c;
272     DIR *d;
273     struct dirent *de;
274     char *namefilter = 0;
275     int pidfilter = 0;
276     uint32_t flags = 0;
277     int delay = 3000000;
278     float delay_f;
279 
280     while(1) {
281         c = getopt(argc, argv, "d:ibtamun");
282         if (c == EOF)
283             break;
284         switch (c) {
285         case 'd':
286             delay_f = atof(optarg);
287             delay = delay_f * 1000000;
288             break;
289         case 'b':
290             flags |= FLAG_BATCH;
291             break;
292         case 'i':
293             flags |= FLAG_HIDE_IDLE;
294             break;
295         case 't':
296             flags |= FLAG_SHOW_THREADS;
297             break;
298         case 'a':
299             flags |= FLAG_USE_ALTERNATE_SCREEN;
300             break;
301         case 'm':
302             time_dp = 3;
303             time_div = 1000000;
304             break;
305         case 'u':
306             time_dp = 6;
307             time_div = 1000;
308             break;
309         case 'n':
310             time_dp = 9;
311             time_div = 1;
312             break;
313         }
314     }
315 
316     d = opendir("/proc");
317     if(d == 0) return -1;
318 
319     if (!(flags & FLAG_BATCH)) {
320         if(flags & FLAG_USE_ALTERNATE_SCREEN) {
321             signal(SIGINT, sig_abort);
322             signal(SIGPIPE, sig_abort);
323             signal(SIGTERM, sig_abort);
324             printf("\e7\e[?47h");
325         }
326         printf("\e[2J");
327     }
328     while (1) {
329         update_table(d, flags);
330         usleep(delay);
331     }
332     closedir(d);
333     return 0;
334 }
335 
336