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 printf(exit_ca_mode); 76 printf(restore_cursor); 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 printf(save_cursor); 96 printf(enter_ca_mode); 97 printf(clear_string); 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", "KERNEL", 191 "%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("%6ld %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%% TOTAL (%4.1f%% idle time, %4.1f%% unknown)", 245 (double) (gtotal / 1000), 246 (double) (utotal / 1000), 247 (double) (ktotal / 1000), 248 cpu_perc(gtotal, uinterval), 249 cpu_perc(idletime, uinterval), 250 cpu_perc(cpus * uinterval - (gtotal + idletime), uinterval)); 251 fflush(stdout); 252 printf(clear_string); 253 printf(cursor_home); 254 } 255 256 257 static int 258 adjust_term(bool onlyRows) 259 { 260 struct winsize ws; 261 262 if (ioctl(1, TIOCGWINSZ, &ws) < 0) { 263 return (0); 264 } 265 if (ws.ws_row <= 0) { 266 return (0); 267 } 268 columns = ws.ws_col; 269 rows = ws.ws_row; 270 if (onlyRows) 271 return 1; 272 273 return (1); 274 } 275 276 277 /* 278 * Gather up thread data since previous run 279 */ 280 static ThreadTimeList 281 gather(ThreadTimeList *old, int refresh) 282 { 283 int32 tmcookie; 284 int32 thcookie; 285 thread_info t; 286 team_info tm; 287 ThreadTimeList times; 288 bigtime_t oldLastMeasure; 289 290 tmcookie = 0; 291 oldLastMeasure = lastMeasure; 292 lastMeasure = system_time(); 293 294 while (get_next_team_info(&tmcookie, &tm) == B_NO_ERROR) { 295 thcookie = 0; 296 while (get_next_thread_info(tm.team, &thcookie, &t) == B_NO_ERROR) { 297 ThreadTime entry; 298 entry.thid = t.thread; 299 entry.user_time = t.user_time; 300 entry.kernel_time = t.kernel_time; 301 times.push_back(entry); 302 } 303 } 304 if (old != NULL) { 305 if (screen_size_changed) { 306 adjust_term(true); 307 screen_size_changed = 0; 308 } 309 compare(old, ×, system_time() - oldLastMeasure, refresh); 310 old->clear(); 311 } 312 return (times); 313 } 314 315 316 /* 317 * print usage message and exit 318 */ 319 static void 320 usage(const char *myname) 321 { 322 fprintf(stderr, "usage: %s [-d] [-i interval] [-n ntimes]\n", myname); 323 fprintf(stderr, 324 " -d, do not clear the screen between displays\n"); 325 fprintf(stderr, 326 " -i interval, wait `interval' seconds before displaying\n"); 327 fprintf(stderr, 328 " -n ntimes, display `ntimes' times before exiting\n"); 329 fprintf(stderr, "Default is clear screen, interval=5, ntimes=infinite\n"); 330 exit(1); 331 } 332 333 334 int 335 main(int argc, char **argv) 336 { 337 ThreadTimeList baseline; 338 int i; 339 int iters = -1; 340 int interval = 5; 341 int refresh = 1; 342 system_info sysinfo; 343 bigtime_t uinterval; 344 bigtime_t elapsed; 345 char *myname; 346 347 get_system_info (&sysinfo); 348 cpus = sysinfo.cpu_count; 349 350 myname = argv[0]; 351 for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) { 352 if (strcmp(argv[0], "-i") == 0) { 353 argc--, argv++; 354 if (argc == 0) { 355 usage(myname); 356 } 357 interval = atoi(argv[0]); 358 } else if (strcmp(argv[0], "-n") == 0) { 359 argc--, argv++; 360 if (argc == 0) { 361 usage(myname); 362 } 363 iters = atoi(argv[0]); 364 } else if (strcmp(argv[0], "-d") == 0) { 365 refresh = 0; 366 } else { 367 usage(myname); 368 } 369 } 370 if (argc) { 371 usage(myname); 372 } 373 374 init_term(); 375 376 if (refresh) { 377 if (!adjust_term(false)) { 378 refresh = 0; 379 } 380 } 381 if (iters >= 0) { 382 printf("Starting: %d interval%s of %d second%s each\n", iters, 383 (iters == 1) ? "" : "s", interval, 384 (interval == 1) ? "" : "s"); 385 } 386 387 signal(SIGWINCH, winch_handler); 388 signal(SIGINT, sigint_handler); 389 390 lastMeasure = system_time(); 391 if (iters < 0) { 392 // You will only have to wait half a second for the first iteration. 393 uinterval = 1 * 1000000 / 2; 394 baseline = gather(NULL, refresh); 395 elapsed = system_time() - lastMeasure; 396 if (elapsed < uinterval) 397 snooze(uinterval - elapsed); 398 // then = system_time(); 399 baseline = gather(&baseline, refresh); 400 401 } else 402 baseline = gather(NULL, refresh); 403 404 uinterval = interval * 1000000; 405 for (i = 0; iters < 0 || i < iters; i++) { 406 elapsed = system_time() - lastMeasure; 407 if (elapsed < uinterval) 408 snooze(uinterval - elapsed); 409 baseline = gather(&baseline, refresh); 410 } 411 412 printf(exit_ca_mode); 413 printf(restore_cursor); 414 415 exit(0); 416 } 417