display.c revision de3d2ce46fc25c7b67ccbae4afe5f15e5357568f
/*
* 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 "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 dimention */
/* Is display initialized, i.e. window pointers set up. */
static int display_initialized = FALSE;
/* Is initscr() called */
static int curses_inited = FALSE;
/* Changed by user key press */
static int thread_mode = FALSE;
/* what kind of list are we showing now */
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 {
}
}
/* Formats a human readable string out of nanosecond value */
static const char *
{
const double ONE_USEC = 1000.0;
const double ONE_MSEC = 1000000.0;
const double ONE_SEC = 1000000000.0;
} else {
}
return (buffer);
}
/*
* Print statistics in a window.
* IN: window - the global or process statistics window.
* begin_line - where to start printing.
* count - how many lines should we print.
* list - a stat_list.
*/
#define WIDTH_REASON_STRING 36
#define WIDTH_COUNT 12
#define WIDTH_SUM 12
#define WIDTH_MAX 12
#define WIDTH_PCT 8
#define BEGIN_COUNT WIDTH_REASON_STRING
static void
{
int i = 0;
if (!display_initialized) {
return;
}
if (total == 0) {
return;
}
/*
* We intentionally make tmp[] hold one character less
* than WIDTH_REASON_STRING, so it will look nice on the
* screen.
*/
char tmp[WIDTH_REASON_STRING];
if (count == 0) {
continue;
}
lt_stat_list_get_count(list, i));
"%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 global statistics. Calls print_statistics().
*/
static void
print_sysglobal(void)
{
void *list;
char header[256];
if (!display_initialized) {
return;
}
(void) werase(sysglobal_window);
(void) wrefresh(sysglobal_window);
}
/*
*/
static void
{
char type;
if (!display_initialized) {
return;
}
switch (current_list_type) {
case LT_LIST_CAUSE:
type = 'C';
break;
case LT_LIST_SPECIALS:
type = 'S';
break;
case LT_LIST_SOBJ:
type = 'L';
break;
default:
type = '?';
break;
}
}
/*
* Print per-process statistics. Calls print_statistics().
* This one is used in per-process mode.
*/
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) {
lt_text("Total: %s from %d threads"),
}
(void) wrefresh(process_window);
}
/*
* List all processes in task bar.
* This one is used in per-process mode.
*/
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) {
}
}
/*
* List all processes in task bar.
* This one is used in per-thread mode.
*/
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 thread id length, leave enough space by print
* shorter process name.
*/
"%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 statistics. Calls print_statistics().
* This one is used in per-thread mode.
*/
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 pick a message from useful tips
* and display it.
*/
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 {
/*
* To ensure important message
* show at least 2 cycles.
*/
}
}
/*
* Get information from existing statistics, and create a PID list
*/
static void
{
if (!thread_mode) {
/* Per-process mode */
/* Search for previous selected PID */
++*list_index) {
}
if (*list_index >= *list_len) {
/*
* The old selected pid is gone.
* Select the first one
*/
*list_index = 0;
}
} else {
/* Per-thread mode */
/* Search for previous selected PID & TID */
++*list_index) {
break;
}
}
if (*list_index >= *list_len) {
/*
* Select the first one in the pid
*/
for (*list_index = 0;
*list_index < *list_len &&
++*list_index) {
}
}
if (*list_index >= *list_len) {
/*
* The old selected pid is gone.
* Select the first one
*/
*list_index = 0;
}
}
}
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);
}
/*
* Signal handler on terminal resize
*/
/* ARGSUSED */
static void
{
lt_gpipe_break("r");
}
/*
* Initialize display part. Screen will be cleared when this function returns.
*/
void
lt_display_init(void)
{
if (display_initialized) {
return;
}
/* Window resize signal */
/* Initialize curses lib. */
(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;
}
/* Setup all windows on screen. */
screen_width, 2, 0);
(void) refresh();
print_title();
}
/*
* The event loop. Display data on screen and handles key press. Will return
* after "duration" seconds, unless exit or refresh hotkey is pressed.
* Return 0 means main() should exit. 1 means to loop 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 again to get time for
* next shot.
*/
next_snap = lt_dtrace_work(0);
}
}
/* Wait for keyboard input, or signal from gpipe */
int k = 0;
/* data from pipe has priority */
char ch; /* Need this for big-endian */
k = ch;
} else {
k = getch();
}
/*
* We check if we need to update hint line whenever we
* get chance.
* NOTE: current implementation depends on
* g_config.snap_interval, but it's OK because it
* doesn't have to be precise.
*/
/*
* If help is on, and a key press happens,
* we need to clear the help and go on.
*/
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.low_overhead_mode) {
lt_display_error("Switching mode is "
"not available for '-f low'.");
} else {
}
break;
case '3':
case '#':
if (g_config.trace_syncobj) {
} else if (g_config.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 need to refresh */
break;
}
} else {
}
}
quit:
}
}
return (retval);
}
/*
* Close display part.
*/
void
lt_display_deinit(void)
{
if (curses_inited) {
(void) clear();
(void) refresh();
(void) endwin();
}
captionbar = NULL;
screen_width = 1;
screen_height = 1;
}
/*
* Print error message.
*/
/* ARGSUSED */
void
lt_display_error(const char *fmt, ...)
{
char tmp[81];
int l;
tmp[l - 1] = 0;
--l;
}
if (!display_initialized) {
} else if (!show_help) {
}
}