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