#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; }