1 /* 2 * Copyright (C) 1996 Be, Inc. 3 * All Rights Reserved 4 * 5 * This source code was published by Be, Inc. in the file gnu_x86.tar.gz for R3, 6 * a mirror is at http://linux.inf.elte.hu/ftp/beos/development/gnu/r3.0/ 7 * needs to link to termcap. 8 * The GPL should apply here, since AFAIK termcap is GPLed. 9 */ 10 11 /* 12 * Top -- a program for finding the top cpu-eating threads 13 */ 14 #include <signal.h> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <OS.h> 19 #include <termios.h> 20 21 #include <list> 22 23 #include "termcap.h" 24 25 static const char IDLE_NAME[] = "idle thread "; 26 static bigtime_t lastMeasure = 0; 27 28 /* 29 * Keeps track of a single thread's times 30 */ 31 struct ThreadTime { 32 thread_id thid; 33 bigtime_t user_time; 34 bigtime_t kernel_time; 35 36 bigtime_t total_time() const { 37 return user_time + kernel_time; 38 } 39 40 bool operator< (const ThreadTime& other) const { 41 return total_time() > other.total_time(); 42 } 43 }; 44 45 /* 46 * Keeps track of all the threads' times 47 */ 48 typedef std::list<ThreadTime> ThreadTimeList; 49 50 51 static char *clear_string; /*output string for clearing the screen */ 52 static char *enter_ca_mode; /* output string for switching screen buffer */ 53 static char *exit_ca_mode; /* output string for releasing screen buffer */ 54 static char *cursor_home; /* Places cursor back to (1,1) */ 55 static char *restore_cursor; 56 static char *save_cursor; 57 static int columns; /* Columns on screen */ 58 static int rows; /* how many rows on the screen */ 59 static int screen_size_changed = 0; /* tells to refresh the screen size */ 60 static int cpus; /* how many cpus we are runing on */ 61 62 /* SIGWINCH handler */ 63 static void 64 winch_handler(int notused) 65 { 66 (void) notused; 67 screen_size_changed = 1; 68 } 69 70 71 /* SIGINT handler */ 72 static void 73 sigint_handler(int) 74 { 75 tputs(exit_ca_mode, 1, putchar); 76 tputs(restore_cursor, 1, putchar); 77 exit(1); 78 } 79 80 81 static void 82 init_term() 83 { 84 static char buf[2048]; 85 char *entries = &buf[0]; 86 87 tgetent(buf, getenv("TERM")); 88 exit_ca_mode = tgetstr("te", &entries); 89 enter_ca_mode = tgetstr("ti", &entries); 90 cursor_home = tgetstr("ho", &entries); 91 clear_string = tgetstr("cl", &entries); 92 restore_cursor = tgetstr("rc", &entries); 93 save_cursor = tgetstr("sc", &entries); 94 95 tputs(save_cursor, 1, putchar); 96 tputs(enter_ca_mode, 1, putchar); 97 tputs(clear_string, 1, putchar); 98 } 99 100 101 /* 102 * Calculate the cpu percentage used by a given thread 103 * Remember: for multiple CPUs, multiply the interval by # cpus 104 * when calculating 105 */ 106 static float 107 cpu_perc(bigtime_t spent, bigtime_t interval) 108 { 109 return ((100.0 * spent) / (cpus * interval)); 110 } 111 112 113 /* 114 * Compare an old snapshot with the new one 115 */ 116 static void 117 compare( 118 ThreadTimeList *old, 119 ThreadTimeList *newList, 120 bigtime_t uinterval, 121 int refresh 122 ) 123 { 124 bigtime_t oldtime; 125 bigtime_t newtime; 126 ThreadTimeList times; 127 thread_info t; 128 team_info tm; 129 bigtime_t total; 130 bigtime_t utotal; 131 bigtime_t ktotal; 132 bigtime_t gtotal; 133 bigtime_t idletime; 134 int newthread; 135 int ignore; 136 int linecount; 137 //system_info info; 138 char *p; 139 140 /* 141 * First, merge both old and new lists, computing the differences in time 142 * Threads in only one list are dropped. 143 * Threads with no elapsed time are dropped too. 144 */ 145 gtotal = 0; 146 utotal = 0; 147 ktotal = 0; 148 ThreadTimeList::iterator it; 149 ThreadTimeList::iterator itOld; 150 ThreadTime entry; 151 152 for (it = newList->begin(); it != newList->end(); it++) { 153 newthread = 1; 154 ignore = 0; 155 for (itOld = old->begin(); itOld != old->end(); itOld++) { 156 if (itOld->thid != it->thid) { 157 continue; 158 } 159 newthread = 0; 160 oldtime = itOld->total_time(); 161 newtime = it->total_time(); 162 if (oldtime == newtime) { 163 ignore = 1; 164 break; 165 } 166 entry.thid = it->thid; 167 entry.user_time = (it->user_time - itOld->user_time); 168 entry.kernel_time = (it->kernel_time - itOld->kernel_time); 169 } 170 if (newthread) { 171 entry.thid = it->thid; 172 entry.user_time = it->user_time; 173 entry.kernel_time = it->kernel_time; 174 } 175 if (!ignore) { 176 times.push_back(entry); 177 178 total = entry.total_time(); 179 gtotal += total; 180 utotal += entry.user_time; 181 ktotal += entry.kernel_time; 182 } 183 } 184 185 /* 186 * Sort what we found and print 187 */ 188 times.sort(); 189 190 printf("%6s %7s %7s %7s %4s %16s %-16s \n", "THID", "TOTAL", "USER", 191 "KERNEL", "%CPU", "TEAM NAME", "THREAD NAME"); 192 linecount = 1; 193 idletime = 0; 194 gtotal = 0; 195 ktotal = 0; 196 utotal = 0; 197 for (it = times.begin(); it != times.end(); it++) { 198 ignore = 0; 199 if (get_thread_info(it->thid, &t) < B_NO_ERROR) { 200 strcpy(t.name, "(unknown)"); 201 strcpy(tm.args, "(unknown)"); 202 } else { 203 if (strncmp(t.name, IDLE_NAME, strlen(IDLE_NAME)) == 0) { 204 ignore++; 205 } 206 if (get_team_info(t.team, &tm) < B_NO_ERROR) { 207 strcpy(tm.args, "(unknown)"); 208 } else { 209 if ((p = strrchr(tm.args, '/'))) { 210 strlcpy(tm.args, p + 1, sizeof(tm.args)); 211 } 212 } 213 } 214 215 tm.args[16] = 0; 216 217 if (columns <= 80) 218 t.name[16] = 0; 219 else if (columns - 64 < sizeof(t.name)) 220 t.name[columns - 64] = 0; 221 222 total = it->total_time(); 223 if (ignore) { 224 idletime += total; 225 } else { 226 gtotal += total; 227 ktotal += it->kernel_time; 228 utotal += it->user_time; 229 } 230 if (!ignore && (!refresh || (linecount < (rows - 1)))) { 231 232 printf("%6" B_PRId32 " %7.2f %7.2f %7.2f %4.1f %16s %s \n", 233 it->thid, 234 total / 1000.0, 235 (double)(it->user_time / 1000), 236 (double)(it->kernel_time / 1000), 237 cpu_perc(total, uinterval), 238 tm.args, 239 t.name); 240 linecount++; 241 } 242 } 243 244 printf("------ %7.2f %7.2f %7.2f %4.1f%% " 245 "TOTAL (%4.1f%% idle time, %4.1f%% unknown)", 246 (double) (gtotal / 1000), 247 (double) (utotal / 1000), 248 (double) (ktotal / 1000), 249 cpu_perc(gtotal, uinterval), 250 cpu_perc(idletime, uinterval), 251 cpu_perc(cpus * uinterval - (gtotal + idletime), uinterval)); 252 fflush(stdout); 253 tputs(clear_string, 1, putchar); 254 tputs(cursor_home, 1, putchar); 255 } 256 257 258 static int 259 adjust_term(bool onlyRows) 260 { 261 struct winsize ws; 262 263 if (ioctl(1, TIOCGWINSZ, &ws) < 0) { 264 return (0); 265 } 266 if (ws.ws_row <= 0) { 267 return (0); 268 } 269 columns = ws.ws_col; 270 rows = ws.ws_row; 271 if (onlyRows) 272 return 1; 273 274 return (1); 275 } 276 277 278 /* 279 * Gather up thread data since previous run 280 */ 281 static ThreadTimeList 282 gather(ThreadTimeList *old, int refresh) 283 { 284 int32 tmcookie; 285 int32 thcookie; 286 thread_info t; 287 team_info tm; 288 ThreadTimeList times; 289 bigtime_t oldLastMeasure; 290 291 tmcookie = 0; 292 oldLastMeasure = lastMeasure; 293 lastMeasure = system_time(); 294 295 while (get_next_team_info(&tmcookie, &tm) == B_NO_ERROR) { 296 thcookie = 0; 297 while (get_next_thread_info(tm.team, &thcookie, &t) == B_NO_ERROR) { 298 ThreadTime entry; 299 entry.thid = t.thread; 300 entry.user_time = t.user_time; 301 entry.kernel_time = t.kernel_time; 302 times.push_back(entry); 303 } 304 } 305 if (old != NULL) { 306 if (screen_size_changed) { 307 adjust_term(true); 308 screen_size_changed = 0; 309 } 310 compare(old, ×, system_time() - oldLastMeasure, refresh); 311 old->clear(); 312 } 313 return (times); 314 } 315 316 317 /* 318 * print usage message and exit 319 */ 320 static void 321 usage(const char *myname) 322 { 323 fprintf(stderr, "usage: %s [-d] [-i interval] [-n ntimes]\n", myname); 324 fprintf(stderr, 325 " -d, do not clear the screen between displays\n"); 326 fprintf(stderr, 327 " -i interval, wait `interval' seconds before displaying\n"); 328 fprintf(stderr, 329 " -n ntimes, display `ntimes' times before exiting\n"); 330 fprintf(stderr, "Default is clear screen, interval=5, ntimes=infinite\n"); 331 exit(1); 332 } 333 334 335 int 336 main(int argc, char **argv) 337 { 338 ThreadTimeList baseline; 339 int i; 340 int iters = -1; 341 int interval = 5; 342 int refresh = 1; 343 system_info sysinfo; 344 bigtime_t uinterval; 345 bigtime_t elapsed; 346 char *myname; 347 348 get_system_info (&sysinfo); 349 cpus = sysinfo.cpu_count; 350 351 myname = argv[0]; 352 for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) { 353 if (strcmp(argv[0], "-i") == 0) { 354 argc--, argv++; 355 if (argc == 0) { 356 usage(myname); 357 } 358 interval = atoi(argv[0]); 359 } else if (strcmp(argv[0], "-n") == 0) { 360 argc--, argv++; 361 if (argc == 0) { 362 usage(myname); 363 } 364 iters = atoi(argv[0]); 365 } else if (strcmp(argv[0], "-d") == 0) { 366 refresh = 0; 367 } else { 368 usage(myname); 369 } 370 } 371 if (argc) { 372 usage(myname); 373 } 374 375 init_term(); 376 377 if (refresh) { 378 if (!adjust_term(false)) { 379 refresh = 0; 380 } 381 } 382 if (iters >= 0) { 383 printf("Starting: %d interval%s of %d second%s each\n", iters, 384 (iters == 1) ? "" : "s", interval, 385 (interval == 1) ? "" : "s"); 386 } 387 388 signal(SIGWINCH, winch_handler); 389 signal(SIGINT, sigint_handler); 390 391 lastMeasure = system_time(); 392 if (iters < 0) { 393 // You will only have to wait half a second for the first iteration. 394 uinterval = 1 * 1000000 / 2; 395 baseline = gather(NULL, refresh); 396 elapsed = system_time() - lastMeasure; 397 if (elapsed < uinterval) 398 snooze(uinterval - elapsed); 399 // then = system_time(); 400 baseline = gather(&baseline, refresh); 401 402 } else 403 baseline = gather(NULL, refresh); 404 405 uinterval = interval * 1000000; 406 for (i = 0; iters < 0 || i < iters; i++) { 407 elapsed = system_time() - lastMeasure; 408 if (elapsed < uinterval) 409 snooze(uinterval - elapsed); 410 baseline = gather(&baseline, refresh); 411 } 412 413 tputs(exit_ca_mode, 1, putchar); 414 tputs(restore_cursor, 1, putchar); 415 416 exit(0); 417 } 418