|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <ctype.h> | 
|  | #include <fcntl.h> | 
|  |  | 
|  | #include <string.h> | 
|  |  | 
|  | #include <sys/stat.h> | 
|  | #include <sys/types.h> | 
|  | #include <dirent.h> | 
|  | #include <signal.h> | 
|  |  | 
|  | #include <pwd.h> | 
|  |  | 
|  | struct thread_info { | 
|  | int pid; | 
|  | int tid; | 
|  | char name[64]; | 
|  | uint64_t exec_time; | 
|  | uint64_t delay_time; | 
|  | uint32_t 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 %u", &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 %5u %5u.%0*u %5u.%0*u %7u  %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: %d, Threads %d\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; | 
|  | int tid = last_processes.data[i].tid; | 
|  | 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 %5u %5u.%0*u %5u.%0*u %7u %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; | 
|  | struct dirent *de; | 
|  | char *namefilter = 0; | 
|  | int pidfilter = 0; | 
|  | 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; | 
|  | } | 
|  |  |