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