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