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
fill_space(char * buffer,int len,int buffer_limit,int is_right)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 *
get_time_string(double nanoseconds,char * buffer,int len,int fill_width)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
print_statistics(WINDOW * window,int begin_line,int nlines,void * list)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
print_sysglobal(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
print_current_mode()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
print_empty_process_bar()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
print_process(unsigned int pid)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
print_taskbar_process(pid_t * pidlist,int pidlist_len,int pidlist_index)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
print_taskbar_thread(pid_t * pidlist,id_t * tidlist,int list_len,int list_index)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
print_thread(pid_t pid,id_t tid)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
print_hint(const char * hint)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
get_plist(pid_t ** plist,id_t ** tlist,int * list_len,int * list_index)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
print_help(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
print_title(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
on_resize(int sig)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
lt_display_init(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
lt_display_loop(int duration)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
lt_display_deinit(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
lt_display_error(const char * fmt,...)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