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