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