Home | History | Annotate | Download | only in toolbox
      1 #include <ctype.h>
      2 #include <dirent.h>
      3 #include <fcntl.h>
      4 #include <pwd.h>
      5 #include <signal.h>
      6 #include <stdio.h>
      7 #include <stdint.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <sys/stat.h>
     11 #include <sys/types.h>
     12 #include <unistd.h>
     13 
     14 struct thread_info {
     15     int pid;
     16     int tid;
     17     char name[64];
     18     unsigned long long exec_time;
     19     unsigned long long delay_time;
     20     unsigned long long run_count;
     21 };
     22 
     23 struct thread_table {
     24     size_t allocated;
     25     size_t active;
     26     struct thread_info *data;
     27 };
     28 
     29 enum {
     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 
     36 static int time_dp = 9;
     37 static 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 
     41 struct thread_table processes;
     42 struct thread_table last_processes;
     43 struct thread_table threads;
     44 struct thread_table last_threads;
     45 
     46 static 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 
     64 static 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 
     71 static void commit_item(struct thread_table *table)
     72 {
     73     table->active++;
     74 }
     75 
     76 static 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 
     91 static 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;
    110     if(sscanf(line, "%llu %llu %llu",
    111               &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 
    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 
    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 %5llu %5u.%0*u %5u.%0*u %7llu  %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 
    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: %zu, Threads %zu\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         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) {
    236             printf("%5u  %2u.%0*u %2u.%0*u %5llu %5u.%0*u %5u.%0*u %7llu %s\n", pid,
    237                 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 
    260 void
    261 sig_abort(int signum)
    262 {
    263     printf("\e[?47l");
    264     exit(0);
    265 }
    266 
    267 
    268 int schedtop_main(int argc, char **argv)
    269 {
    270     int c;
    271     DIR *d;
    272     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 }
    331