PageRenderTime 26ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/usr/src/cmd/latencytop/common/display.c

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