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 */
49static WINDOW	*titlebar = NULL;
50static WINDOW	*captionbar = NULL;
51static WINDOW	*sysglobal_window = NULL;
52static WINDOW	*taskbar = NULL;
53static WINDOW	*process_window = NULL;
54static WINDOW	*hintbar = NULL;
55/* Screen dimension */
56static int	screen_width = 1, screen_height = 1;
57/* Is display initialized, i.e. are window pointers set up. */
58static int	display_initialized = FALSE;
59/* Is initscr() called */
60static int	curses_inited = FALSE;
61
62/* To handle user key presses */
63static pid_t selected_pid = INVALID_PID;
64static id_t selected_tid = INVALID_TID;
65static lt_sort_t sort_type = LT_SORT_TOTAL;
66static int thread_mode = FALSE;
67/* Type of list being displayed */
68static int current_list_type = LT_LIST_CAUSE;
69static 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
75static void
76fill_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 */
103static const char *
104get_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 */
151static void
152print_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 */
214static void
215print_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 */
246static void
247print_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 */
277static void
278print_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 */
303static void
304print_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 */
342static void
343print_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 */
411static void
412print_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 */
500static void
501print_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 */
540static void
541print_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 */
587static void
588get_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 */
642static void
643print_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 */
682static void
683print_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 */
711static void
712on_resize(int sig)
713{
714	lt_gpipe_break("r");
715}
716
717/*
718 * Initialize display. Display will be cleared when this function returns.
719 */
720void
721lt_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 */
785int
786lt_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
1001quit:
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 */
1018void
1019lt_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 */
1044void
1045lt_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