| #include <ctype.h> | 
 | #include <dirent.h> | 
 | #include <fcntl.h> | 
 | #include <pwd.h> | 
 | #include <signal.h> | 
 | #include <stdio.h> | 
 | #include <stdint.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <sys/stat.h> | 
 | #include <sys/types.h> | 
 | #include <unistd.h> | 
 |  | 
 | struct thread_info { | 
 |     int pid; | 
 |     int tid; | 
 |     char name[64]; | 
 |     unsigned long long exec_time; | 
 |     unsigned long long delay_time; | 
 |     unsigned long long run_count; | 
 | }; | 
 |  | 
 | struct thread_table { | 
 |     size_t allocated; | 
 |     size_t active; | 
 |     struct thread_info *data; | 
 | }; | 
 |  | 
 | enum { | 
 |     FLAG_BATCH = 1U << 0, | 
 |     FLAG_HIDE_IDLE = 1U << 1, | 
 |     FLAG_SHOW_THREADS = 1U << 2, | 
 |     FLAG_USE_ALTERNATE_SCREEN = 1U << 3, | 
 | }; | 
 |  | 
 | static int time_dp = 9; | 
 | static int time_div = 1; | 
 | #define NS_TO_S_D(ns) \ | 
 |     (uint32_t)((ns) / 1000000000), time_dp, ((uint32_t)((ns) % 1000000000) / time_div) | 
 |  | 
 | struct thread_table processes; | 
 | struct thread_table last_processes; | 
 | struct thread_table threads; | 
 | struct thread_table last_threads; | 
 |  | 
 | static void grow_table(struct thread_table *table) | 
 | { | 
 |     size_t size = table->allocated; | 
 |     struct thread_info *new_table; | 
 |     if (size < 128) | 
 |         size = 128; | 
 |     else | 
 |         size *= 2; | 
 |      | 
 |     new_table = realloc(table->data, size * sizeof(*table->data)); | 
 |     if (new_table == NULL) { | 
 |         fprintf(stderr, "out of memory\n"); | 
 |         exit(1); | 
 |     } | 
 |     table->data = new_table; | 
 |     table->allocated = size; | 
 | } | 
 |  | 
 | static struct thread_info *get_item(struct thread_table *table) | 
 | { | 
 |     if (table->active >= table->allocated) | 
 |         grow_table(table); | 
 |     return table->data + table->active; | 
 | } | 
 |  | 
 | static void commit_item(struct thread_table *table) | 
 | { | 
 |     table->active++; | 
 | } | 
 |  | 
 | static int read_line(char *line, size_t line_size) | 
 | { | 
 |     int fd; | 
 |     int len; | 
 |     fd = open(line, O_RDONLY); | 
 |     if(fd == 0) | 
 |         return -1; | 
 |     len = read(fd, line, line_size - 1); | 
 |     close(fd); | 
 |     if (len <= 0) | 
 |         return -1; | 
 |     line[len] = '\0'; | 
 |     return 0; | 
 | } | 
 |  | 
 | static void add_thread(int pid, int tid, struct thread_info *proc_info) | 
 | { | 
 |     char line[1024]; | 
 |     char *name, *name_end; | 
 |     size_t name_len; | 
 |     struct thread_info *info; | 
 |     if(tid == 0) | 
 |         info = get_item(&processes); | 
 |     else | 
 |         info = get_item(&threads); | 
 |     info->pid = pid; | 
 |     info->tid = tid; | 
 |  | 
 |     if(tid) | 
 |         sprintf(line, "/proc/%d/task/%d/schedstat", pid, tid); | 
 |     else | 
 |         sprintf(line, "/proc/%d/schedstat", pid); | 
 |     if (read_line(line, sizeof(line))) | 
 |         return; | 
 |     if(sscanf(line, "%llu %llu %llu", | 
 |               &info->exec_time, &info->delay_time, &info->run_count) != 3) | 
 |         return; | 
 |     if (proc_info) { | 
 |         proc_info->exec_time += info->exec_time; | 
 |         proc_info->delay_time += info->delay_time; | 
 |         proc_info->run_count += info->run_count; | 
 |     } | 
 |  | 
 |     name = NULL; | 
 |     if (!tid) { | 
 |         sprintf(line, "/proc/%d/cmdline", pid); | 
 |         if (read_line(line, sizeof(line)) == 0 && line[0]) { | 
 |             name = line; | 
 |             name_len = strlen(name); | 
 |         } | 
 |     } | 
 |     if (!name) { | 
 |         if (tid) | 
 |             sprintf(line, "/proc/%d/task/%d/stat", pid, tid); | 
 |         else | 
 |             sprintf(line, "/proc/%d/stat", pid); | 
 |         if (read_line(line, sizeof(line))) | 
 |             return; | 
 |         name = strchr(line, '('); | 
 |         if (name == NULL) | 
 |             return; | 
 |         name_end = strchr(name, ')'); | 
 |         if (name_end == NULL) | 
 |             return; | 
 |         name++; | 
 |         name_len = name_end - name; | 
 |     } | 
 |     if (name_len >= sizeof(info->name)) | 
 |         name_len = sizeof(info->name) - 1; | 
 |     memcpy(info->name, name, name_len); | 
 |     info->name[name_len] = '\0'; | 
 |     if(tid == 0) | 
 |         commit_item(&processes); | 
 |     else | 
 |         commit_item(&threads); | 
 | } | 
 |  | 
 | static void add_threads(int pid, struct thread_info *proc_info) | 
 | { | 
 |     char path[1024]; | 
 |     DIR *d; | 
 |     struct dirent *de; | 
 |     sprintf(path, "/proc/%d/task", pid); | 
 |     d = opendir(path); | 
 |     if(d == 0) return; | 
 |     while((de = readdir(d)) != 0){ | 
 |         if(isdigit(de->d_name[0])){ | 
 |             int tid = atoi(de->d_name); | 
 |             add_thread(pid, tid, proc_info); | 
 |         } | 
 |     } | 
 |     closedir(d); | 
 | } | 
 |  | 
 | static void print_threads(int pid, uint32_t flags) | 
 | { | 
 |     size_t i, j; | 
 |     for (i = 0; i < last_threads.active; i++) { | 
 |         int epid = last_threads.data[i].pid; | 
 |         int tid = last_threads.data[i].tid; | 
 |         if (epid != pid) | 
 |             continue; | 
 |         for (j = 0; j < threads.active; j++) | 
 |             if (tid == threads.data[j].tid) | 
 |                 break; | 
 |         if (j == threads.active) | 
 |             printf(" %5u died\n", tid); | 
 |         else if (!(flags & FLAG_HIDE_IDLE) || threads.data[j].run_count - last_threads.data[i].run_count) | 
 |             printf(" %5u %2u.%0*u %2u.%0*u %5llu %5u.%0*u %5u.%0*u %7llu  %s\n", tid, | 
 |                 NS_TO_S_D(threads.data[j].exec_time - last_threads.data[i].exec_time), | 
 |                 NS_TO_S_D(threads.data[j].delay_time - last_threads.data[i].delay_time), | 
 |                 threads.data[j].run_count - last_threads.data[i].run_count, | 
 |                 NS_TO_S_D(threads.data[j].exec_time), NS_TO_S_D(threads.data[j].delay_time), | 
 |                 threads.data[j].run_count, threads.data[j].name); | 
 |     } | 
 | } | 
 |  | 
 | static void update_table(DIR *d, uint32_t flags) | 
 | { | 
 |     size_t i, j; | 
 |     struct dirent *de; | 
 |      | 
 |     rewinddir(d); | 
 |     while((de = readdir(d)) != 0){ | 
 |         if(isdigit(de->d_name[0])){ | 
 |             int pid = atoi(de->d_name); | 
 |             struct thread_info *proc_info; | 
 |             add_thread(pid, 0, NULL); | 
 |             proc_info = &processes.data[processes.active - 1]; | 
 |             proc_info->exec_time = 0; | 
 |             proc_info->delay_time = 0; | 
 |             proc_info->run_count = 0; | 
 |             add_threads(pid, proc_info); | 
 |         } | 
 |     } | 
 |     if (!(flags & FLAG_BATCH)) | 
 |         printf("\e[H\e[0J"); | 
 |     printf("Processes: %zu, Threads %zu\n", processes.active, threads.active); | 
 |     switch (time_dp) { | 
 |     case 3: | 
 |         printf("   TID --- SINCE LAST ---- ---------- TOTAL ----------\n"); | 
 |         printf("  PID  EXEC_T  DELAY SCHED EXEC_TIME DELAY_TIM   SCHED NAME\n"); | 
 |         break; | 
 |     case 6: | 
 |         printf("   TID ------ SINCE LAST -------    ------------ TOTAL -----------\n"); | 
 |         printf("  PID  EXEC_TIME DELAY_TIM SCHED    EXEC_TIME   DELAY_TIME   SCHED NAME\n"); | 
 |         break; | 
 |     default: | 
 |         printf("   TID    -------- SINCE LAST --------       ------------- TOTAL -------------\n"); | 
 |         printf("  PID     EXEC_TIME   DELAY_TIME SCHED       EXEC_TIME      DELAY_TIME   SCHED NAME\n"); | 
 |         break; | 
 |     } | 
 |     for (i = 0; i < last_processes.active; i++) { | 
 |         int pid = last_processes.data[i].pid; | 
 |         for (j = 0; j < processes.active; j++) | 
 |             if (pid == processes.data[j].pid) | 
 |                 break; | 
 |         if (j == processes.active) | 
 |             printf("%5u died\n", pid); | 
 |         else if (!(flags & FLAG_HIDE_IDLE) || processes.data[j].run_count - last_processes.data[i].run_count) { | 
 |             printf("%5u  %2u.%0*u %2u.%0*u %5llu %5u.%0*u %5u.%0*u %7llu %s\n", pid, | 
 |                 NS_TO_S_D(processes.data[j].exec_time - last_processes.data[i].exec_time), | 
 |                 NS_TO_S_D(processes.data[j].delay_time - last_processes.data[i].delay_time), | 
 |                 processes.data[j].run_count - last_processes.data[i].run_count, | 
 |                 NS_TO_S_D(processes.data[j].exec_time), NS_TO_S_D(processes.data[j].delay_time), | 
 |                 processes.data[j].run_count, processes.data[j].name); | 
 |             if (flags & FLAG_SHOW_THREADS) | 
 |                 print_threads(pid, flags); | 
 |         } | 
 |     } | 
 |  | 
 |     { | 
 |         struct thread_table tmp; | 
 |         tmp = last_processes; | 
 |         last_processes = processes; | 
 |         processes = tmp; | 
 |         processes.active = 0; | 
 |         tmp = last_threads; | 
 |         last_threads = threads; | 
 |         threads = tmp; | 
 |         threads.active = 0; | 
 |     } | 
 | } | 
 |  | 
 | void | 
 | sig_abort(int signum) | 
 | { | 
 |     printf("\e[?47l"); | 
 |     exit(0); | 
 | } | 
 |  | 
 |  | 
 | int schedtop_main(int argc, char **argv) | 
 | { | 
 |     int c; | 
 |     DIR *d; | 
 |     uint32_t flags = 0;     | 
 |     int delay = 3000000; | 
 |     float delay_f; | 
 |  | 
 |     while(1) { | 
 |         c = getopt(argc, argv, "d:ibtamun"); | 
 |         if (c == EOF) | 
 |             break; | 
 |         switch (c) { | 
 |         case 'd': | 
 |             delay_f = atof(optarg); | 
 |             delay = delay_f * 1000000; | 
 |             break; | 
 |         case 'b': | 
 |             flags |= FLAG_BATCH; | 
 |             break; | 
 |         case 'i': | 
 |             flags |= FLAG_HIDE_IDLE; | 
 |             break; | 
 |         case 't': | 
 |             flags |= FLAG_SHOW_THREADS; | 
 |             break; | 
 |         case 'a': | 
 |             flags |= FLAG_USE_ALTERNATE_SCREEN; | 
 |             break; | 
 |         case 'm': | 
 |             time_dp = 3; | 
 |             time_div = 1000000; | 
 |             break; | 
 |         case 'u': | 
 |             time_dp = 6; | 
 |             time_div = 1000; | 
 |             break; | 
 |         case 'n': | 
 |             time_dp = 9; | 
 |             time_div = 1; | 
 |             break; | 
 |         } | 
 |     } | 
 |  | 
 |     d = opendir("/proc"); | 
 |     if(d == 0) return -1; | 
 |  | 
 |     if (!(flags & FLAG_BATCH)) { | 
 |         if(flags & FLAG_USE_ALTERNATE_SCREEN) { | 
 |             signal(SIGINT, sig_abort); | 
 |             signal(SIGPIPE, sig_abort); | 
 |             signal(SIGTERM, sig_abort); | 
 |             printf("\e7\e[?47h"); | 
 |         } | 
 |         printf("\e[2J"); | 
 |     } | 
 |     while (1) { | 
 |         update_table(d, flags); | 
 |         usleep(delay); | 
 |     } | 
 |     closedir(d); | 
 |     return 0; | 
 | } |