Merge commit '00f1a4f432b3d8aad1aa270e91c44c57f03ef407'
[unleashed.git] / usr / src / cmd / latencytop / common / display.c
blob723f9873a77808d7d78d82c85edda9a711faa887
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 2008-2009, Intel Corporation.
23 * All Rights Reserved.
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>
40 #include "latencytop.h"
42 #define LT_WINDOW_X 80
43 #define LT_WINDOW_Y 24
45 #define LT_COLOR_DEFAULT 1
46 #define LT_COLOR_HEADER 2
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;
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;
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)
75 static void
76 fill_space(char *buffer, int len, int buffer_limit, int is_right)
78 int i = 0;
79 int tofill;
81 if (len >= buffer_limit) {
82 len = buffer_limit - 1;
85 i = strlen(buffer);
87 if (i >= len) {
88 return;
91 tofill = len - i;
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);
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)
106 const double ONE_USEC = 1000.0;
107 const double ONE_MSEC = 1000000.0;
108 const double ONE_SEC = 1000000000.0;
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);
126 fill_space_left(buffer, fill_width, len);
127 return (buffer);
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)
142 * Print statistics in global/process pane. Called by print_sysglobal
143 * print_process.
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.
151 static void
152 print_statistics(WINDOW * window, int begin_line, int nlines, void *list)
154 uint64_t total;
155 int i = 0;
157 if (!display_initialized) {
158 return;
161 total = lt_stat_list_get_gtotal(list);
163 if (total == 0) {
164 return;
167 while (i < nlines && lt_stat_list_has_item(list, i)) {
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);
173 if (count == 0) {
174 continue;
177 (void) snprintf(tmp, sizeof (tmp), "%s", reason);
178 (void) mvwprintw(window, i + begin_line, 0, "%s", tmp);
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);
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));
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));
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), "--- ");
203 fill_space_left(tmp, WIDTH_PCT, sizeof (tmp));
205 (void) mvwprintw(window, i + begin_line, BEGIN_PCT,
206 "%s", tmp);
207 i++;
212 * Print statistics in global pane.
214 static void
215 print_sysglobal(void)
217 void *list;
218 char header[256];
220 if (!display_initialized) {
221 return;
224 (void) werase(sysglobal_window);
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);
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);
238 (void) wrefresh(sysglobal_window);
242 * Prints current operation mode. Mode is combination of:
244 * "Process or Thread", and "1 or 2 or 3".
246 static void
247 print_current_mode()
249 char type;
251 if (!display_initialized) {
252 return;
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;
270 (void) mvwprintw(process_window, 0, screen_width - 8, "View: %c%c",
271 type, thread_mode ? 'T' : 'P');
275 * Print process window bar when the list is empty.
277 static void
278 print_empty_process_bar()
280 char header[256];
282 if (!display_initialized) {
283 return;
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);
293 print_current_mode();
294 (void) wattroff(process_window, A_REVERSE);
296 (void) wrefresh(process_window);
300 * Print per-process statistics in process pane.
301 * This is called when mode of operation is process.
303 static void
304 print_process(unsigned int pid)
306 void *list;
307 char header[256];
308 char tmp[30];
310 if (!display_initialized) {
311 return;
314 list = lt_stat_list_create(current_list_type, LT_LEVEL_PROCESS,
315 pid, 0, 8, sort_type);
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);
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));
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);
335 (void) wrefresh(process_window);
339 * Display the list of processes that are tracked, in task bar.
340 * This one is called when mode of operation is process.
342 static void
343 print_taskbar_process(pid_t *pidlist, int pidlist_len, int pidlist_index)
345 const int ITEM_WIDTH = 8;
347 int number_item;
348 int i;
349 int xpos = 0;
351 if (!display_initialized) {
352 return;
355 number_item = (screen_width / ITEM_WIDTH) - 1;
356 i = pidlist_index - (pidlist_index % number_item);
358 (void) werase(taskbar);
360 if (i != 0) {
361 (void) mvwprintw(taskbar, 0, xpos, "<-");
364 xpos = ITEM_WIDTH / 2;
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]);
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]);
378 slen = strlen(str);
380 if (slen < ITEM_WIDTH) {
381 (void) memset(&str[slen], ' ', ITEM_WIDTH - slen);
384 str[sizeof (str) - 1] = '\0';
386 if (i == pidlist_index) {
387 (void) wattron(taskbar, A_REVERSE);
390 (void) mvwprintw(taskbar, 0, xpos, "%s", str);
392 if (i == pidlist_index) {
393 (void) wattroff(taskbar, A_REVERSE);
396 xpos += ITEM_WIDTH;
397 i++;
400 if (i != pidlist_len) {
401 (void) mvwprintw(taskbar, 0, screen_width - 2, "->");
404 (void) wrefresh(taskbar);
408 * Display the list of processes that are tracked, in task bar.
409 * This one is called when mode of operation is thread.
411 static void
412 print_taskbar_thread(pid_t *pidlist, id_t *tidlist, int list_len,
413 int list_index)
415 const int ITEM_WIDTH = 12;
417 int number_item;
418 int i;
419 int xpos = 0;
420 const char *pname = NULL;
421 pid_t last_pid = INVALID_PID;
424 if (!display_initialized) {
425 return;
428 number_item = (screen_width - 8) / ITEM_WIDTH;
429 i = list_index - (list_index % number_item);
431 (void) werase(taskbar);
433 if (i != 0) {
434 (void) mvwprintw(taskbar, 0, xpos, "<-");
437 xpos = 4;
439 while (xpos + ITEM_WIDTH <= screen_width && i < list_len) {
440 char str[ITEM_WIDTH+1];
441 int slen, tlen;
443 if (pidlist[i] != last_pid) {
444 pname = lt_stat_proc_get_name(pidlist[i]);
445 last_pid = pidlist[i];
449 * Calculate length of thread's ID; use shorter process name
450 * in order to save space on the screen.
452 tlen = snprintf(NULL, 0, "_%d", tidlist[i]);
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]);
462 slen = strlen(str);
464 (void) snprintf(&str[slen], sizeof (str) - slen,
465 "_%d", tidlist[i]);
467 slen += tlen;
469 if (slen < ITEM_WIDTH) {
470 (void) memset(&str[slen], ' ', ITEM_WIDTH - slen);
473 str[sizeof (str) - 1] = '\0';
475 if (i == list_index) {
476 (void) wattron(taskbar, A_REVERSE);
479 (void) mvwprintw(taskbar, 0, xpos, "%s", str);
481 if (i == list_index) {
482 (void) wattroff(taskbar, A_REVERSE);
485 xpos += ITEM_WIDTH;
486 i++;
489 if (i != list_len) {
490 (void) mvwprintw(taskbar, 0, screen_width - 2, "->");
493 (void) wrefresh(taskbar);
497 * Print per-thread statistics in process pane.
498 * This is called when mode of operation is thread.
500 static void
501 print_thread(pid_t pid, id_t tid)
503 void *list;
504 char header[256];
505 char tmp[30];
507 if (!display_initialized) {
508 return;
511 list = lt_stat_list_create(current_list_type, LT_LEVEL_THREAD,
512 pid, tid, 8, sort_type);
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);
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));
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);
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.
540 static void
541 print_hint(const char *hint)
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."
552 const uint64_t update_interval = 5000; /* 5 seconds */
554 static int index = 0;
555 static uint64_t next_hint = 0;
556 uint64_t now = lt_millisecond();
558 if (!display_initialized) {
559 return;
562 if (hint == NULL) {
563 if (now < next_hint) {
564 return;
567 hint = HINTS[index];
568 index = (index + 1) % (sizeof (HINTS) / sizeof (HINTS[0]));
569 next_hint = now + update_interval;
570 } else {
572 * Important messages are displayed at least every 2 cycles.
574 next_hint = now + update_interval * 2;
577 (void) werase(hintbar);
578 (void) mvwprintw(hintbar, 0, (screen_width - strlen(hint)) / 2,
579 "%s", hint);
580 (void) wrefresh(hintbar);
584 * Create a PID list or a PID/TID list (if operation mode is thread) from
585 * available statistics.
587 static void
588 get_plist(pid_t **plist, id_t **tlist, int *list_len, int *list_index)
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) {
599 if (*list_index >= *list_len) {
601 * The previously selected pid is gone.
602 * Select the first one.
604 *list_index = 0;
606 } else {
607 /* Per-thread mode */
608 *list_len = lt_stat_proc_list_create(plist, tlist);
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;
619 if (*list_index >= *list_len) {
621 * The previously selected pid/tid is gone.
622 * Select the first one.
624 for (*list_index = 0;
625 *list_index < *list_len &&
626 (*plist)[*list_index] != selected_pid;
627 ++*list_index) {
631 if (*list_index >= *list_len) {
633 * The previously selected pid is gone.
634 * Select the first one
636 *list_index = 0;
641 /* Print help message when user presses 'h' hot key */
642 static void
643 print_help(void)
645 const char *HELP[] = {
646 TITLE,
647 COPYRIGHT,
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.",
664 "Press any key to continue..."
666 int i;
668 if (!display_initialized) {
669 return;
672 for (i = 0; i < sizeof (HELP) / sizeof (HELP[0]); ++i) {
673 (void) mvwprintw(stdscr, i, 0, "%s", HELP[i]);
676 (void) refresh();
680 * Print title on screen
682 static void
683 print_title(void)
685 if (!display_initialized) {
686 return;
689 (void) wattrset(titlebar, COLOR_PAIR(LT_COLOR_HEADER));
690 (void) wbkgd(titlebar, COLOR_PAIR(LT_COLOR_HEADER));
691 (void) werase(titlebar);
693 (void) mvwprintw(titlebar, 0, (screen_width - strlen(TITLE)) / 2,
694 "%s", TITLE);
695 (void) wrefresh(titlebar);
697 (void) werase(captionbar);
698 (void) mvwprintw(captionbar, 0, 0, "%s",
699 " Cause "
700 "Count Average Maximum Percent");
701 (void) wrefresh(captionbar);
703 (void) wattrset(hintbar, COLOR_PAIR(LT_COLOR_HEADER));
704 (void) wbkgd(hintbar, COLOR_PAIR(LT_COLOR_HEADER));
708 * Handle signal from terminal resize
710 /* ARGSUSED */
711 static void
712 on_resize(int sig)
714 lt_gpipe_break("r");
718 * Initialize display. Display will be cleared when this function returns.
720 void
721 lt_display_init(void)
723 if (display_initialized) {
724 return;
727 /* Window resize signal */
728 (void) signal(SIGWINCH, on_resize);
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);
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);
743 curses_inited = TRUE;
744 getmaxyx(stdscr, screen_height, screen_width);
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;
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();
767 display_initialized = TRUE;
769 print_title();
773 * The event loop for display. It displays data on screen and handles hotkey
774 * presses.
776 * Parameter :
777 * duration - returns after 'duration'
779 * The function also returns if user presses 'q', 'Ctrl+C' or 'r'.
781 * Return value:
782 * 0 - main() exits
783 * 1 - main() calls it again
786 lt_display_loop(int duration)
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;
801 start = lt_millisecond();
802 gpipe = lt_gpipe_readfd();
804 if (!show_help) {
805 print_hint(NULL);
806 print_sysglobal();
809 get_plist(&plist, &tlist, &list_len, &list_index);
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]);
824 } else {
825 print_empty_process_bar();
829 need_refresh = TRUE; /* Usually we need refresh. */
830 remaining = duration - (int)(lt_millisecond() - start);
832 if (remaining <= 0) {
833 break;
836 /* Embedded dtrace snap action here. */
837 next_snap = lt_dtrace_work(0);
839 if (next_snap == 0) {
841 * Just did a snap, check time for the next one.
843 next_snap = lt_dtrace_work(0);
846 if (next_snap > 0 && remaining > next_snap) {
847 remaining = next_snap;
850 timeout.tv_sec = remaining / 1000;
851 timeout.tv_usec = (remaining % 1000) * 1000;
853 FD_ZERO(&read_fd);
854 FD_SET(0, &read_fd);
855 FD_SET(gpipe, &read_fd);
857 /* Wait for keyboard input, or signal from gpipe */
858 if (select(gpipe + 1, &read_fd, NULL, NULL, &timeout) > 0) {
859 int k = 0;
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();
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.
877 print_hint(NULL);
879 * If help is on display right now, and a key press
880 * happens, we need to clear the help and continue.
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;
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;
914 if (list_index < 0) {
915 list_index = 0;
918 break;
919 case '.':
920 case '>':
921 case KEY_RIGHT:
922 ++list_index;
924 if (list_index >= list_len) {
925 list_index = list_len - 1;
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];
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();
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.");
990 break;
991 default:
992 /* Wake up for nothing; no refresh is needed */
993 need_refresh = FALSE;
994 break;
996 } else {
997 need_refresh = FALSE;
1001 quit:
1002 if (plist != NULL) {
1003 selected_pid = plist[list_index];
1006 if (tlist != NULL) {
1007 selected_tid = tlist[list_index];
1010 lt_stat_proc_list_free(plist, tlist);
1012 return (retval);
1016 * Clean up display.
1018 void
1019 lt_display_deinit(void)
1021 if (curses_inited) {
1022 (void) clear();
1023 (void) refresh();
1024 (void) endwin();
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;
1036 display_initialized = FALSE;
1037 curses_inited = FALSE;
1041 * Print message when display error happens.
1043 /* ARGSUSED */
1044 void
1045 lt_display_error(const char *fmt, ...)
1047 va_list vl;
1048 char tmp[81];
1049 int l;
1051 va_start(vl, fmt);
1052 (void) vsnprintf(tmp, sizeof (tmp), fmt, vl);
1053 va_end(vl);
1055 l = strlen(tmp);
1057 while (l > 0 && (tmp[l - 1] == '\n' || tmp[l - 1] == '\r')) {
1058 tmp[l - 1] = '\0';
1059 --l;
1062 if (!display_initialized) {
1063 (void) fprintf(stderr, "%s\n", tmp);
1064 } else if (!show_help) {
1065 print_hint(tmp);