display.c revision 15db28971f91c98efb449aebf46024ac72779fa3
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2008-2009, Intel Corporation.
* All Rights Reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <dirent.h>
#include <curses.h>
#include <time.h>
#include <wchar.h>
#include <ctype.h>
#include <stdarg.h>
#include <signal.h>
#include "latencytop.h"
#define LT_WINDOW_X 80
#define LT_WINDOW_Y 24
#define LT_COLOR_DEFAULT 1
#define LT_COLOR_HEADER 2
/* Windows created by libcurses */
static WINDOW *titlebar = NULL;
static WINDOW *captionbar = NULL;
static WINDOW *sysglobal_window = NULL;
static WINDOW *taskbar = NULL;
static WINDOW *process_window = NULL;
static WINDOW *hintbar = NULL;
/* Screen dimension */
static int screen_width = 1, screen_height = 1;
/* Is display initialized, i.e. are window pointers set up. */
static int display_initialized = FALSE;
/* Is initscr() called */
static int curses_inited = FALSE;
/* To handle user key presses */
static pid_t selected_pid = INVALID_PID;
static id_t selected_tid = INVALID_TID;
static lt_sort_t sort_type = LT_SORT_TOTAL;
static int thread_mode = FALSE;
/* Type of list being displayed */
static int current_list_type = LT_LIST_CAUSE;
static int show_help = FALSE;
/* Help functions that append/prepend a blank to the given string */
#define fill_space_right(a, b, c) fill_space((a), (b), (c), TRUE)
#define fill_space_left(a, b, c) fill_space((a), (b), (c), FALSE)
static void
fill_space(char *buffer, int len, int buffer_limit, int is_right)
{
int i = 0;
int tofill;
if (len >= buffer_limit) {
len = buffer_limit - 1;
}
i = strlen(buffer);
if (i >= len) {
return;
}
tofill = len - i;
if (is_right) {
(void) memset(&buffer[i], ' ', tofill);
buffer[len] = '\0';
} else {
(void) memmove(&buffer[tofill], buffer, i+1);
(void) memset(buffer, ' ', tofill);
}
}
/* Convert the nanosecond value to a human readable string */
static const char *
get_time_string(double nanoseconds, char *buffer, int len, int fill_width)
{
const double ONE_USEC = 1000.0;
const double ONE_MSEC = 1000000.0;
const double ONE_SEC = 1000000000.0;
if (nanoseconds < (ONE_USEC - .5)) {
(void) snprintf(buffer, len, "%3.1f nsec", nanoseconds);
} else if (nanoseconds < (ONE_MSEC - .5 * ONE_USEC)) {
(void) snprintf(buffer, len,
"%3.1f usec", nanoseconds / ONE_USEC);
} else if (nanoseconds < (ONE_SEC - .5 * ONE_MSEC)) {
(void) snprintf(buffer, len,
"%3.1f msec", nanoseconds / ONE_MSEC);
} else if (nanoseconds < 999.5 * ONE_SEC) {
(void) snprintf(buffer, len,
"%3.1f sec", nanoseconds / ONE_SEC);
} else {
(void) snprintf(buffer, len,
"%.0e sec", nanoseconds / ONE_SEC);
}
fill_space_left(buffer, fill_width, len);
return (buffer);
}
/* Used in print_statistics below */
#define WIDTH_REASON_STRING 36
#define WIDTH_COUNT 12
#define WIDTH_AVG 12
#define WIDTH_MAX 12
#define WIDTH_PCT 8
#define BEGIN_COUNT WIDTH_REASON_STRING
#define BEGIN_AVG (BEGIN_COUNT + WIDTH_COUNT)
#define BEGIN_MAX (BEGIN_AVG + WIDTH_AVG)
#define BEGIN_PCT (BEGIN_MAX + WIDTH_MAX)
/*
* Print statistics in global/process pane. Called by print_sysglobal
* print_process.
*
* Parameters:
* window - the global or process statistics window.
* begin_line - where to start printing.
* count - how many lines should be printed.
* list - a stat_list.
*/
static void
print_statistics(WINDOW * window, int begin_line, int nlines, void *list)
{
uint64_t total;
int i = 0;
if (!display_initialized) {
return;
}
total = lt_stat_list_get_gtotal(list);
if (total == 0) {
return;
}
while (i < nlines && lt_stat_list_has_item(list, i)) {
char tmp[WIDTH_REASON_STRING];
const char *reason = lt_stat_list_get_reason(list, i);
uint64_t count = lt_stat_list_get_count(list, i);
if (count == 0) {
continue;
}
(void) snprintf(tmp, sizeof (tmp), "%s", reason);
(void) mvwprintw(window, i + begin_line, 0, "%s", tmp);
(void) snprintf(tmp, sizeof (tmp), "%llu", count);
fill_space_left(tmp, WIDTH_COUNT, sizeof (tmp));
(void) mvwprintw(window, i + begin_line, BEGIN_COUNT,
"%s", tmp);
(void) mvwprintw(window, i + begin_line, BEGIN_AVG,
"%s", get_time_string(
(double)lt_stat_list_get_sum(list, i) / count,
tmp, sizeof (tmp), WIDTH_AVG));
(void) mvwprintw(window, i + begin_line, BEGIN_MAX,
"%s", get_time_string(
(double)lt_stat_list_get_max(list, i),
tmp, sizeof (tmp), WIDTH_MAX));
if (LT_LIST_SPECIALS != current_list_type) {
(void) snprintf(tmp, sizeof (tmp), "%.1f %%",
(double)lt_stat_list_get_sum(list, i)
/ total * 100.0);
} else {
(void) snprintf(tmp, sizeof (tmp), "--- ");
}
fill_space_left(tmp, WIDTH_PCT, sizeof (tmp));
(void) mvwprintw(window, i + begin_line, BEGIN_PCT,
"%s", tmp);
i++;
}
}
/*
* Print statistics in global pane.
*/
static void
print_sysglobal(void)
{
void *list;
char header[256];
if (!display_initialized) {
return;
}
(void) werase(sysglobal_window);
(void) wattron(sysglobal_window, A_REVERSE);
(void) snprintf(header, sizeof (header),
"%s", "System wide latencies");
fill_space_right(header, screen_width, sizeof (header));
(void) mvwprintw(sysglobal_window, 0, 0, "%s", header);
(void) wattroff(sysglobal_window, A_REVERSE);
list = lt_stat_list_create(current_list_type,
LT_LEVEL_GLOBAL, 0, 0, 10, sort_type);
print_statistics(sysglobal_window, 1, 10, list);
lt_stat_list_free(list);
(void) wrefresh(sysglobal_window);
}
/*
* Prints current operation mode. Mode is combination of:
*
* "Process or Thread", and "1 or 2 or 3".
*/
static void
print_current_mode()
{
char type;
if (!display_initialized) {
return;
}
switch (current_list_type) {
case LT_LIST_CAUSE:
type = '1';
break;
case LT_LIST_SPECIALS:
type = '2';
break;
case LT_LIST_SOBJ:
type = '3';
break;
default:
type = '?';
break;
}
(void) mvwprintw(process_window, 0, screen_width - 8, "View: %c%c",
type, thread_mode ? 'T' : 'P');
}
/*
* Print per-process statistics in process pane.
* This is called when mode of operation is process.
*/
static void
print_process(unsigned int pid)
{
void *list;
char header[256];
char tmp[30];
if (!display_initialized) {
return;
}
list = lt_stat_list_create(current_list_type, LT_LEVEL_PROCESS,
pid, 0, 8, sort_type);
(void) werase(process_window);
(void) wattron(process_window, A_REVERSE);
(void) snprintf(header, sizeof (header), "Process %s (%i), %d threads",
lt_stat_proc_get_name(pid), pid, lt_stat_proc_get_nthreads(pid));
fill_space_right(header, screen_width, sizeof (header));
(void) mvwprintw(process_window, 0, 0, "%s", header);
if (current_list_type != LT_LIST_SPECIALS) {
(void) mvwprintw(process_window, 0, 48, "Total: %s",
get_time_string((double)lt_stat_list_get_gtotal(list),
tmp, sizeof (tmp), 12));
}
print_current_mode();
(void) wattroff(process_window, A_REVERSE);
print_statistics(process_window, 1, 8, list);
lt_stat_list_free(list);
(void) wrefresh(process_window);
}
/*
* Display the list of processes that are tracked, in task bar.
* This one is called when mode of operation is process.
*/
static void
print_taskbar_process(pid_t *pidlist, int pidlist_len, int pidlist_index)
{
const int ITEM_WIDTH = 8;
int number_item;
int i;
int xpos = 0;
if (!display_initialized) {
return;
}
number_item = (screen_width / ITEM_WIDTH) - 1;
i = pidlist_index - (pidlist_index % number_item);
(void) werase(taskbar);
if (i != 0) {
(void) mvwprintw(taskbar, 0, xpos, "<-");
}
xpos = ITEM_WIDTH / 2;
while (xpos + ITEM_WIDTH <= screen_width && i < pidlist_len) {
char str[ITEM_WIDTH+1];
int slen;
const char *pname = lt_stat_proc_get_name(pidlist[i]);
if (pname && pname[0]) {
(void) snprintf(str, sizeof (str) - 1, "%s", pname);
} else {
(void) snprintf(str, sizeof (str) - 1,
"<%d>", pidlist[i]);
}
slen = strlen(str);
if (slen < ITEM_WIDTH) {
(void) memset(&str[slen], ' ', ITEM_WIDTH - slen);
}
str[sizeof (str) - 1] = '\0';
if (i == pidlist_index) {
(void) wattron(taskbar, A_REVERSE);
}
(void) mvwprintw(taskbar, 0, xpos, "%s", str);
if (i == pidlist_index) {
(void) wattroff(taskbar, A_REVERSE);
}
xpos += ITEM_WIDTH;
i++;
}
if (i != pidlist_len) {
(void) mvwprintw(taskbar, 0, screen_width - 2, "->");
}
(void) wrefresh(taskbar);
}
/*
* Display the list of processes that are tracked, in task bar.
* This one is called when mode of operation is thread.
*/
static void
print_taskbar_thread(pid_t *pidlist, id_t *tidlist, int list_len,
int list_index)
{
const int ITEM_WIDTH = 12;
int number_item;
int i;
int xpos = 0;
const char *pname = NULL;
pid_t last_pid = INVALID_PID;
if (!display_initialized) {
return;
}
number_item = (screen_width - 8) / ITEM_WIDTH;
i = list_index - (list_index % number_item);
(void) werase(taskbar);
if (i != 0) {
(void) mvwprintw(taskbar, 0, xpos, "<-");
}
xpos = 4;
while (xpos + ITEM_WIDTH <= screen_width && i < list_len) {
char str[ITEM_WIDTH+1];
int slen, tlen;
if (pidlist[i] != last_pid) {
pname = lt_stat_proc_get_name(pidlist[i]);
last_pid = pidlist[i];
}
/*
* Calculate length of thread's ID; use shorter process name
* in order to save space on the screen.
*/
tlen = snprintf(NULL, 0, "_%d", tidlist[i]);
if (pname && pname[0]) {
(void) snprintf(str, sizeof (str) - tlen - 1,
"%s", pname);
} else {
(void) snprintf(str, sizeof (str) - tlen - 1,
"<%d>", pidlist[i]);
}
slen = strlen(str);
(void) snprintf(&str[slen], sizeof (str) - slen,
"_%d", tidlist[i]);
slen += tlen;
if (slen < ITEM_WIDTH) {
(void) memset(&str[slen], ' ', ITEM_WIDTH - slen);
}
str[sizeof (str) - 1] = '\0';
if (i == list_index) {
(void) wattron(taskbar, A_REVERSE);
}
(void) mvwprintw(taskbar, 0, xpos, "%s", str);
if (i == list_index) {
(void) wattroff(taskbar, A_REVERSE);
}
xpos += ITEM_WIDTH;
i++;
}
if (i != list_len) {
(void) mvwprintw(taskbar, 0, screen_width - 2, "->");
}
(void) wrefresh(taskbar);
}
/*
* Print per-thread statistics in process pane.
* This is called when mode of operation is thread.
*/
static void
print_thread(pid_t pid, id_t tid)
{
void *list;
char header[256];
char tmp[30];
if (!display_initialized) {
return;
}
list = lt_stat_list_create(current_list_type, LT_LEVEL_THREAD,
pid, tid, 8, sort_type);
(void) werase(process_window);
(void) wattron(process_window, A_REVERSE);
(void) snprintf(header, sizeof (header),
"Process %s (%i), LWP %d",
lt_stat_proc_get_name(pid), pid, tid);
fill_space_right(header, screen_width, sizeof (header));
(void) mvwprintw(process_window, 0, 0, "%s", header);
if (current_list_type != LT_LIST_SPECIALS) {
(void) mvwprintw(process_window, 0, 48, "Total: %s",
get_time_string(
(double)lt_stat_list_get_gtotal(list),
tmp, sizeof (tmp), 12));
}
print_current_mode();
(void) wattroff(process_window, A_REVERSE);
print_statistics(process_window, 1, 8, list);
lt_stat_list_free(list);
(void) wrefresh(process_window);
}
/*
* Update hint string at the bottom line. The message to print is stored in
* hint. If hint is NULL, the function will display its own message.
*/
static void
print_hint(const char *hint)
{
const char *HINTS[] = {
"Press '<' or '>' to switch between processes.",
"Press 'q' to exit.",
"Press 'r' to refresh immediately.",
"Press 't' to toggle Process/Thread display mode.",
"Press 'h' for help.",
"Use 'c', 'a', 'm', 'p' to change sort criteria."
"Use '1', '2', '3' to switch between windows."
};
const uint64_t update_interval = 5000; /* 5 seconds */
static int index = 0;
static uint64_t next_hint = 0;
uint64_t now = lt_millisecond();
if (!display_initialized) {
return;
}
if (hint == NULL) {
if (now < next_hint) {
return;
}
hint = HINTS[index];
index = (index + 1) % (sizeof (HINTS) / sizeof (HINTS[0]));
next_hint = now + update_interval;
} else {
/*
* Important messages are displayed at least every 2 cycles.
*/
next_hint = now + update_interval * 2;
}
(void) werase(hintbar);
(void) mvwprintw(hintbar, 0, (screen_width - strlen(hint)) / 2,
"%s", hint);
(void) wrefresh(hintbar);
}
/*
* Create a PID list or a PID/TID list (if operation mode is thread) from
* available statistics.
*/
static void
get_plist(pid_t **plist, id_t **tlist, int *list_len, int *list_index)
{
if (!thread_mode) {
/* Per-process mode */
*list_len = lt_stat_proc_list_create(plist, NULL);
/* Search for previously selected PID */
for (*list_index = 0; *list_index < *list_len &&
(*plist)[*list_index] != selected_pid;
++*list_index) {
}
if (*list_index >= *list_len) {
/*
* The previously selected pid is gone.
* Select the first one.
*/
*list_index = 0;
}
} else {
/* Per-thread mode */
*list_len = lt_stat_proc_list_create(plist, tlist);
/* Search for previously selected PID & TID */
for (*list_index = 0; *list_index < *list_len;
++*list_index) {
if ((*plist)[*list_index] == selected_pid &&
(*tlist)[*list_index] == selected_tid) {
break;
}
}
if (*list_index >= *list_len) {
/*
* The previously selected pid/tid is gone.
* Select the first one.
*/
for (*list_index = 0;
*list_index < *list_len &&
(*plist)[*list_index] != selected_pid;
++*list_index) {
}
}
if (*list_index >= *list_len) {
/*
* The previously selected pid is gone.
* Select the first one
*/
*list_index = 0;
}
}
}
/* Print help message when user presses 'h' hot key */
static void
print_help(void)
{
const char *HELP[] = {
TITLE,
COPYRIGHT,
"",
"These single-character commands are available:",
"< - Move to previous process/thread.",
"> - Move to next process/thread.",
"q - Exit.",
"r - Refresh.",
"t - Toggle process/thread mode.",
"c - Sort by count.",
"a - Sort by average.",
"m - Sort by maximum.",
"p - Sort by percent.",
"1 - Show list by causes.",
"2 - Show list of special entries.",
"3 - Show list by synchronization objects.",
"h - Show this help.",
"",
"Press any key to continue..."
};
int i;
if (!display_initialized) {
return;
}
for (i = 0; i < sizeof (HELP) / sizeof (HELP[0]); ++i) {
(void) mvwprintw(stdscr, i, 0, "%s", HELP[i]);
}
(void) refresh();
}
/*
* Print title on screen
*/
static void
print_title(void)
{
if (!display_initialized) {
return;
}
(void) wattrset(titlebar, COLOR_PAIR(LT_COLOR_HEADER));
(void) wbkgd(titlebar, COLOR_PAIR(LT_COLOR_HEADER));
(void) werase(titlebar);
(void) mvwprintw(titlebar, 0, (screen_width - strlen(TITLE)) / 2,
"%s", TITLE);
(void) wrefresh(titlebar);
(void) werase(captionbar);
(void) mvwprintw(captionbar, 0, 0, "%s",
" Cause "
"Count Average Maximum Percent");
(void) wrefresh(captionbar);
(void) wattrset(hintbar, COLOR_PAIR(LT_COLOR_HEADER));
(void) wbkgd(hintbar, COLOR_PAIR(LT_COLOR_HEADER));
}
/*
* Handle signal from terminal resize
*/
/* ARGSUSED */
static void
on_resize(int sig)
{
lt_gpipe_break("r");
}
/*
* Initialize display. Display will be cleared when this function returns.
*/
void
lt_display_init(void)
{
if (display_initialized) {
return;
}
/* Window resize signal */
(void) signal(SIGWINCH, on_resize);
/* Initialize curses library */
(void) initscr();
(void) start_color();
(void) keypad(stdscr, TRUE);
(void) nonl();
(void) cbreak();
(void) noecho();
(void) curs_set(0);
/* Set up color pairs */
(void) init_pair(LT_COLOR_DEFAULT, COLOR_WHITE, COLOR_BLACK);
(void) init_pair(LT_COLOR_HEADER, COLOR_BLACK, COLOR_WHITE);
curses_inited = TRUE;
getmaxyx(stdscr, screen_height, screen_width);
if (screen_width < LT_WINDOW_X || screen_height < LT_WINDOW_Y) {
(void) mvwprintw(stdscr, 0, 0, "Terminal size is too small.");
(void) mvwprintw(stdscr, 1, 0,
"Please resize it to 80x24 or larger.");
(void) mvwprintw(stdscr, 2, 0, "Press q to quit.");
(void) refresh();
return;
}
/* Set up all window panes */
titlebar = subwin(stdscr, 1, screen_width, 0, 0);
captionbar = subwin(stdscr, 1, screen_width, 1, 0);
sysglobal_window = subwin(stdscr, screen_height / 2 - 1,
screen_width, 2, 0);
process_window = subwin(stdscr, screen_height / 2 - 3,
screen_width, screen_height / 2 + 1, 0);
taskbar = subwin(stdscr, 1, screen_width, screen_height - 2, 0);
hintbar = subwin(stdscr, 1, screen_width, screen_height - 1, 0);
(void) werase(stdscr);
(void) refresh();
display_initialized = TRUE;
print_title();
}
/*
* The event loop for display. It displays data on screen and handles hotkey
* presses.
*
* Parameter :
* duration - returns after 'duration'
*
* The function also returns if user presses 'q', 'Ctrl+C' or 'r'.
*
* Return value:
* 0 - main() exits
* 1 - main() calls it again
*/
int
lt_display_loop(int duration)
{
uint64_t start;
int remaining;
struct timeval timeout;
fd_set read_fd;
int need_refresh = TRUE;
pid_t *plist = NULL;
id_t *tlist = NULL;
int list_len = 0;
int list_index = 0;
int retval = 1;
int next_snap;
int gpipe;
start = lt_millisecond();
gpipe = lt_gpipe_readfd();
if (!show_help) {
print_hint(NULL);
print_sysglobal();
}
get_plist(&plist, &tlist, &list_len, &list_index);
for (;;) {
if (list_len != 0 && need_refresh && !show_help) {
if (!thread_mode) {
print_taskbar_process(plist, list_len,
list_index);
print_process(plist[list_index]);
} else {
print_taskbar_thread(plist, tlist,
list_len, list_index);
print_thread(plist[list_index],
tlist[list_index]);
}
}
need_refresh = TRUE; /* Usually we need refresh. */
remaining = duration - (int)(lt_millisecond() - start);
if (remaining <= 0) {
break;
}
/* Embedded dtrace snap action here. */
next_snap = lt_dtrace_work(0);
if (next_snap == 0) {
/*
* Just did a snap, check time for the next one.
*/
next_snap = lt_dtrace_work(0);
}
if (next_snap > 0 && remaining > next_snap) {
remaining = next_snap;
}
timeout.tv_sec = remaining / 1000;
timeout.tv_usec = (remaining % 1000) * 1000;
FD_ZERO(&read_fd);
FD_SET(0, &read_fd);
FD_SET(gpipe, &read_fd);
/* Wait for keyboard input, or signal from gpipe */
if (select(gpipe + 1, &read_fd, NULL, NULL, &timeout) > 0) {
int k = 0;
if (FD_ISSET(gpipe, &read_fd)) {
/* Data from pipe has priority */
char ch;
(void) read(gpipe, &ch, 1);
k = ch; /* Need this for big-endianness */
} else {
k = getch();
}
/*
* Check if we need to update the hint line whenever we
* get a chance.
* NOTE: current implementation depends on
* g_config.lt_cfg_snap_interval, but it's OK because it
* doesn't have to be precise.
*/
print_hint(NULL);
/*
* If help is on display right now, and a key press
* happens, we need to clear the help and continue.
*/
if (show_help) {
(void) werase(stdscr);
(void) refresh();
print_title();
print_sysglobal();
show_help = FALSE;
/* Drop this key and continue */
continue;
}
switch (k) {
case 'Q':
case 'q':
retval = 0;
goto quit;
case 'R':
case 'r':
lt_display_deinit();
lt_display_init();
goto quit;
case 'H':
case 'h':
show_help = TRUE;
(void) werase(stdscr);
(void) refresh();
print_help();
break;
case ',':
case '<':
case KEY_LEFT:
--list_index;
if (list_index < 0) {
list_index = 0;
}
break;
case '.':
case '>':
case KEY_RIGHT:
++list_index;
if (list_index >= list_len) {
list_index = list_len - 1;
}
break;
case 'a':
case 'A':
sort_type = LT_SORT_AVG;
print_sysglobal();
break;
case 'p':
case 'P':
sort_type = LT_SORT_TOTAL;
print_sysglobal();
break;
case 'm':
case 'M':
sort_type = LT_SORT_MAX;
print_sysglobal();
break;
case 'c':
case 'C':
sort_type = LT_SORT_COUNT;
print_sysglobal();
break;
case 't':
case 'T':
if (plist != NULL) {
selected_pid = plist[list_index];
}
selected_tid = INVALID_TID;
thread_mode = !thread_mode;
get_plist(&plist, &tlist,
&list_len, &list_index);
break;
case '1':
case '!':
current_list_type = LT_LIST_CAUSE;
print_sysglobal();
break;
case '2':
case '@':
if (g_config.lt_cfg_low_overhead_mode) {
lt_display_error("Switching mode is "
"not available for '-f low'.");
} else {
current_list_type = LT_LIST_SPECIALS;
print_sysglobal();
}
break;
case '3':
case '#':
if (g_config.lt_cfg_trace_syncobj) {
current_list_type = LT_LIST_SOBJ;
print_sysglobal();
} else if (g_config.lt_cfg_low_overhead_mode) {
lt_display_error("Switching mode is "
"not available for '-f low'.");
} else {
lt_display_error("Tracing "
"synchronization objects is "
"disabled.");
}
break;
default:
/* Wake up for nothing; no refresh is needed */
need_refresh = FALSE;
break;
}
} else {
need_refresh = FALSE;
}
}
quit:
if (plist != NULL) {
selected_pid = plist[list_index];
}
if (tlist != NULL) {
selected_tid = tlist[list_index];
}
lt_stat_proc_list_free(plist, tlist);
return (retval);
}
/*
* Clean up display.
*/
void
lt_display_deinit(void)
{
if (curses_inited) {
(void) clear();
(void) refresh();
(void) endwin();
}
titlebar = NULL;
captionbar = NULL;
sysglobal_window = NULL;
taskbar = NULL;
process_window = NULL;
hintbar = NULL;
screen_width = 1;
screen_height = 1;
display_initialized = FALSE;
curses_inited = FALSE;
}
/*
* Print message when display error happens.
*/
/* ARGSUSED */
void
lt_display_error(const char *fmt, ...)
{
va_list vl;
char tmp[81];
int l;
va_start(vl, fmt);
(void) vsnprintf(tmp, sizeof (tmp), fmt, vl);
va_end(vl);
l = strlen(tmp);
while (l > 0 && (tmp[l - 1] == '\n' || tmp[l - 1] == '\r')) {
tmp[l - 1] = '\0';
--l;
}
if (!display_initialized) {
(void) fprintf(stderr, "%s\n", tmp);
} else if (!show_help) {
print_hint(tmp);
}
}