mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
0d6e63abdc
Physical machine information does not include any monotonically increasing time field like other per CPU information blocks. This Prevents the percentage calculation for the physical information fields as the divisor(time_delta) is missing; field% = (value_current - value_previous) / time_delta To circumvent that, use the current partition's per CPU online time values to calculate the time_delta. Integrate time_delta as a new field(phys_delta_us) specific to the physical systems. Explicitly CPU0 online time is used, since CPU0 is always online and cannot be deconfigured on s390x. Its online-time deltas would match those of the physical CPUs. Since a new field has to be used for physical systems a new column in the table also has to be created but instead map the physical system field to the corresponding regular system fields for a nicer table view. Reviewed-by: Vasily Gorbik <gor@linux.ibm.com> Signed-off-by: Mete Durlu <meted@linux.ibm.com> Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
395 lines
6.9 KiB
C
395 lines
6.9 KiB
C
/*
|
|
* hyptop - Show hypervisor performance data on System z
|
|
*
|
|
* Main & init functions
|
|
*
|
|
* Copyright IBM Corp. 2010, 2017
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <ncurses.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
|
|
#include "lib/util_fmt.h"
|
|
|
|
#include "dg_debugfs.h"
|
|
#include "helper.h"
|
|
#include "hyptop.h"
|
|
#include "opts.h"
|
|
#include "sd.h"
|
|
#include "win_cpu_types.h"
|
|
|
|
#ifdef WITH_HYPFS
|
|
#include "dg_hypfs.h"
|
|
#endif
|
|
|
|
/*
|
|
* Globals for the whole program
|
|
*/
|
|
struct hyptop_globals g;
|
|
|
|
/*
|
|
* Get current terminal size and tell curses about it
|
|
*/
|
|
static void l_term_size_get(void)
|
|
{
|
|
struct winsize ws;
|
|
|
|
g.c.col_cnt = 80;
|
|
g.c.row_cnt = 24;
|
|
|
|
if (ioctl(1, TIOCGWINSZ, &ws) != -1) {
|
|
if ((ws.ws_col != 0) && (ws.ws_row != 0)) {
|
|
g.c.col_cnt = ws.ws_col;
|
|
g.c.row_cnt = ws.ws_row;
|
|
}
|
|
}
|
|
resizeterm(g.c.row_cnt, g.c.col_cnt);
|
|
}
|
|
|
|
/*
|
|
* Process input
|
|
*/
|
|
static enum hyptop_win_action l_process_input(struct hyptop_win *win)
|
|
{
|
|
int c;
|
|
|
|
/* Skip all resize events */
|
|
while ((c = wgetch(stdscr)) == KEY_RESIZE) {}
|
|
return win->process_input(win, c);
|
|
}
|
|
|
|
/*
|
|
* Process input with timeout
|
|
*/
|
|
static enum hyptop_win_action l_process_input_timeout(time_t time_s,
|
|
long time_us)
|
|
{
|
|
struct timeval tv;
|
|
fd_set fds;
|
|
int rc;
|
|
|
|
while (1) {
|
|
FD_ZERO(&fds);
|
|
FD_SET(0, &fds);
|
|
tv.tv_sec = time_s;
|
|
tv.tv_usec = time_us;
|
|
rc = select(1, &fds, NULL, NULL, &tv);
|
|
switch (rc) {
|
|
case 0:
|
|
/* Timeout */
|
|
return WIN_KEEP;
|
|
case 1:
|
|
/* Input */
|
|
if (l_process_input(g.w.cur) == WIN_SWITCH)
|
|
return WIN_SWITCH;
|
|
continue;
|
|
case -1:
|
|
if (errno != EINTR)
|
|
ERR_EXIT_ERRNO("Select call failed");
|
|
/* Signal: Resize */
|
|
hyptop_update_term();
|
|
continue;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Sleep
|
|
*/
|
|
static enum hyptop_win_action l_sleep(time_t time_s, long time_us)
|
|
{
|
|
struct timespec ts;
|
|
|
|
ts.tv_sec = time_s;
|
|
ts.tv_nsec = time_us * 1000;
|
|
|
|
nanosleep(&ts, NULL);
|
|
return WIN_KEEP;
|
|
}
|
|
|
|
/*
|
|
* External process input with timeout funciton
|
|
*/
|
|
enum hyptop_win_action hyptop_process_input_timeout(void)
|
|
{
|
|
enum hyptop_win_action rc;
|
|
|
|
if (g.o.batch_mode_specified) {
|
|
opts_iterations_next();
|
|
rc = l_sleep(g.o.delay_s, g.o.delay_us);
|
|
} else {
|
|
rc = l_process_input_timeout(g.o.delay_s, g.o.delay_us);
|
|
opts_iterations_next();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* External process input funciton
|
|
*/
|
|
enum hyptop_win_action hyptop_process_input(void)
|
|
{
|
|
return l_process_input_timeout(-1U, 0);
|
|
}
|
|
|
|
/*
|
|
* Signal handler for exiting hyptop
|
|
*/
|
|
static void l_sig_exit(int sig)
|
|
{
|
|
(void) sig;
|
|
|
|
hyptop_exit(0);
|
|
}
|
|
|
|
/*
|
|
* Install signal handler
|
|
*/
|
|
static void l_sig_handler_init(void)
|
|
{
|
|
struct sigaction sigact;
|
|
|
|
/* Ignore signals SIGUSR1 and SIGUSR2 */
|
|
if (sigemptyset(&sigact.sa_mask) < 0)
|
|
goto fail;
|
|
sigact.sa_flags = 0;
|
|
sigact.sa_handler = SIG_IGN;
|
|
if (sigaction(SIGUSR1, &sigact, NULL) < 0)
|
|
goto fail;
|
|
if (sigaction(SIGUSR2, &sigact, NULL) < 0)
|
|
goto fail;
|
|
|
|
/* Exit on SIGINT, SIGTERM, SIGHUP, ... */
|
|
if (sigemptyset(&sigact.sa_mask) < 0)
|
|
goto fail;
|
|
sigact.sa_handler = l_sig_exit;
|
|
if (sigaction(SIGINT, &sigact, NULL) < 0)
|
|
goto fail;
|
|
if (sigaction(SIGTERM, &sigact, NULL) < 0)
|
|
goto fail;
|
|
if (sigaction(SIGHUP, &sigact, NULL) < 0)
|
|
goto fail;
|
|
if (sigaction(SIGQUIT, &sigact, NULL) < 0)
|
|
goto fail;
|
|
if (sigaction(SIGALRM, &sigact, NULL) < 0)
|
|
goto fail;
|
|
if (sigaction(SIGPIPE, &sigact, NULL) < 0)
|
|
goto fail;
|
|
return;
|
|
fail:
|
|
ERR_EXIT_ERRNO("Could not initialize signal handler");
|
|
}
|
|
|
|
/*
|
|
* Start curses
|
|
*/
|
|
static int l_initscr(void)
|
|
{
|
|
if (!initscr())
|
|
return ERR;
|
|
g.c.initialized = 1;
|
|
atexit(hyptop_text_mode);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Check if terminal is able to run hyptop in curses mode
|
|
*/
|
|
static void l_term_check(void)
|
|
{
|
|
char *term_str = getenv("TERM");
|
|
|
|
if (!term_str)
|
|
ERR_EXIT("Please set TERM environment variable or "
|
|
"try \"--batch_mode\"\n");
|
|
|
|
/* S390 line mode terminals normally have TERM=dumb */
|
|
if (strcmp(term_str, "dumb") == 0)
|
|
ERR_EXIT("Terminal of type \"dumb\" is not supported,"
|
|
" try \"--batch_mode\"\n");
|
|
}
|
|
|
|
/*
|
|
* Init util_fmt if --format is specified on the command line.
|
|
*/
|
|
static void l_fmt_init(void)
|
|
{
|
|
unsigned int flags = FMT_WARN;
|
|
|
|
if (!g.o.format_specified)
|
|
return;
|
|
if (g.o.format == FMT_CSV) {
|
|
flags |= FMT_QUOTEALL;
|
|
flags |= FMT_KEEPINVAL;
|
|
}
|
|
if (g.o.format == FMT_JSON || g.o.format == FMT_JSONSEQ)
|
|
flags |= FMT_HANDLEINT;
|
|
util_fmt_init(stdout, g.o.format, flags, 1);
|
|
}
|
|
|
|
/*
|
|
* Init curses
|
|
*/
|
|
static void l_term_init(void)
|
|
{
|
|
if (g.o.batch_mode_specified)
|
|
return;
|
|
|
|
l_term_check();
|
|
|
|
if (l_initscr() == ERR)
|
|
goto fail;
|
|
if (noecho() == ERR)
|
|
goto fail;
|
|
if (nodelay(stdscr, TRUE) == ERR)
|
|
goto fail;
|
|
if (cbreak() == ERR) /* Line buffering disabled. pass on everything */
|
|
goto fail;
|
|
if (keypad(stdscr, TRUE) == ERR)
|
|
goto fail;
|
|
curs_set(0); /* prevent cursor from blinking */
|
|
l_term_size_get();
|
|
l_sig_handler_init();
|
|
return;
|
|
fail:
|
|
ERR_EXIT("Could not initialize curses, try \"--batch_mode\"\n");
|
|
}
|
|
|
|
/*
|
|
* Initialize data gatherer
|
|
*/
|
|
#ifdef WITH_HYPFS
|
|
static void l_dg_init(void)
|
|
{
|
|
if (dg_debugfs_init(0) == 0)
|
|
return;
|
|
if (dg_hypfs_init() == 0)
|
|
return;
|
|
ERR_EXIT("Could not initialize data gatherer\n");
|
|
}
|
|
#else
|
|
static void l_dg_init(void)
|
|
{
|
|
dg_debugfs_init(1);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Windows event loop
|
|
*/
|
|
static void l_event_loop(void)
|
|
{
|
|
while (1)
|
|
g.w.cur->run(g.w.cur);
|
|
}
|
|
|
|
/*
|
|
* Clear terminal and write new window content to it
|
|
*/
|
|
static void l_update_term_curses(void)
|
|
{
|
|
/* Init screen */
|
|
l_term_size_get();
|
|
curs_set(0); /* pervent cursor from blinking */
|
|
move(0, 0);
|
|
erase();
|
|
hyptop_printf_init();
|
|
/* Write window to screen */
|
|
g.w.cur->update_term(g.w.cur);
|
|
refresh();
|
|
}
|
|
|
|
/*
|
|
* Write window content in line mode
|
|
*/
|
|
static void l_update_term_batch(void)
|
|
{
|
|
g.w.cur->update_term(g.w.cur);
|
|
}
|
|
|
|
/*
|
|
* Update terminal with new window content
|
|
*/
|
|
void hyptop_update_term(void)
|
|
{
|
|
if (g.o.batch_mode_specified)
|
|
l_update_term_batch();
|
|
else
|
|
l_update_term_curses();
|
|
}
|
|
|
|
/*
|
|
* Switch to new window "win"
|
|
*/
|
|
enum hyptop_win_action win_switch(struct hyptop_win *win)
|
|
{
|
|
assert(g.w.prev_cnt < sizeof(g.w.prev) / sizeof(void *));
|
|
g.w.prev[g.w.prev_cnt] = g.w.cur;
|
|
g.w.prev_cnt++;
|
|
g.w.cur = win;
|
|
return WIN_SWITCH;
|
|
}
|
|
|
|
/*
|
|
* Switch back to previous window
|
|
*/
|
|
enum hyptop_win_action win_back(void)
|
|
{
|
|
g.w.prev_cnt--;
|
|
g.w.cur = g.w.prev[g.w.prev_cnt];
|
|
return WIN_SWITCH;
|
|
}
|
|
|
|
/*
|
|
* Switch to text mode
|
|
*/
|
|
void hyptop_text_mode(void)
|
|
{
|
|
if (!g.c.initialized)
|
|
return;
|
|
g.c.initialized = 0;
|
|
clear();
|
|
refresh();
|
|
endwin();
|
|
}
|
|
|
|
/*
|
|
* Exit hyptop
|
|
*/
|
|
void __noreturn hyptop_exit(int rc)
|
|
{
|
|
hyptop_text_mode();
|
|
exit(rc);
|
|
}
|
|
|
|
/*
|
|
* Initialize all modules and start first window
|
|
*/
|
|
int main(int argc, char *argv[])
|
|
{
|
|
opts_parse(argc, argv);
|
|
l_fmt_init();
|
|
hyptop_helper_init();
|
|
sd_init();
|
|
l_dg_init();
|
|
opt_verify_systems();
|
|
l_term_init();
|
|
|
|
win_sys_list_init();
|
|
win_sys_init();
|
|
g.win_cpu_types = win_cpu_types_new();
|
|
l_event_loop();
|
|
return 0;
|
|
}
|