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