xref: /titanic_52/usr/src/cmd/latencytop/common/display.c (revision a9c12afde877c596eed286ac3f1d518246e5cd1e)
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