schedtop.c revision dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0
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
15struct 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
24struct thread_table {
25    size_t allocated;
26    size_t active;
27    struct thread_info *data;
28};
29
30enum {
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
37static int time_dp = 9;
38static 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
42struct thread_table processes;
43struct thread_table last_processes;
44struct thread_table threads;
45struct thread_table last_threads;
46
47static 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
65static 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
72static void commit_item(struct thread_table *table)
73{
74    table->active++;
75}
76
77static 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
92static 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
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)
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
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");
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
261void
262sig_abort(int signum)
263{
264    printf("\e[?47l");
265    exit(0);
266}
267
268
269int 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