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 per-process statistics in process pane. 276 * This is called when mode of operation is process. 277 */ 278 static void 279 print_process(unsigned int pid) 280 { 281 void *list; 282 char header[256]; 283 char tmp[30]; 284 285 if (!display_initialized) { 286 return; 287 } 288 289 list = lt_stat_list_create(current_list_type, LT_LEVEL_PROCESS, 290 pid, 0, 8, sort_type); 291 292 (void) werase(process_window); 293 (void) wattron(process_window, A_REVERSE); 294 (void) snprintf(header, sizeof (header), "Process %s (%i), %d threads", 295 lt_stat_proc_get_name(pid), pid, lt_stat_proc_get_nthreads(pid)); 296 fill_space_right(header, screen_width, sizeof (header)); 297 (void) mvwprintw(process_window, 0, 0, "%s", header); 298 299 if (current_list_type != LT_LIST_SPECIALS) { 300 (void) mvwprintw(process_window, 0, 48, "Total: %s", 301 get_time_string((double)lt_stat_list_get_gtotal(list), 302 tmp, sizeof (tmp), 12)); 303 } 304 305 print_current_mode(); 306 (void) wattroff(process_window, A_REVERSE); 307 print_statistics(process_window, 1, 8, list); 308 lt_stat_list_free(list); 309 310 (void) wrefresh(process_window); 311 } 312 313 /* 314 * Display the list of processes that are tracked, in task bar. 315 * This one is called when mode of operation is process. 316 */ 317 static void 318 print_taskbar_process(pid_t *pidlist, int pidlist_len, int pidlist_index) 319 { 320 const int ITEM_WIDTH = 8; 321 322 int number_item; 323 int i; 324 int xpos = 0; 325 326 if (!display_initialized) { 327 return; 328 } 329 330 number_item = (screen_width / ITEM_WIDTH) - 1; 331 i = pidlist_index - (pidlist_index % number_item); 332 333 (void) werase(taskbar); 334 335 if (i != 0) { 336 (void) mvwprintw(taskbar, 0, xpos, "<-"); 337 } 338 339 xpos = ITEM_WIDTH / 2; 340 341 while (xpos + ITEM_WIDTH <= screen_width && i < pidlist_len) { 342 char str[ITEM_WIDTH+1]; 343 int slen; 344 const char *pname = lt_stat_proc_get_name(pidlist[i]); 345 346 if (pname && pname[0]) { 347 (void) snprintf(str, sizeof (str) - 1, "%s", pname); 348 } else { 349 (void) snprintf(str, sizeof (str) - 1, 350 "<%d>", pidlist[i]); 351 } 352 353 slen = strlen(str); 354 355 if (slen < ITEM_WIDTH) { 356 (void) memset(&str[slen], ' ', ITEM_WIDTH - slen); 357 } 358 359 str[sizeof (str) - 1] = '\0'; 360 361 if (i == pidlist_index) { 362 (void) wattron(taskbar, A_REVERSE); 363 } 364 365 (void) mvwprintw(taskbar, 0, xpos, "%s", str); 366 367 if (i == pidlist_index) { 368 (void) wattroff(taskbar, A_REVERSE); 369 } 370 371 xpos += ITEM_WIDTH; 372 i++; 373 } 374 375 if (i != pidlist_len) { 376 (void) mvwprintw(taskbar, 0, screen_width - 2, "->"); 377 } 378 379 (void) wrefresh(taskbar); 380 } 381 382 /* 383 * Display the list of processes that are tracked, in task bar. 384 * This one is called when mode of operation is thread. 385 */ 386 static void 387 print_taskbar_thread(pid_t *pidlist, id_t *tidlist, int list_len, 388 int list_index) 389 { 390 const int ITEM_WIDTH = 12; 391 392 int number_item; 393 int i; 394 int xpos = 0; 395 const char *pname = NULL; 396 pid_t last_pid = INVALID_PID; 397 398 399 if (!display_initialized) { 400 return; 401 } 402 403 number_item = (screen_width - 8) / ITEM_WIDTH; 404 i = list_index - (list_index % number_item); 405 406 (void) werase(taskbar); 407 408 if (i != 0) { 409 (void) mvwprintw(taskbar, 0, xpos, "<-"); 410 } 411 412 xpos = 4; 413 414 while (xpos + ITEM_WIDTH <= screen_width && i < list_len) { 415 char str[ITEM_WIDTH+1]; 416 int slen, tlen; 417 418 if (pidlist[i] != last_pid) { 419 pname = lt_stat_proc_get_name(pidlist[i]); 420 last_pid = pidlist[i]; 421 } 422 423 /* 424 * Calculate length of thread's ID; use shorter process name 425 * in order to save space on the screen. 426 */ 427 tlen = snprintf(NULL, 0, "_%d", tidlist[i]); 428 429 if (pname && pname[0]) { 430 (void) snprintf(str, sizeof (str) - tlen - 1, 431 "%s", pname); 432 } else { 433 (void) snprintf(str, sizeof (str) - tlen - 1, 434 "<%d>", pidlist[i]); 435 } 436 437 slen = strlen(str); 438 439 (void) snprintf(&str[slen], sizeof (str) - slen, 440 "_%d", tidlist[i]); 441 442 slen += tlen; 443 444 if (slen < ITEM_WIDTH) { 445 (void) memset(&str[slen], ' ', ITEM_WIDTH - slen); 446 } 447 448 str[sizeof (str) - 1] = '\0'; 449 450 if (i == list_index) { 451 (void) wattron(taskbar, A_REVERSE); 452 } 453 454 (void) mvwprintw(taskbar, 0, xpos, "%s", str); 455 456 if (i == list_index) { 457 (void) wattroff(taskbar, A_REVERSE); 458 } 459 460 xpos += ITEM_WIDTH; 461 i++; 462 } 463 464 if (i != list_len) { 465 (void) mvwprintw(taskbar, 0, screen_width - 2, "->"); 466 } 467 468 (void) wrefresh(taskbar); 469 } 470 471 /* 472 * Print per-thread statistics in process pane. 473 * This is called when mode of operation is thread. 474 */ 475 static void 476 print_thread(pid_t pid, id_t tid) 477 { 478 void *list; 479 char header[256]; 480 char tmp[30]; 481 482 if (!display_initialized) { 483 return; 484 } 485 486 list = lt_stat_list_create(current_list_type, LT_LEVEL_THREAD, 487 pid, tid, 8, sort_type); 488 489 (void) werase(process_window); 490 (void) wattron(process_window, A_REVERSE); 491 (void) snprintf(header, sizeof (header), 492 "Process %s (%i), LWP %d", 493 lt_stat_proc_get_name(pid), pid, tid); 494 fill_space_right(header, screen_width, sizeof (header)); 495 (void) mvwprintw(process_window, 0, 0, "%s", header); 496 497 if (current_list_type != LT_LIST_SPECIALS) { 498 (void) mvwprintw(process_window, 0, 48, "Total: %s", 499 get_time_string( 500 (double)lt_stat_list_get_gtotal(list), 501 tmp, sizeof (tmp), 12)); 502 } 503 504 print_current_mode(); 505 (void) wattroff(process_window, A_REVERSE); 506 print_statistics(process_window, 1, 8, list); 507 lt_stat_list_free(list); 508 (void) wrefresh(process_window); 509 } 510 511 /* 512 * Update hint string at the bottom line. The message to print is stored in 513 * hint. If hint is NULL, the function will display its own message. 514 */ 515 static void 516 print_hint(const char *hint) 517 { 518 const char *HINTS[] = { 519 "Press '<' or '>' to switch between processes.", 520 "Press 'q' to exit.", 521 "Press 'r' to refresh immediately.", 522 "Press 't' to toggle Process/Thread display mode.", 523 "Press 'h' for help.", 524 "Use 'c', 'a', 'm', 'p' to change sort criteria." 525 "Use '1', '2', '3' to switch between windows." 526 }; 527 const uint64_t update_interval = 5000; /* 5 seconds */ 528 529 static int index = 0; 530 static uint64_t next_hint = 0; 531 uint64_t now = lt_millisecond(); 532 533 if (!display_initialized) { 534 return; 535 } 536 537 if (hint == NULL) { 538 if (now < next_hint) { 539 return; 540 } 541 542 hint = HINTS[index]; 543 index = (index + 1) % (sizeof (HINTS) / sizeof (HINTS[0])); 544 next_hint = now + update_interval; 545 } else { 546 /* 547 * Important messages are displayed at least every 2 cycles. 548 */ 549 next_hint = now + update_interval * 2; 550 } 551 552 (void) werase(hintbar); 553 (void) mvwprintw(hintbar, 0, (screen_width - strlen(hint)) / 2, 554 "%s", hint); 555 (void) wrefresh(hintbar); 556 } 557 558 /* 559 * Create a PID list or a PID/TID list (if operation mode is thread) from 560 * available statistics. 561 */ 562 static void 563 get_plist(pid_t **plist, id_t **tlist, int *list_len, int *list_index) 564 { 565 if (!thread_mode) { 566 /* Per-process mode */ 567 *list_len = lt_stat_proc_list_create(plist, NULL); 568 569 /* Search for previously selected PID */ 570 for (*list_index = 0; *list_index < *list_len && 571 (*plist)[*list_index] != selected_pid; 572 ++*list_index) { 573 } 574 575 if (*list_index >= *list_len) { 576 /* 577 * The previously selected pid is gone. 578 * Select the first one. 579 */ 580 *list_index = 0; 581 } 582 } else { 583 /* Per-thread mode */ 584 *list_len = lt_stat_proc_list_create(plist, tlist); 585 586 /* Search for previously selected PID & TID */ 587 for (*list_index = 0; *list_index < *list_len; 588 ++*list_index) { 589 if ((*plist)[*list_index] == selected_pid && 590 (*tlist)[*list_index] == selected_tid) { 591 break; 592 } 593 } 594 595 if (*list_index >= *list_len) { 596 /* 597 * The previously selected pid/tid is gone. 598 * Select the first one. 599 */ 600 for (*list_index = 0; 601 *list_index < *list_len && 602 (*plist)[*list_index] != selected_pid; 603 ++*list_index) { 604 } 605 } 606 607 if (*list_index >= *list_len) { 608 /* 609 * The previously selected pid is gone. 610 * Select the first one 611 */ 612 *list_index = 0; 613 } 614 } 615 } 616 617 /* Print help message when user presses 'h' hot key */ 618 static void 619 print_help(void) 620 { 621 const char *HELP[] = { 622 TITLE, 623 COPYRIGHT, 624 "", 625 "These single-character commands are available:", 626 "< - Move to previous process/thread.", 627 "> - Move to next process/thread.", 628 "q - Exit.", 629 "r - Refresh.", 630 "t - Toggle process/thread mode.", 631 "c - Sort by count.", 632 "a - Sort by average.", 633 "m - Sort by maximum.", 634 "p - Sort by percent.", 635 "1 - Show list by causes.", 636 "2 - Show list of special entries.", 637 "3 - Show list by synchronization objects.", 638 "h - Show this help.", 639 "", 640 "Press any key to continue..." 641 }; 642 int i; 643 644 if (!display_initialized) { 645 return; 646 } 647 648 for (i = 0; i < sizeof (HELP) / sizeof (HELP[0]); ++i) { 649 (void) mvwprintw(stdscr, i, 0, "%s", HELP[i]); 650 } 651 652 (void) refresh(); 653 } 654 655 /* 656 * Print title on screen 657 */ 658 static void 659 print_title(void) 660 { 661 if (!display_initialized) { 662 return; 663 } 664 665 (void) wattrset(titlebar, COLOR_PAIR(LT_COLOR_HEADER)); 666 (void) wbkgd(titlebar, COLOR_PAIR(LT_COLOR_HEADER)); 667 (void) werase(titlebar); 668 669 (void) mvwprintw(titlebar, 0, (screen_width - strlen(TITLE)) / 2, 670 "%s", TITLE); 671 (void) wrefresh(titlebar); 672 673 (void) werase(captionbar); 674 (void) mvwprintw(captionbar, 0, 0, "%s", 675 " Cause " 676 "Count Average Maximum Percent"); 677 (void) wrefresh(captionbar); 678 679 (void) wattrset(hintbar, COLOR_PAIR(LT_COLOR_HEADER)); 680 (void) wbkgd(hintbar, COLOR_PAIR(LT_COLOR_HEADER)); 681 } 682 683 /* 684 * Handle signal from terminal resize 685 */ 686 /* ARGSUSED */ 687 static void 688 on_resize(int sig) 689 { 690 lt_gpipe_break("r"); 691 } 692 693 /* 694 * Initialize display. Display will be cleared when this function returns. 695 */ 696 void 697 lt_display_init(void) 698 { 699 if (display_initialized) { 700 return; 701 } 702 703 /* Window resize signal */ 704 (void) signal(SIGWINCH, on_resize); 705 706 /* Initialize curses library */ 707 (void) initscr(); 708 (void) start_color(); 709 (void) keypad(stdscr, TRUE); 710 (void) nonl(); 711 (void) cbreak(); 712 (void) noecho(); 713 (void) curs_set(0); 714 715 /* Set up color pairs */ 716 (void) init_pair(LT_COLOR_DEFAULT, COLOR_WHITE, COLOR_BLACK); 717 (void) init_pair(LT_COLOR_HEADER, COLOR_BLACK, COLOR_WHITE); 718 719 curses_inited = TRUE; 720 getmaxyx(stdscr, screen_height, screen_width); 721 722 if (screen_width < LT_WINDOW_X || screen_height < LT_WINDOW_Y) { 723 (void) mvwprintw(stdscr, 0, 0, "Terminal size is too small."); 724 (void) mvwprintw(stdscr, 1, 0, 725 "Please resize it to 80x24 or larger."); 726 (void) mvwprintw(stdscr, 2, 0, "Press q to quit."); 727 (void) refresh(); 728 return; 729 } 730 731 /* Set up all window panes */ 732 titlebar = subwin(stdscr, 1, screen_width, 0, 0); 733 captionbar = subwin(stdscr, 1, screen_width, 1, 0); 734 sysglobal_window = subwin(stdscr, screen_height / 2 - 1, 735 screen_width, 2, 0); 736 process_window = subwin(stdscr, screen_height / 2 - 3, 737 screen_width, screen_height / 2 + 1, 0); 738 taskbar = subwin(stdscr, 1, screen_width, screen_height - 2, 0); 739 hintbar = subwin(stdscr, 1, screen_width, screen_height - 1, 0); 740 (void) werase(stdscr); 741 (void) refresh(); 742 743 display_initialized = TRUE; 744 745 print_title(); 746 } 747 748 /* 749 * The event loop for display. It displays data on screen and handles hotkey 750 * presses. 751 * 752 * Parameter : 753 * duration - returns after 'duration' 754 * 755 * The function also returns if user presses 'q', 'Ctrl+C' or 'r'. 756 * 757 * Return value: 758 * 0 - main() exits 759 * 1 - main() calls it again 760 */ 761 int 762 lt_display_loop(int duration) 763 { 764 uint64_t start; 765 int remaining; 766 struct timeval timeout; 767 fd_set read_fd; 768 int need_refresh = TRUE; 769 pid_t *plist = NULL; 770 id_t *tlist = NULL; 771 int list_len = 0; 772 int list_index = 0; 773 int retval = 1; 774 int next_snap; 775 int gpipe; 776 777 start = lt_millisecond(); 778 gpipe = lt_gpipe_readfd(); 779 780 if (!show_help) { 781 print_hint(NULL); 782 print_sysglobal(); 783 } 784 785 get_plist(&plist, &tlist, &list_len, &list_index); 786 787 for (;;) { 788 if (list_len != 0 && need_refresh && !show_help) { 789 if (!thread_mode) { 790 print_taskbar_process(plist, list_len, 791 list_index); 792 print_process(plist[list_index]); 793 } else { 794 print_taskbar_thread(plist, tlist, 795 list_len, list_index); 796 print_thread(plist[list_index], 797 tlist[list_index]); 798 } 799 } 800 801 need_refresh = TRUE; /* Usually we need refresh. */ 802 remaining = duration - (int)(lt_millisecond() - start); 803 804 if (remaining <= 0) { 805 break; 806 } 807 808 /* Embedded dtrace snap action here. */ 809 next_snap = lt_dtrace_work(0); 810 811 if (next_snap == 0) { 812 /* 813 * Just did a snap, check time for the next one. 814 */ 815 next_snap = lt_dtrace_work(0); 816 } 817 818 if (next_snap > 0 && remaining > next_snap) { 819 remaining = next_snap; 820 } 821 822 timeout.tv_sec = remaining / 1000; 823 timeout.tv_usec = (remaining % 1000) * 1000; 824 825 FD_ZERO(&read_fd); 826 FD_SET(0, &read_fd); 827 FD_SET(gpipe, &read_fd); 828 829 /* Wait for keyboard input, or signal from gpipe */ 830 if (select(gpipe + 1, &read_fd, NULL, NULL, &timeout) > 0) { 831 int k = 0; 832 833 if (FD_ISSET(gpipe, &read_fd)) { 834 /* Data from pipe has priority */ 835 char ch; 836 (void) read(gpipe, &ch, 1); 837 k = ch; /* Need this for big-endianness */ 838 } else { 839 k = getch(); 840 } 841 842 /* 843 * Check if we need to update the hint line whenever we 844 * get a chance. 845 * NOTE: current implementation depends on 846 * g_config.lt_cfg_snap_interval, but it's OK because it 847 * doesn't have to be precise. 848 */ 849 print_hint(NULL); 850 /* 851 * If help is on display right now, and a key press 852 * happens, we need to clear the help and continue. 853 */ 854 if (show_help) { 855 (void) werase(stdscr); 856 (void) refresh(); 857 print_title(); 858 print_sysglobal(); 859 show_help = FALSE; 860 /* Drop this key and continue */ 861 continue; 862 } 863 864 switch (k) { 865 case 'Q': 866 case 'q': 867 retval = 0; 868 goto quit; 869 case 'R': 870 case 'r': 871 lt_display_deinit(); 872 lt_display_init(); 873 goto quit; 874 case 'H': 875 case 'h': 876 show_help = TRUE; 877 (void) werase(stdscr); 878 (void) refresh(); 879 print_help(); 880 break; 881 case ',': 882 case '<': 883 case KEY_LEFT: 884 --list_index; 885 886 if (list_index < 0) { 887 list_index = 0; 888 } 889 890 break; 891 case '.': 892 case '>': 893 case KEY_RIGHT: 894 ++list_index; 895 896 if (list_index >= list_len) { 897 list_index = list_len - 1; 898 } 899 900 break; 901 case 'a': 902 case 'A': 903 sort_type = LT_SORT_AVG; 904 print_sysglobal(); 905 break; 906 case 'p': 907 case 'P': 908 sort_type = LT_SORT_TOTAL; 909 print_sysglobal(); 910 break; 911 case 'm': 912 case 'M': 913 sort_type = LT_SORT_MAX; 914 print_sysglobal(); 915 break; 916 case 'c': 917 case 'C': 918 sort_type = LT_SORT_COUNT; 919 print_sysglobal(); 920 break; 921 case 't': 922 case 'T': 923 if (plist != NULL) { 924 selected_pid = plist[list_index]; 925 } 926 927 selected_tid = INVALID_TID; 928 thread_mode = !thread_mode; 929 get_plist(&plist, &tlist, 930 &list_len, &list_index); 931 break; 932 case '1': 933 case '!': 934 current_list_type = LT_LIST_CAUSE; 935 print_sysglobal(); 936 break; 937 case '2': 938 case '@': 939 if (g_config.lt_cfg_low_overhead_mode) { 940 lt_display_error("Switching mode is " 941 "not available for '-f low'."); 942 } else { 943 current_list_type = LT_LIST_SPECIALS; 944 print_sysglobal(); 945 } 946 947 break; 948 case '3': 949 case '#': 950 if (g_config.lt_cfg_trace_syncobj) { 951 current_list_type = LT_LIST_SOBJ; 952 print_sysglobal(); 953 } else if (g_config.lt_cfg_low_overhead_mode) { 954 lt_display_error("Switching mode is " 955 "not available for '-f low'."); 956 } else { 957 lt_display_error("Tracing " 958 "synchronization objects is " 959 "disabled."); 960 } 961 962 break; 963 default: 964 /* Wake up for nothing; no refresh is needed */ 965 need_refresh = FALSE; 966 break; 967 } 968 } else { 969 need_refresh = FALSE; 970 } 971 } 972 973 quit: 974 if (plist != NULL) { 975 selected_pid = plist[list_index]; 976 } 977 978 if (tlist != NULL) { 979 selected_tid = tlist[list_index]; 980 } 981 982 lt_stat_proc_list_free(plist, tlist); 983 984 return (retval); 985 } 986 987 /* 988 * Clean up display. 989 */ 990 void 991 lt_display_deinit(void) 992 { 993 if (curses_inited) { 994 (void) clear(); 995 (void) refresh(); 996 (void) endwin(); 997 } 998 999 titlebar = NULL; 1000 captionbar = NULL; 1001 sysglobal_window = NULL; 1002 taskbar = NULL; 1003 process_window = NULL; 1004 hintbar = NULL; 1005 screen_width = 1; 1006 screen_height = 1; 1007 1008 display_initialized = FALSE; 1009 curses_inited = FALSE; 1010 } 1011 1012 /* 1013 * Print message when display error happens. 1014 */ 1015 /* ARGSUSED */ 1016 void 1017 lt_display_error(const char *fmt, ...) 1018 { 1019 va_list vl; 1020 char tmp[81]; 1021 int l; 1022 1023 va_start(vl, fmt); 1024 (void) vsnprintf(tmp, sizeof (tmp), fmt, vl); 1025 va_end(vl); 1026 1027 l = strlen(tmp); 1028 1029 while (l > 0 && (tmp[l - 1] == '\n' || tmp[l - 1] == '\r')) { 1030 tmp[l - 1] = '\0'; 1031 --l; 1032 } 1033 1034 if (!display_initialized) { 1035 (void) fprintf(stderr, "%s\n", tmp); 1036 } else if (!show_help) { 1037 print_hint(tmp); 1038 } 1039 1040 } 1041