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
* 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 <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 */
/* Screen dimension */
/* 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 int thread_mode = FALSE;
/* Type of list being displayed */
static int current_list_type = LT_LIST_CAUSE;
static void
{
int i = 0;
int tofill;
if (len >= buffer_limit) {
}
if (i >= len) {
return;
}
if (is_right) {
} else {
}
}
/* Convert the nanosecond value to a human readable string */
static const char *
{
const double ONE_USEC = 1000.0;
const double ONE_MSEC = 1000000.0;
const double ONE_SEC = 1000000000.0;
} else {
}
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
/*
* 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
{
int i = 0;
if (!display_initialized) {
return;
}
if (total == 0) {
return;
}
char tmp[WIDTH_REASON_STRING];
if (count == 0) {
continue;
}
"%s", tmp);
"%s", get_time_string(
"%s", get_time_string(
(double)lt_stat_list_get_max(list, i),
if (LT_LIST_SPECIALS != current_list_type) {
(double)lt_stat_list_get_sum(list, i)
/ total * 100.0);
} else {
}
"%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);
"%s", "System wide latencies");
(void) wrefresh(sysglobal_window);
}
/*
* Prints current operation mode. Mode is combination of:
*
* "Process or Thread", and "1 or 2 or 3".
*/
static void
{
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;
}
}
/*
* 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;
}
(void) werase(process_window);
if (current_list_type != LT_LIST_SPECIALS) {
}
(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
{
const int ITEM_WIDTH = 8;
int number_item;
int i;
int xpos = 0;
if (!display_initialized) {
return;
}
if (i != 0) {
}
int slen;
} else {
"<%d>", pidlist[i]);
}
if (slen < ITEM_WIDTH) {
}
if (i == pidlist_index) {
}
if (i == pidlist_index) {
}
xpos += ITEM_WIDTH;
i++;
}
if (i != pidlist_len) {
}
}
/*
* Display the list of processes that are tracked, in task bar.
* This one is called when mode of operation is thread.
*/
static void
int list_index)
{
const int ITEM_WIDTH = 12;
int number_item;
int i;
int xpos = 0;
if (!display_initialized) {
return;
}
if (i != 0) {
}
xpos = 4;
}
/*
* Calculate length of thread's ID; use shorter process name
* in order to save space on the screen.
*/
"%s", pname);
} else {
"<%d>", pidlist[i]);
}
"_%d", tidlist[i]);
if (slen < ITEM_WIDTH) {
}
if (i == list_index) {
}
if (i == list_index) {
}
xpos += ITEM_WIDTH;
i++;
}
if (i != list_len) {
}
}
/*
* Print per-thread statistics in process pane.
* This is called when mode of operation is thread.
*/
static void
{
void *list;
char header[256];
char tmp[30];
if (!display_initialized) {
return;
}
(void) werase(process_window);
"Process %s (%i), LWP %d",
if (current_list_type != LT_LIST_SPECIALS) {
(double)lt_stat_list_get_gtotal(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 'h' for help.",
"Use 'c', 'a', 'm', 'p' to change sort criteria."
"Use '1', '2', '3' to switch between windows."
};
static int index = 0;
if (!display_initialized) {
return;
}
return;
}
} else {
/*
* Important messages are displayed at least every 2 cycles.
*/
}
"%s", hint);
}
/*
* available statistics.
*/
static void
{
if (!thread_mode) {
/* Per-process mode */
/* Search for previously 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 */
/* Search for previously selected PID & TID */
++*list_index) {
break;
}
}
if (*list_index >= *list_len) {
/*
* Select the first one.
*/
for (*list_index = 0;
*list_index < *list_len &&
++*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[] = {
"",
"These single-character commands are available:",
"q - Exit.",
"r - Refresh.",
"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;
}
}
(void) refresh();
}
/*
* Print title on screen
*/
static void
print_title(void)
{
if (!display_initialized) {
return;
}
"%s", TITLE);
(void) werase(captionbar);
" Cause "
"Count Average Maximum Percent");
(void) wrefresh(captionbar);
}
/*
* Handle signal from terminal resize
*/
/* ARGSUSED */
static void
{
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 */
/* Initialize curses library */
(void) initscr();
(void) start_color();
(void) nonl();
(void) cbreak();
(void) noecho();
(void) curs_set(0);
/* Set up color pairs */
"Please resize it to 80x24 or larger.");
(void) refresh();
return;
}
/* Set up all window panes */
screen_width, 2, 0);
(void) refresh();
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)
{
int remaining;
int need_refresh = TRUE;
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) {
}
for (;;) {
if (!thread_mode) {
} else {
tlist[list_index]);
}
}
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);
}
}
/* Wait for keyboard input, or signal from gpipe */
int k = 0;
/* Data from pipe has priority */
char ch;
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.
*/
/*
* If help is on display right now, and a key press
* happens, we need to clear the help and continue.
*/
if (show_help) {
(void) refresh();
print_title();
/* Drop this key and continue */
continue;
}
switch (k) {
case 'Q':
case 'q':
retval = 0;
goto quit;
case 'R':
case 'r':
goto quit;
case 'H':
case 'h':
(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) {
}
break;
case 'a':
case 'A':
break;
case 'p':
case 'P':
break;
case 'm':
case 'M':
break;
case 'c':
case 'C':
break;
case 't':
case 'T':
}
&list_len, &list_index);
break;
case '1':
case '!':
break;
case '2':
case '@':
if (g_config.lt_cfg_low_overhead_mode) {
lt_display_error("Switching mode is "
"not available for '-f low'.");
} else {
}
break;
case '3':
case '#':
if (g_config.lt_cfg_trace_syncobj) {
} 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 */
break;
}
} else {
}
}
quit:
}
}
return (retval);
}
/*
* Clean up display.
*/
void
lt_display_deinit(void)
{
if (curses_inited) {
(void) clear();
(void) refresh();
(void) endwin();
}
captionbar = NULL;
screen_width = 1;
screen_height = 1;
}
/*
* Print message when display error happens.
*/
/* ARGSUSED */
void
lt_display_error(const char *fmt, ...)
{
char tmp[81];
int l;
--l;
}
if (!display_initialized) {
} else if (!show_help) {
}
}