1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2008-2009, Intel Corporation. 23 * All Rights Reserved. 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <unistd.h> 29 #include <string.h> 30 #include <sys/types.h> 31 #include <sys/time.h> 32 #include <dirent.h> 33 #include <curses.h> 34 #include <time.h> 35 #include <wchar.h> 36 #include <ctype.h> 37 #include <stdarg.h> 38 #include <signal.h> 39 40 #include "latencytop.h" 41 42 #define LT_WINDOW_X 80 43 #define LT_WINDOW_Y 24 44 45 #define LT_COLOR_DEFAULT 1 46 #define LT_COLOR_HEADER 2 47 48 /* Windows created by libcurses */ 49 static WINDOW *titlebar = NULL; 50 static WINDOW *captionbar = NULL; 51 static WINDOW *sysglobal_window = NULL; 52 static WINDOW *taskbar = NULL; 53 static WINDOW *process_window = NULL; 54 static WINDOW *hintbar = NULL; 55 /* Screen dimension */ 56 static int screen_width = 1, screen_height = 1; 57 /* Is display initialized, i.e. are window pointers set up. */ 58 static int display_initialized = FALSE; 59 /* Is initscr() called */ 60 static int curses_inited = FALSE; 61 62 /* To handle user key presses */ 63 static pid_t selected_pid = INVALID_PID; 64 static id_t selected_tid = INVALID_TID; 65 static lt_sort_t sort_type = LT_SORT_TOTAL; 66 static int thread_mode = FALSE; 67 /* Type of list being displayed */ 68 static int current_list_type = LT_LIST_CAUSE; 69 static int show_help = FALSE; 70 71 /* Help functions that append/prepend a blank to the given string */ 72 #define fill_space_right(a, b, c) fill_space((a), (b), (c), TRUE) 73 #define fill_space_left(a, b, c) fill_space((a), (b), (c), FALSE) 74 75 static void 76 fill_space(char *buffer, int len, int buffer_limit, int is_right) 77 { 78 int i = 0; 79 int tofill; 80 81 if (len >= buffer_limit) { 82 len = buffer_limit - 1; 83 } 84 85 i = strlen(buffer); 86 87 if (i >= len) { 88 return; 89 } 90 91 tofill = len - i; 92 93 if (is_right) { 94 (void) memset(&buffer[i], ' ', tofill); 95 buffer[len] = '\0'; 96 } else { 97 (void) memmove(&buffer[tofill], buffer, i+1); 98 (void) memset(buffer, ' ', tofill); 99 } 100 } 101 102 /* Convert the nanosecond value to a human readable string */ 103 static const char * 104 get_time_string(double nanoseconds, char *buffer, int len, int fill_width) 105 { 106 const double ONE_USEC = 1000.0; 107 const double ONE_MSEC = 1000000.0; 108 const double ONE_SEC = 1000000000.0; 109 110 if (nanoseconds < (ONE_USEC - .5)) { 111 (void) snprintf(buffer, len, "%3.1f nsec", nanoseconds); 112 } else if (nanoseconds < (ONE_MSEC - .5 * ONE_USEC)) { 113 (void) snprintf(buffer, len, 114 "%3.1f usec", nanoseconds / ONE_USEC); 115 } else if (nanoseconds < (ONE_SEC - .5 * ONE_MSEC)) { 116 (void) snprintf(buffer, len, 117 "%3.1f msec", nanoseconds / ONE_MSEC); 118 } else if (nanoseconds < 999.5 * ONE_SEC) { 119 (void) snprintf(buffer, len, 120 "%3.1f sec", nanoseconds / ONE_SEC); 121 } else { 122 (void) snprintf(buffer, len, 123 "%.0e sec", nanoseconds / ONE_SEC); 124 } 125 126 fill_space_left(buffer, fill_width, len); 127 return (buffer); 128 } 129 130 /* Used in print_statistics below */ 131 #define WIDTH_REASON_STRING 36 132 #define WIDTH_COUNT 12 133 #define WIDTH_AVG 12 134 #define WIDTH_MAX 12 135 #define WIDTH_PCT 8 136 #define BEGIN_COUNT WIDTH_REASON_STRING 137 #define BEGIN_AVG (BEGIN_COUNT + WIDTH_COUNT) 138 #define BEGIN_MAX (BEGIN_AVG + WIDTH_AVG) 139 #define BEGIN_PCT (BEGIN_MAX + WIDTH_MAX) 140 141 /* 142 * Print statistics in global/process pane. Called by print_sysglobal 143 * print_process. 144 * 145 * Parameters: 146 * window - the global or process statistics window. 147 * begin_line - where to start printing. 148 * count - how many lines should be printed. 149 * list - a stat_list. 150 */ 151 static void 152 print_statistics(WINDOW * window, int begin_line, int nlines, void *list) 153 { 154 uint64_t total; 155 int i = 0; 156 157 if (!display_initialized) { 158 return; 159 } 160 161 total = lt_stat_list_get_gtotal(list); 162 163 if (total == 0) { 164 return; 165 } 166 167 while (i < nlines && lt_stat_list_has_item(list, i)) { 168 169 char tmp[WIDTH_REASON_STRING]; 170 const char *reason = lt_stat_list_get_reason(list, i); 171 uint64_t count = lt_stat_list_get_count(list, i); 172 173 if (count == 0) { 174 continue; 175 } 176 177 (void) snprintf(tmp, sizeof (tmp), "%s", reason); 178 (void) mvwprintw(window, i + begin_line, 0, "%s", tmp); 179 180 (void) snprintf(tmp, sizeof (tmp), "%llu", count); 181 fill_space_left(tmp, WIDTH_COUNT, sizeof (tmp)); 182 (void) mvwprintw(window, i + begin_line, BEGIN_COUNT, 183 "%s", tmp); 184 185 (void) mvwprintw(window, i + begin_line, BEGIN_AVG, 186 "%s", get_time_string( 187 (double)lt_stat_list_get_sum(list, i) / count, 188 tmp, sizeof (tmp), WIDTH_AVG)); 189 190 (void) mvwprintw(window, i + begin_line, BEGIN_MAX, 191 "%s", get_time_string( 192 (double)lt_stat_list_get_max(list, i), 193 tmp, sizeof (tmp), WIDTH_MAX)); 194 195 if (LT_LIST_SPECIALS != current_list_type) { 196 (void) snprintf(tmp, sizeof (tmp), "%.1f %%", 197 (double)lt_stat_list_get_sum(list, i) 198 / total * 100.0); 199 } else { 200 (void) snprintf(tmp, sizeof (tmp), "--- "); 201 } 202 203 fill_space_left(tmp, WIDTH_PCT, sizeof (tmp)); 204 205 (void) mvwprintw(window, i + begin_line, BEGIN_PCT, 206 "%s", tmp); 207 i++; 208 } 209 } 210 211 /* 212 * Print statistics in global pane. 213 */ 214 static void 215 print_sysglobal(void) 216 { 217 void *list; 218 char header[256]; 219 220 if (!display_initialized) { 221 return; 222 } 223 224 (void) werase(sysglobal_window); 225 226 (void) wattron(sysglobal_window, A_REVERSE); 227 (void) snprintf(header, sizeof (header), 228 "%s", "System wide latencies"); 229 fill_space_right(header, screen_width, sizeof (header)); 230 (void) mvwprintw(sysglobal_window, 0, 0, "%s", header); 231 (void) wattroff(sysglobal_window, A_REVERSE); 232 233 list = lt_stat_list_create(current_list_type, 234 LT_LEVEL_GLOBAL, 0, 0, 10, sort_type); 235 print_statistics(sysglobal_window, 1, 10, list); 236 lt_stat_list_free(list); 237 238 (void) wrefresh(sysglobal_window); 239 } 240 241 /* 242 * Prints current operation mode. Mode is combination of: 243 * 244 * "Process or Thread", and "1 or 2 or 3". 245 */ 246 static void 247 print_current_mode() 248 { 249 char type; 250 251 if (!display_initialized) { 252 return; 253 } 254 255 switch (current_list_type) { 256 case LT_LIST_CAUSE: 257 type = '1'; 258 break; 259 case LT_LIST_SPECIALS: 260 type = '2'; 261 break; 262 case LT_LIST_SOBJ: 263 type = '3'; 264 break; 265 default: 266 type = '?'; 267 break; 268 } 269 270 (void) mvwprintw(process_window, 0, screen_width - 8, "View: %c%c", 271 type, thread_mode ? 'T' : 'P'); 272 } 273 274 /* 275 * Print process window bar when the list is empty. 276 */ 277 static void 278 print_empty_process_bar() 279 { 280 char header[256]; 281 282 if (!display_initialized) { 283 return; 284 } 285 286 (void) werase(process_window); 287 (void) wattron(process_window, A_REVERSE); 288 (void) snprintf(header, sizeof (header), 289 "No process/thread data is available"); 290 fill_space_right(header, screen_width, sizeof (header)); 291 (void) mvwprintw(process_window, 0, 0, "%s", header); 292 293 print_current_mode(); 294 (void) wattroff(process_window, A_REVERSE); 295 296 (void) wrefresh(process_window); 297 } 298 299 /* 300 * Print per-process statistics in process pane. 301 * This is called when mode of operation is process. 302 */ 303 static void 304 print_process(unsigned int pid) 305 { 306 void *list; 307 char header[256]; 308 char tmp[30]; 309 310 if (!display_initialized) { 311 return; 312 } 313 314 list = lt_stat_list_create(current_list_type, LT_LEVEL_PROCESS, 315 pid, 0, 8, sort_type); 316 317 (void) werase(process_window); 318 (void) wattron(process_window, A_REVERSE); 319 (void) snprintf(header, sizeof (header), "Process %s (%i), %d threads", 320 lt_stat_proc_get_name(pid), pid, lt_stat_proc_get_nthreads(pid)); 321 fill_space_right(header, screen_width, sizeof (header)); 322 (void) mvwprintw(process_window, 0, 0, "%s", header); 323 324 if (current_list_type != LT_LIST_SPECIALS) { 325 (void) mvwprintw(process_window, 0, 48, "Total: %s", 326 get_time_string((double)lt_stat_list_get_gtotal(list), 327 tmp, sizeof (tmp), 12)); 328 } 329 330 print_current_mode(); 331 (void) wattroff(process_window, A_REVERSE); 332 print_statistics(process_window, 1, 8, list); 333 lt_stat_list_free(list); 334 335 (void) wrefresh(process_window); 336 } 337 338 /* 339 * Display the list of processes that are tracked, in task bar. 340 * This one is called when mode of operation is process. 341 */ 342 static void 343 print_taskbar_process(pid_t *pidlist, int pidlist_len, int pidlist_index) 344 { 345 const int ITEM_WIDTH = 8; 346 347 int number_item; 348 int i; 349 int xpos = 0; 350 351 if (!display_initialized) { 352 return; 353 } 354 355 number_item = (screen_width / ITEM_WIDTH) - 1; 356 i = pidlist_index - (pidlist_index % number_item); 357 358 (void) werase(taskbar); 359 360 if (i != 0) { 361 (void) mvwprintw(taskbar, 0, xpos, "<-"); 362 } 363 364 xpos = ITEM_WIDTH / 2; 365 366 while (xpos + ITEM_WIDTH <= screen_width && i < pidlist_len) { 367 char str[ITEM_WIDTH+1]; 368 int slen; 369 const char *pname = lt_stat_proc_get_name(pidlist[i]); 370 371 if (pname && pname[0]) { 372 (void) snprintf(str, sizeof (str) - 1, "%s", pname); 373 } else { 374 (void) snprintf(str, sizeof (str) - 1, 375 "<%d>", pidlist[i]); 376 } 377 378 slen = strlen(str); 379 380 if (slen < ITEM_WIDTH) { 381 (void) memset(&str[slen], ' ', ITEM_WIDTH - slen); 382 } 383 384 str[sizeof (str) - 1] = '\0'; 385 386 if (i == pidlist_index) { 387 (void) wattron(taskbar, A_REVERSE); 388 } 389 390 (void) mvwprintw(taskbar, 0, xpos, "%s", str); 391 392 if (i == pidlist_index) { 393 (void) wattroff(taskbar, A_REVERSE); 394 } 395 396 xpos += ITEM_WIDTH; 397 i++; 398 } 399 400 if (i != pidlist_len) { 401 (void) mvwprintw(taskbar, 0, screen_width - 2, "->"); 402 } 403 404 (void) wrefresh(taskbar); 405 } 406 407 /* 408 * Display the list of processes that are tracked, in task bar. 409 * This one is called when mode of operation is thread. 410 */ 411 static void 412 print_taskbar_thread(pid_t *pidlist, id_t *tidlist, int list_len, 413 int list_index) 414 { 415 const int ITEM_WIDTH = 12; 416 417 int number_item; 418 int i; 419 int xpos = 0; 420 const char *pname = NULL; 421 pid_t last_pid = INVALID_PID; 422 423 424 if (!display_initialized) { 425 return; 426 } 427 428 number_item = (screen_width - 8) / ITEM_WIDTH; 429 i = list_index - (list_index % number_item); 430 431 (void) werase(taskbar); 432 433 if (i != 0) { 434 (void) mvwprintw(taskbar, 0, xpos, "<-"); 435 } 436 437 xpos = 4; 438 439 while (xpos + ITEM_WIDTH <= screen_width && i < list_len) { 440 char str[ITEM_WIDTH+1]; 441 int slen, tlen; 442 443 if (pidlist[i] != last_pid) { 444 pname = lt_stat_proc_get_name(pidlist[i]); 445 last_pid = pidlist[i]; 446 } 447 448 /* 449 * Calculate length of thread's ID; use shorter process name 450 * in order to save space on the screen. 451 */ 452 tlen = snprintf(NULL, 0, "_%d", tidlist[i]); 453 454 if (pname && pname[0]) { 455 (void) snprintf(str, sizeof (str) - tlen - 1, 456 "%s", pname); 457 } else { 458 (void) snprintf(str, sizeof (str) - tlen - 1, 459 "<%d>", pidlist[i]); 460 } 461 462 slen = strlen(str); 463 464 (void) snprintf(&str[slen], sizeof (str) - slen, 465 "_%d", tidlist[i]); 466 467 slen += tlen; 468 469 if (slen < ITEM_WIDTH) { 470 (void) memset(&str[slen], ' ', ITEM_WIDTH - slen); 471 } 472 473 str[sizeof (str) - 1] = '\0'; 474 475 if (i == list_index) { 476 (void) wattron(taskbar, A_REVERSE); 477 } 478 479 (void) mvwprintw(taskbar, 0, xpos, "%s", str); 480 481 if (i == list_index) { 482 (void) wattroff(taskbar, A_REVERSE); 483 } 484 485 xpos += ITEM_WIDTH; 486 i++; 487 } 488 489 if (i != list_len) { 490 (void) mvwprintw(taskbar, 0, screen_width - 2, "->"); 491 } 492 493 (void) wrefresh(taskbar); 494 } 495 496 /* 497 * Print per-thread statistics in process pane. 498 * This is called when mode of operation is thread. 499 */ 500 static void 501 print_thread(pid_t pid, id_t tid) 502 { 503 void *list; 504 char header[256]; 505 char tmp[30]; 506 507 if (!display_initialized) { 508 return; 509 } 510 511 list = lt_stat_list_create(current_list_type, LT_LEVEL_THREAD, 512 pid, tid, 8, sort_type); 513 514 (void) werase(process_window); 515 (void) wattron(process_window, A_REVERSE); 516 (void) snprintf(header, sizeof (header), 517 "Process %s (%i), LWP %d", 518 lt_stat_proc_get_name(pid), pid, tid); 519 fill_space_right(header, screen_width, sizeof (header)); 520 (void) mvwprintw(process_window, 0, 0, "%s", header); 521 522 if (current_list_type != LT_LIST_SPECIALS) { 523 (void) mvwprintw(process_window, 0, 48, "Total: %s", 524 get_time_string( 525 (double)lt_stat_list_get_gtotal(list), 526 tmp, sizeof (tmp), 12)); 527 } 528 529 print_current_mode(); 530 (void) wattroff(process_window, A_REVERSE); 531 print_statistics(process_window, 1, 8, list); 532 lt_stat_list_free(list); 533 (void) wrefresh(process_window); 534 } 535 536 /* 537 * Update hint string at the bottom line. The message to print is stored in 538 * hint. If hint is NULL, the function will display its own message. 539 */ 540 static void 541 print_hint(const char *hint) 542 { 543 const char *HINTS[] = { 544 "Press '<' or '>' to switch between processes.", 545 "Press 'q' to exit.", 546 "Press 'r' to refresh immediately.", 547 "Press 't' to toggle Process/Thread display mode.", 548 "Press 'h' for help.", 549 "Use 'c', 'a', 'm', 'p' to change sort criteria.", 550 "Use '1', '2', '3' to switch between windows." 551 }; 552 const uint64_t update_interval = 5000; /* 5 seconds */ 553 554 static int index = 0; 555 static uint64_t next_hint = 0; 556 uint64_t now = lt_millisecond(); 557 558 if (!display_initialized) { 559 return; 560 } 561 562 if (hint == NULL) { 563 if (now < next_hint) { 564 return; 565 } 566 567 hint = HINTS[index]; 568 index = (index + 1) % (sizeof (HINTS) / sizeof (HINTS[0])); 569 next_hint = now + update_interval; 570 } else { 571 /* 572 * Important messages are displayed at least every 2 cycles. 573 */ 574 next_hint = now + update_interval * 2; 575 } 576 577 (void) werase(hintbar); 578 (void) mvwprintw(hintbar, 0, (screen_width - strlen(hint)) / 2, 579 "%s", hint); 580 (void) wrefresh(hintbar); 581 } 582 583 /* 584 * Create a PID list or a PID/TID list (if operation mode is thread) from 585 * available statistics. 586 */ 587 static void 588 get_plist(pid_t **plist, id_t **tlist, int *list_len, int *list_index) 589 { 590 if (!thread_mode) { 591 /* Per-process mode */ 592 *list_len = lt_stat_proc_list_create(plist, NULL); 593 /* Search for previously selected PID */ 594 for (*list_index = 0; *list_index < *list_len && 595 (*plist)[*list_index] != selected_pid; 596 ++*list_index) { 597 } 598 599 if (*list_index >= *list_len) { 600 /* 601 * The previously selected pid is gone. 602 * Select the first one. 603 */ 604 *list_index = 0; 605 } 606 } else { 607 /* Per-thread mode */ 608 *list_len = lt_stat_proc_list_create(plist, tlist); 609 610 /* Search for previously selected PID & TID */ 611 for (*list_index = 0; *list_index < *list_len; 612 ++*list_index) { 613 if ((*plist)[*list_index] == selected_pid && 614 (*tlist)[*list_index] == selected_tid) { 615 break; 616 } 617 } 618 619 if (*list_index >= *list_len) { 620 /* 621 * The previously selected pid/tid is gone. 622 * Select the first one. 623 */ 624 for (*list_index = 0; 625 *list_index < *list_len && 626 (*plist)[*list_index] != selected_pid; 627 ++*list_index) { 628 } 629 } 630 631 if (*list_index >= *list_len) { 632 /* 633 * The previously selected pid is gone. 634 * Select the first one 635 */ 636 *list_index = 0; 637 } 638 } 639 } 640 641 /* Print help message when user presses 'h' hot key */ 642 static void 643 print_help(void) 644 { 645 const char *HELP[] = { 646 TITLE, 647 COPYRIGHT, 648 "", 649 "These single-character commands are available:", 650 "< - Move to previous process/thread.", 651 "> - Move to next process/thread.", 652 "q - Exit.", 653 "r - Refresh.", 654 "t - Toggle process/thread mode.", 655 "c - Sort by count.", 656 "a - Sort by average.", 657 "m - Sort by maximum.", 658 "p - Sort by percent.", 659 "1 - Show list by causes.", 660 "2 - Show list of special entries.", 661 "3 - Show list by synchronization objects.", 662 "h - Show this help.", 663 "", 664 "Press any key to continue..." 665 }; 666 int i; 667 668 if (!display_initialized) { 669 return; 670 } 671 672 for (i = 0; i < sizeof (HELP) / sizeof (HELP[0]); ++i) { 673 (void) mvwprintw(stdscr, i, 0, "%s", HELP[i]); 674 } 675 676 (void) refresh(); 677 } 678 679 /* 680 * Print title on screen 681 */ 682 static void 683 print_title(void) 684 { 685 if (!display_initialized) { 686 return; 687 } 688 689 (void) wattrset(titlebar, COLOR_PAIR(LT_COLOR_HEADER)); 690 (void) wbkgd(titlebar, COLOR_PAIR(LT_COLOR_HEADER)); 691 (void) werase(titlebar); 692 693 (void) mvwprintw(titlebar, 0, (screen_width - strlen(TITLE)) / 2, 694 "%s", TITLE); 695 (void) wrefresh(titlebar); 696 697 (void) werase(captionbar); 698 (void) mvwprintw(captionbar, 0, 0, "%s", 699 " Cause " 700 "Count Average Maximum Percent"); 701 (void) wrefresh(captionbar); 702 703 (void) wattrset(hintbar, COLOR_PAIR(LT_COLOR_HEADER)); 704 (void) wbkgd(hintbar, COLOR_PAIR(LT_COLOR_HEADER)); 705 } 706 707 /* 708 * Handle signal from terminal resize 709 */ 710 /* ARGSUSED */ 711 static void 712 on_resize(int sig) 713 { 714 lt_gpipe_break("r"); 715 } 716 717 /* 718 * Initialize display. Display will be cleared when this function returns. 719 */ 720 void 721 lt_display_init(void) 722 { 723 if (display_initialized) { 724 return; 725 } 726 727 /* Window resize signal */ 728 (void) signal(SIGWINCH, on_resize); 729 730 /* Initialize curses library */ 731 (void) initscr(); 732 (void) start_color(); 733 (void) keypad(stdscr, TRUE); 734 (void) nonl(); 735 (void) cbreak(); 736 (void) noecho(); 737 (void) curs_set(0); 738 739 /* Set up color pairs */ 740 (void) init_pair(LT_COLOR_DEFAULT, COLOR_WHITE, COLOR_BLACK); 741 (void) init_pair(LT_COLOR_HEADER, COLOR_BLACK, COLOR_WHITE); 742 743 curses_inited = TRUE; 744 getmaxyx(stdscr, screen_height, screen_width); 745 746 if (screen_width < LT_WINDOW_X || screen_height < LT_WINDOW_Y) { 747 (void) mvwprintw(stdscr, 0, 0, "Terminal size is too small."); 748 (void) mvwprintw(stdscr, 1, 0, 749 "Please resize it to 80x24 or larger."); 750 (void) mvwprintw(stdscr, 2, 0, "Press q to quit."); 751 (void) refresh(); 752 return; 753 } 754 755 /* Set up all window panes */ 756 titlebar = subwin(stdscr, 1, screen_width, 0, 0); 757 captionbar = subwin(stdscr, 1, screen_width, 1, 0); 758 sysglobal_window = subwin(stdscr, screen_height / 2 - 1, 759 screen_width, 2, 0); 760 process_window = subwin(stdscr, screen_height / 2 - 3, 761 screen_width, screen_height / 2 + 1, 0); 762 taskbar = subwin(stdscr, 1, screen_width, screen_height - 2, 0); 763 hintbar = subwin(stdscr, 1, screen_width, screen_height - 1, 0); 764 (void) werase(stdscr); 765 (void) refresh(); 766 767 display_initialized = TRUE; 768 769 print_title(); 770 } 771 772 /* 773 * The event loop for display. It displays data on screen and handles hotkey 774 * presses. 775 * 776 * Parameter : 777 * duration - returns after 'duration' 778 * 779 * The function also returns if user presses 'q', 'Ctrl+C' or 'r'. 780 * 781 * Return value: 782 * 0 - main() exits 783 * 1 - main() calls it again 784 */ 785 int 786 lt_display_loop(int duration) 787 { 788 uint64_t start; 789 int remaining; 790 struct timeval timeout; 791 fd_set read_fd; 792 int need_refresh = TRUE; 793 pid_t *plist = NULL; 794 id_t *tlist = NULL; 795 int list_len = 0; 796 int list_index = 0; 797 int retval = 1; 798 int next_snap; 799 int gpipe; 800 801 start = lt_millisecond(); 802 gpipe = lt_gpipe_readfd(); 803 804 if (!show_help) { 805 print_hint(NULL); 806 print_sysglobal(); 807 } 808 809 get_plist(&plist, &tlist, &list_len, &list_index); 810 811 for (;;) { 812 if (need_refresh && !show_help) { 813 if (list_len != 0) { 814 if (!thread_mode) { 815 print_taskbar_process(plist, list_len, 816 list_index); 817 print_process(plist[list_index]); 818 } else { 819 print_taskbar_thread(plist, tlist, 820 list_len, list_index); 821 print_thread(plist[list_index], 822 tlist[list_index]); 823 } 824 } else { 825 print_empty_process_bar(); 826 } 827 } 828 829 need_refresh = TRUE; /* Usually we need refresh. */ 830 remaining = duration - (int)(lt_millisecond() - start); 831 832 if (remaining <= 0) { 833 break; 834 } 835 836 /* Embedded dtrace snap action here. */ 837 next_snap = lt_dtrace_work(0); 838 839 if (next_snap == 0) { 840 /* 841 * Just did a snap, check time for the next one. 842 */ 843 next_snap = lt_dtrace_work(0); 844 } 845 846 if (next_snap > 0 && remaining > next_snap) { 847 remaining = next_snap; 848 } 849 850 timeout.tv_sec = remaining / 1000; 851 timeout.tv_usec = (remaining % 1000) * 1000; 852 853 FD_ZERO(&read_fd); 854 FD_SET(0, &read_fd); 855 FD_SET(gpipe, &read_fd); 856 857 /* Wait for keyboard input, or signal from gpipe */ 858 if (select(gpipe + 1, &read_fd, NULL, NULL, &timeout) > 0) { 859 int k = 0; 860 861 if (FD_ISSET(gpipe, &read_fd)) { 862 /* Data from pipe has priority */ 863 char ch; 864 (void) read(gpipe, &ch, 1); 865 k = ch; /* Need this for big-endianness */ 866 } else { 867 k = getch(); 868 } 869 870 /* 871 * Check if we need to update the hint line whenever we 872 * get a chance. 873 * NOTE: current implementation depends on 874 * g_config.lt_cfg_snap_interval, but it's OK because it 875 * doesn't have to be precise. 876 */ 877 print_hint(NULL); 878 /* 879 * If help is on display right now, and a key press 880 * happens, we need to clear the help and continue. 881 */ 882 if (show_help) { 883 (void) werase(stdscr); 884 (void) refresh(); 885 print_title(); 886 print_sysglobal(); 887 show_help = FALSE; 888 /* Drop this key and continue */ 889 continue; 890 } 891 892 switch (k) { 893 case 'Q': 894 case 'q': 895 retval = 0; 896 goto quit; 897 case 'R': 898 case 'r': 899 lt_display_deinit(); 900 lt_display_init(); 901 goto quit; 902 case 'H': 903 case 'h': 904 show_help = TRUE; 905 (void) werase(stdscr); 906 (void) refresh(); 907 print_help(); 908 break; 909 case ',': 910 case '<': 911 case KEY_LEFT: 912 --list_index; 913 914 if (list_index < 0) { 915 list_index = 0; 916 } 917 918 break; 919 case '.': 920 case '>': 921 case KEY_RIGHT: 922 ++list_index; 923 924 if (list_index >= list_len) { 925 list_index = list_len - 1; 926 } 927 928 break; 929 case 'a': 930 case 'A': 931 sort_type = LT_SORT_AVG; 932 print_sysglobal(); 933 break; 934 case 'p': 935 case 'P': 936 sort_type = LT_SORT_TOTAL; 937 print_sysglobal(); 938 break; 939 case 'm': 940 case 'M': 941 sort_type = LT_SORT_MAX; 942 print_sysglobal(); 943 break; 944 case 'c': 945 case 'C': 946 sort_type = LT_SORT_COUNT; 947 print_sysglobal(); 948 break; 949 case 't': 950 case 'T': 951 if (plist != NULL) { 952 selected_pid = plist[list_index]; 953 } 954 955 selected_tid = INVALID_TID; 956 thread_mode = !thread_mode; 957 get_plist(&plist, &tlist, 958 &list_len, &list_index); 959 break; 960 case '1': 961 case '!': 962 current_list_type = LT_LIST_CAUSE; 963 print_sysglobal(); 964 break; 965 case '2': 966 case '@': 967 if (g_config.lt_cfg_low_overhead_mode) { 968 lt_display_error("Switching mode is " 969 "not available for '-f low'."); 970 } else { 971 current_list_type = LT_LIST_SPECIALS; 972 print_sysglobal(); 973 } 974 975 break; 976 case '3': 977 case '#': 978 if (g_config.lt_cfg_trace_syncobj) { 979 current_list_type = LT_LIST_SOBJ; 980 print_sysglobal(); 981 } else if (g_config.lt_cfg_low_overhead_mode) { 982 lt_display_error("Switching mode is " 983 "not available for '-f low'."); 984 } else { 985 lt_display_error("Tracing " 986 "synchronization objects is " 987 "disabled."); 988 } 989 990 break; 991 default: 992 /* Wake up for nothing; no refresh is needed */ 993 need_refresh = FALSE; 994 break; 995 } 996 } else { 997 need_refresh = FALSE; 998 } 999 } 1000 1001 quit: 1002 if (plist != NULL) { 1003 selected_pid = plist[list_index]; 1004 } 1005 1006 if (tlist != NULL) { 1007 selected_tid = tlist[list_index]; 1008 } 1009 1010 lt_stat_proc_list_free(plist, tlist); 1011 1012 return (retval); 1013 } 1014 1015 /* 1016 * Clean up display. 1017 */ 1018 void 1019 lt_display_deinit(void) 1020 { 1021 if (curses_inited) { 1022 (void) clear(); 1023 (void) refresh(); 1024 (void) endwin(); 1025 } 1026 1027 titlebar = NULL; 1028 captionbar = NULL; 1029 sysglobal_window = NULL; 1030 taskbar = NULL; 1031 process_window = NULL; 1032 hintbar = NULL; 1033 screen_width = 1; 1034 screen_height = 1; 1035 1036 display_initialized = FALSE; 1037 curses_inited = FALSE; 1038 } 1039 1040 /* 1041 * Print message when display error happens. 1042 */ 1043 /* ARGSUSED */ 1044 void 1045 lt_display_error(const char *fmt, ...) 1046 { 1047 va_list vl; 1048 char tmp[81]; 1049 int l; 1050 1051 va_start(vl, fmt); 1052 (void) vsnprintf(tmp, sizeof (tmp), fmt, vl); 1053 va_end(vl); 1054 1055 l = strlen(tmp); 1056 1057 while (l > 0 && (tmp[l - 1] == '\n' || tmp[l - 1] == '\r')) { 1058 tmp[l - 1] = '\0'; 1059 --l; 1060 } 1061 1062 if (!display_initialized) { 1063 (void) fprintf(stderr, "%s\n", tmp); 1064 } else if (!show_help) { 1065 print_hint(tmp); 1066 } 1067 1068 } 1069