Home | History | Annotate | Download | only in toolbox
      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 
     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 
     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 
     74 static void commit_item(struct thread_table *table)
     75 {
     76     table->active++;
     77 }
     78 
     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 
     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 
    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 
    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 
    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
    264 sig_abort(int signum)
    265 {
    266     printf("\e[?47l");
    267     exit(0);
    268 }
    269 
    270 
    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