schedtop.c revision dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0
1#include <stdio.h> 2#include <stdlib.h> 3#include <ctype.h> 4#include <fcntl.h> 5 6#include <string.h> 7 8#include <sys/stat.h> 9#include <sys/types.h> 10#include <dirent.h> 11#include <signal.h> 12 13#include <pwd.h> 14 15struct thread_info { 16 int pid; 17 int tid; 18 char name[64]; 19 uint64_t exec_time; 20 uint64_t delay_time; 21 uint32_t run_count; 22}; 23 24struct thread_table { 25 size_t allocated; 26 size_t active; 27 struct thread_info *data; 28}; 29 30enum { 31 FLAG_BATCH = 1U << 0, 32 FLAG_HIDE_IDLE = 1U << 1, 33 FLAG_SHOW_THREADS = 1U << 2, 34 FLAG_USE_ALTERNATE_SCREEN = 1U << 3, 35}; 36 37static int time_dp = 9; 38static int time_div = 1; 39#define NS_TO_S_D(ns) \ 40 (uint32_t)((ns) / 1000000000), time_dp, ((uint32_t)((ns) % 1000000000) / time_div) 41 42struct thread_table processes; 43struct thread_table last_processes; 44struct thread_table threads; 45struct thread_table last_threads; 46 47static void grow_table(struct thread_table *table) 48{ 49 size_t size = table->allocated; 50 struct thread_info *new_table; 51 if (size < 128) 52 size = 128; 53 else 54 size *= 2; 55 56 new_table = realloc(table->data, size * sizeof(*table->data)); 57 if (new_table == NULL) { 58 fprintf(stderr, "out of memory\n"); 59 exit(1); 60 } 61 table->data = new_table; 62 table->allocated = size; 63} 64 65static struct thread_info *get_item(struct thread_table *table) 66{ 67 if (table->active >= table->allocated) 68 grow_table(table); 69 return table->data + table->active; 70} 71 72static void commit_item(struct thread_table *table) 73{ 74 table->active++; 75} 76 77static int read_line(char *line, size_t line_size) 78{ 79 int fd; 80 int len; 81 fd = open(line, O_RDONLY); 82 if(fd == 0) 83 return -1; 84 len = read(fd, line, line_size - 1); 85 close(fd); 86 if (len <= 0) 87 return -1; 88 line[len] = '\0'; 89 return 0; 90} 91 92static void add_thread(int pid, int tid, struct thread_info *proc_info) 93{ 94 char line[1024]; 95 char *name, *name_end; 96 size_t name_len; 97 struct thread_info *info; 98 if(tid == 0) 99 info = get_item(&processes); 100 else 101 info = get_item(&threads); 102 info->pid = pid; 103 info->tid = tid; 104 105 if(tid) 106 sprintf(line, "/proc/%d/task/%d/schedstat", pid, tid); 107 else 108 sprintf(line, "/proc/%d/schedstat", pid); 109 if (read_line(line, sizeof(line))) 110 return; 111 if(sscanf(line, "%llu %llu %u", &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 153static 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 170static 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 %5u %5u.%0*u %5u.%0*u %7u %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 193static 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: %d, Threads %d\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 int tid = last_processes.data[i].tid; 231 for (j = 0; j < processes.active; j++) 232 if (pid == processes.data[j].pid) 233 break; 234 if (j == processes.active) 235 printf("%5u died\n", pid); 236 else if (!(flags & FLAG_HIDE_IDLE) || processes.data[j].run_count - last_processes.data[i].run_count) { 237 printf("%5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", pid, 238 NS_TO_S_D(processes.data[j].exec_time - last_processes.data[i].exec_time), 239 NS_TO_S_D(processes.data[j].delay_time - last_processes.data[i].delay_time), 240 processes.data[j].run_count - last_processes.data[i].run_count, 241 NS_TO_S_D(processes.data[j].exec_time), NS_TO_S_D(processes.data[j].delay_time), 242 processes.data[j].run_count, processes.data[j].name); 243 if (flags & FLAG_SHOW_THREADS) 244 print_threads(pid, flags); 245 } 246 } 247 248 { 249 struct thread_table tmp; 250 tmp = last_processes; 251 last_processes = processes; 252 processes = tmp; 253 processes.active = 0; 254 tmp = last_threads; 255 last_threads = threads; 256 threads = tmp; 257 threads.active = 0; 258 } 259} 260 261void 262sig_abort(int signum) 263{ 264 printf("\e[?47l"); 265 exit(0); 266} 267 268 269int schedtop_main(int argc, char **argv) 270{ 271 int c; 272 DIR *d; 273 struct dirent *de; 274 char *namefilter = 0; 275 int pidfilter = 0; 276 uint32_t flags = 0; 277 int delay = 3000000; 278 float delay_f; 279 280 while(1) { 281 c = getopt(argc, argv, "d:ibtamun"); 282 if (c == EOF) 283 break; 284 switch (c) { 285 case 'd': 286 delay_f = atof(optarg); 287 delay = delay_f * 1000000; 288 break; 289 case 'b': 290 flags |= FLAG_BATCH; 291 break; 292 case 'i': 293 flags |= FLAG_HIDE_IDLE; 294 break; 295 case 't': 296 flags |= FLAG_SHOW_THREADS; 297 break; 298 case 'a': 299 flags |= FLAG_USE_ALTERNATE_SCREEN; 300 break; 301 case 'm': 302 time_dp = 3; 303 time_div = 1000000; 304 break; 305 case 'u': 306 time_dp = 6; 307 time_div = 1000; 308 break; 309 case 'n': 310 time_dp = 9; 311 time_div = 1; 312 break; 313 } 314 } 315 316 d = opendir("/proc"); 317 if(d == 0) return -1; 318 319 if (!(flags & FLAG_BATCH)) { 320 if(flags & FLAG_USE_ALTERNATE_SCREEN) { 321 signal(SIGINT, sig_abort); 322 signal(SIGPIPE, sig_abort); 323 signal(SIGTERM, sig_abort); 324 printf("\e7\e[?47h"); 325 } 326 printf("\e[2J"); 327 } 328 while (1) { 329 update_table(d, flags); 330 usleep(delay); 331 } 332 closedir(d); 333 return 0; 334} 335 336