Files
Matthias Goergens 292fb23a92 tools/xentop: reject invalid --delay and --iterations arguments
xentop parses -d/--delay and -i/--iterations with atoi(), so invalid
input is silently accepted with surprising results: a fractional delay
such as "-d 2.5" is truncated to 2; "-d abc" parses as 0, which in
batch mode turns the output loop into a busy loop; "-d -1" wraps to an
effective delay of about 136 years; and "-i 0" (or any unparsable
iterations count) decrements an unsigned counter from zero, running
for about 2^32 iterations.  atoi() also has undefined behaviour on
out-of-range input.

Parse both options with strtoull() instead, and reject anything that
is not a plain decimal integer in range: a sign, a fractional part,
trailing junk or overflow now produce an error and exit rather than a
silently wrong value.

Compatibility considerations: "--delay 0" remains accepted, since
updating as fast as possible is a plausible deliberate choice and
works today; "--iterations 0" is rejected, since running the loop
2^32 times cannot be what the caller meant.  The interactive 'D'
prompt already validates its input and is unchanged.  The only
previously useful invocation this breaks is a fractional delay, which
now fails loudly instead of silently rounding down - which is the
point of the change.

A patch documenting the --delay truncation was posted in 2010 but
never applied:
Link: https://lore.kernel.org/xen-devel/01ea26d2420e3562eb30.1292604768@chilopoda.uk.xensource.com/

Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com>
Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
2026-10-12 17:31:39 +02:00

1449 lines
39 KiB
C

/*
* Copyright (C) International Business Machines Corp., 2005
* Author(s): Judy Fischbach <jfisch@cs.pdx.edu>
* David Hendricks <cro_marmot@comcast.net>
* Josh Triplett <josh@kernel.org>
* based on code from Anthony Liguori <aliguori@us.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; under version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; If not, see <http://www.gnu.org/licenses/>.
*/
/* get curses header from configure */
#include INCLUDE_CURSES_H
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#if defined(__linux__)
#include <linux/kdev_t.h>
#endif
#include <xenstat.h>
#include "pcpu.h"
#define XENTOP_VERSION "1.0"
#define XENTOP_DISCLAIMER \
"Copyright (C) 2005 International Business Machines Corp\n"\
"This is free software; see the source for copying conditions.There is NO\n"\
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
#define XENTOP_BUGSTO "Report bugs to <xen-devel@lists.xen.org>.\n"
#define _GNU_SOURCE
#include <getopt.h>
#if !defined(__GNUC__) && !defined(__GNUG__)
#define __attribute__(arg) /* empty */
#endif
#define KEY_ESCAPE '\x1B'
#define KEY_REPAINT '\x0C'
#ifdef HOST_SunOS
/* Old curses library on Solaris takes non-const strings. Also, ERR interferes
* with curse's definition.
*/
#undef ERR
#define ERR (-1)
#define curses_str_t char *
#else
#define curses_str_t const char *
#endif
#define INT_FIELD_WIDTH(n) ((unsigned int)(log10(n) + 1))
/*
* Function prototypes
*/
/* Utility functions */
static void usage(const char *);
static void version(void);
static void cleanup(void);
static void fail(const char *);
static int current_row(void);
static int lines(void);
static void print(const char *, ...) __attribute__((format(printf,1,2)));
static void attr_addstr(int attr, const char *str);
static void set_delay(const char *value);
static void set_prompt(const char *new_prompt, void (*func)(const char *));
static int handle_key(int);
static int compare(unsigned long long, unsigned long long);
static int compare_dbl(double, double);
static int compare_domains(xenstat_domain **, xenstat_domain **);
static unsigned long long tot_net_bytes( xenstat_domain *, int);
static bool tot_vbd_reqs(xenstat_domain *, int, unsigned long long *);
/* Field functions */
static int compare_state(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_state(xenstat_domain *domain);
static int compare_cpu(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_cpu(xenstat_domain *domain);
static int compare_cpu_pct(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_cpu_pct(xenstat_domain *domain);
static int compare_mem(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_mem(xenstat_domain *domain);
static void print_mem_pct(xenstat_domain *domain);
static int compare_maxmem(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_maxmem(xenstat_domain *domain);
static void print_max_pct(xenstat_domain *domain);
static int compare_vcpus(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_vcpus(xenstat_domain *domain);
static int compare_nets(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_nets(xenstat_domain *domain);
static int compare_net_tx(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_net_tx(xenstat_domain *domain);
static int compare_net_rx(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_net_rx(xenstat_domain *domain);
static int compare_ssid(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_ssid(xenstat_domain *domain);
static int compare_name(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_name(xenstat_domain *domain);
static int compare_vbds(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_vbds(xenstat_domain *domain);
static int compare_vbd_oo(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_vbd_oo(xenstat_domain *domain);
static int compare_vbd_rd(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_vbd_rd(xenstat_domain *domain);
static int compare_vbd_wr(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_vbd_wr(xenstat_domain *domain);
static int compare_vbd_rsect(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_vbd_rsect(xenstat_domain *domain);
static int compare_vbd_wsect(xenstat_domain *domain1, xenstat_domain *domain2);
static void print_vbd_wsect(xenstat_domain *domain);
static void reset_field_widths(void);
static void adjust_field_widths(xenstat_domain *domain);
/* Section printing functions */
static void do_summary(void);
static void do_header(void);
static void do_bottom_line(void);
static void do_domain(xenstat_domain *);
static void do_vcpu(xenstat_domain *);
static void do_network(xenstat_domain *);
static void do_vbd(xenstat_domain *);
static void top(void);
/* Field types */
typedef enum field_id {
FIELD_DOMID,
FIELD_NAME,
FIELD_STATE,
FIELD_CPU,
FIELD_CPU_PCT,
FIELD_MEM,
FIELD_MEM_PCT,
FIELD_MAXMEM,
FIELD_MAX_PCT,
FIELD_VCPUS,
FIELD_NETS,
FIELD_NET_TX,
FIELD_NET_RX,
FIELD_VBDS,
FIELD_VBD_OO,
FIELD_VBD_RD,
FIELD_VBD_WR,
FIELD_VBD_RSECT,
FIELD_VBD_WSECT,
FIELD_SSID
} field_id;
typedef struct field {
field_id num;
const char *header;
unsigned int default_width;
int (*compare)(xenstat_domain *domain1, xenstat_domain *domain2);
void (*print)(xenstat_domain *domain);
} field;
field fields[] = {
{ FIELD_NAME, "NAME", 10, compare_name, print_name },
{ FIELD_STATE, "STATE", 6, compare_state, print_state },
{ FIELD_CPU, "CPU(sec)", 10, compare_cpu, print_cpu },
{ FIELD_CPU_PCT, "CPU(%)", 6, compare_cpu_pct, print_cpu_pct },
{ FIELD_MEM, "MEM(k)", 10, compare_mem, print_mem },
{ FIELD_MEM_PCT, "MEM(%)", 6, compare_mem, print_mem_pct },
{ FIELD_MAXMEM, "MAXMEM(k)", 10, compare_maxmem, print_maxmem },
{ FIELD_MAX_PCT, "MAXMEM(%)", 9, compare_maxmem, print_max_pct },
{ FIELD_VCPUS, "VCPUS", 5, compare_vcpus, print_vcpus },
{ FIELD_NETS, "NETS", 4, compare_nets, print_nets },
{ FIELD_NET_TX, "NETTX(k)", 8, compare_net_tx, print_net_tx },
{ FIELD_NET_RX, "NETRX(k)", 8, compare_net_rx, print_net_rx },
{ FIELD_VBDS, "VBDS", 4, compare_vbds, print_vbds },
{ FIELD_VBD_OO, "VBD_OO", 8, compare_vbd_oo, print_vbd_oo },
{ FIELD_VBD_RD, "VBD_RD", 8, compare_vbd_rd, print_vbd_rd },
{ FIELD_VBD_WR, "VBD_WR", 8, compare_vbd_wr, print_vbd_wr },
{ FIELD_VBD_RSECT, "VBD_RSECT", 10, compare_vbd_rsect, print_vbd_rsect },
{ FIELD_VBD_WSECT, "VBD_WSECT", 10, compare_vbd_wsect, print_vbd_wsect },
{ FIELD_SSID, "SSID", 4, compare_ssid, print_ssid }
};
const unsigned int NUM_FIELDS = sizeof(fields)/sizeof(field);
/* Globals */
struct timeval curtime, oldtime;
xenstat_handle *xhandle = NULL;
xenstat_node *prev_node = NULL;
xenstat_node *cur_node = NULL;
field_id sort_field = FIELD_DOMID;
unsigned int first_domain_index = 0;
unsigned int delay = 3;
unsigned int batch = 0;
static unsigned int show_pcpus = 0;
unsigned int loop = 1;
unsigned int iterations = 0;
int show_vcpus = 0;
int show_networks = 0;
int show_vbds = 0;
int repeat_header = 0;
int show_full_name = 0;
int dom0_first = 0;
#define PROMPT_VAL_LEN 80
const char *prompt = NULL;
char prompt_val[PROMPT_VAL_LEN];
int prompt_val_len = 0;
void (*prompt_complete_func)(const char *);
static WINDOW *cwin;
/*
* Function definitions
*/
/* Utility functions */
/* Print usage message, using given program name */
static void usage(const char *program)
{
printf("Usage: %s [OPTION]\n"
"Displays ongoing information about xen vm resources \n\n"
"-h, --help display this help and exit\n"
"-V, --version output version information and exit\n"
"-d, --delay=SECONDS seconds between updates (default 3)\n"
"-n, --networks output vif network data\n"
"-x, --vbds output vbd block device data\n"
"-r, --repeat-header repeat table header before each domain\n"
"-v, --vcpus output vcpu data\n"
"-b, --batch output in batch mode, no user input accepted\n"
"-p, --pcpus show physical CPU stats\n"
"-i, --iterations number of iterations before exiting\n"
"-f, --full-name output the full domain name (not truncated)\n"
"-z, --dom0-first display dom0 first (ignore sorting)\n"
"\n" XENTOP_BUGSTO,
program);
return;
}
/* Print program version information */
static void version(void)
{
printf("xentop " XENTOP_VERSION "\n"
"Written by Judy Fischbach, David Hendricks, Josh Triplett\n"
"\n" XENTOP_DISCLAIMER);
}
/* Clean up any open resources */
static void cleanup(void)
{
if(cwin != NULL && !isendwin())
endwin();
if(prev_node != NULL)
xenstat_free_node(prev_node);
if(cur_node != NULL)
xenstat_free_node(cur_node);
if(xhandle != NULL)
xenstat_uninit(xhandle);
free_pcpu_stats();
}
/* Display the given message and gracefully exit */
static void fail(const char *str)
{
if(cwin != NULL && !isendwin())
endwin();
fprintf(stderr, "%s", str);
exit(1);
}
/* Return the row containing the cursor. */
static int current_row(void)
{
int y, x;
getyx(stdscr, y, x);
return y;
}
/* Return the number of lines on the screen. */
static int lines(void)
{
int y, x;
getmaxyx(stdscr, y, x);
return y;
}
/* printf-style print function which calls printw, but only if the cursor is
* not on the last line. */
static void print(const char *fmt, ...)
{
va_list args;
if (!batch) {
if((current_row() < lines()-1)) {
va_start(args, fmt);
vw_printw(stdscr, (curses_str_t)fmt, args);
va_end(args);
}
} else {
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
}
/* PCPU statistics display function */
static void print_pcpu_stats_display(void)
{
int i;
int num_cpus;
if (!has_pcpu_data()) {
print("\nNo PCPU data available\n");
return;
}
num_cpus = get_pcpu_count();
/* Use the existing print() function which handles cursor bounds */
print("\nPhysical CPU Usage:\n");
print("+-------+--------+\n");
print("| Core | Usage |\n");
print("+-------+--------+\n");
for (i = 0; i < num_cpus; i++) {
print("| %-5d | %5.1f%% |\n", i, get_pcpu_usage(i));
}
print("+-------+--------+\n");
}
static void xentop_attron(int attr)
{
if (!batch)
attron(attr);
}
static void xentop_attroff(int attr)
{
if (!batch)
attroff(attr);
}
/* Print a string with the given attributes set. */
static void attr_addstr(int attr, const char *str)
{
xentop_attron(attr);
addstr((curses_str_t)str);
xentop_attroff(attr);
}
/* Handle setting the delay from the user-supplied value in prompt_val */
static void set_delay(const char *value)
{
int new_delay;
new_delay = atoi(value);
if(new_delay > 0)
delay = new_delay;
}
/* Enable prompting mode with the given prompt string; call the given function
* when a value is available. */
static void set_prompt(const char *new_prompt, void (*func)(const char *))
{
prompt = new_prompt;
prompt_val[0] = '\0';
prompt_val_len = 0;
prompt_complete_func = func;
}
/* Handle user input, return 0 if the program should quit, or 1 if not */
static int handle_key(int ch)
{
if(prompt == NULL) {
/* Not prompting for input; handle interactive commands */
switch(ch) {
case 'n': case 'N':
show_networks ^= 1;
break;
case 'b': case 'B':
show_vbds ^= 1;
break;
case 'r': case 'R':
repeat_header ^= 1;
break;
case 's': case 'S':
sort_field = (sort_field + 1) % NUM_FIELDS;
break;
case 'v': case 'V':
show_vcpus ^= 1;
break;
case KEY_DOWN:
first_domain_index++;
break;
case KEY_UP:
if(first_domain_index > 0)
first_domain_index--;
break;
case 'd': case 'D':
set_prompt("Delay(sec)", set_delay);
break;
case KEY_REPAINT:
clear();
break;
case 'q': case 'Q': case KEY_ESCAPE:
return 0;
}
} else {
/* Prompting for input; handle line editing */
switch(ch) {
case '\r':
prompt_complete_func(prompt_val);
set_prompt(NULL, NULL);
break;
case KEY_ESCAPE:
set_prompt(NULL, NULL);
break;
case KEY_BACKSPACE:
if(prompt_val_len > 0)
prompt_val[--prompt_val_len] = '\0';
break;
default:
if((prompt_val_len+1) < PROMPT_VAL_LEN
&& isprint(ch)) {
prompt_val[prompt_val_len++] = (char)ch;
prompt_val[prompt_val_len] = '\0';
}
}
}
return 1;
}
/* Compares two integers, returning -1,0,1 for <,=,> */
static int compare(unsigned long long i1, unsigned long long i2)
{
if(i1 < i2)
return -1;
if(i1 > i2)
return 1;
return 0;
}
/* Compares two double precision numbers, returning -1,0,1 for <,=,> */
static int compare_dbl(double d1, double d2)
{
if (d1 < d2)
return -1;
if (d1 > d2)
return 1;
return 0;
}
/* Comparison function for use with qsort. Compares two domains using the
* current sort field. */
static int compare_domains(xenstat_domain **domain1, xenstat_domain **domain2)
{
return fields[sort_field].compare(*domain1, *domain2);
}
/* Field functions */
/* Compare domain names, returning -1,0,1 for <,=,> */
int compare_name(xenstat_domain *domain1, xenstat_domain *domain2)
{
return strcasecmp(xenstat_domain_name(domain1), xenstat_domain_name(domain2));
}
/* Prints domain name */
void print_name(xenstat_domain *domain)
{
if(show_full_name)
print("%*s", fields[FIELD_NAME-1].default_width, xenstat_domain_name(domain));
else
print("%10.10s", xenstat_domain_name(domain));
}
struct {
unsigned int (*get)(xenstat_domain *);
char ch;
} state_funcs[] = {
{ xenstat_domain_dying, 'd' },
{ xenstat_domain_shutdown, 's' },
{ xenstat_domain_blocked, 'b' },
{ xenstat_domain_crashed, 'c' },
{ xenstat_domain_paused, 'p' },
{ xenstat_domain_running, 'r' }
};
const unsigned int NUM_STATES = sizeof(state_funcs)/sizeof(*state_funcs);
/* Compare states of two domains, returning -1,0,1 for <,=,> */
static int compare_state(xenstat_domain *domain1, xenstat_domain *domain2)
{
unsigned int i, d1s, d2s;
for(i = 0; i < NUM_STATES; i++) {
d1s = state_funcs[i].get(domain1);
d2s = state_funcs[i].get(domain2);
if(d1s && !d2s)
return -1;
if(d2s && !d1s)
return 1;
}
return 0;
}
/* Prints domain state in abbreviated letter format */
static void print_state(xenstat_domain *domain)
{
unsigned int i;
for(i = 0; i < NUM_STATES; i++)
print("%c", state_funcs[i].get(domain) ? state_funcs[i].ch
: '-');
}
/* Compares cpu usage of two domains, returning -1,0,1 for <,=,> */
static int compare_cpu(xenstat_domain *domain1, xenstat_domain *domain2)
{
return -compare(xenstat_domain_cpu_ns(domain1),
xenstat_domain_cpu_ns(domain2));
}
/* Prints domain cpu usage in seconds */
static void print_cpu(xenstat_domain *domain)
{
print("%10llu", xenstat_domain_cpu_ns(domain)/1000000000);
}
/* Computes the CPU percentage used for a specified domain */
static double get_cpu_pct(xenstat_domain *domain)
{
xenstat_domain *old_domain;
double us_elapsed;
/* Can't calculate CPU percentage without a previous sample. */
if(prev_node == NULL)
return 0.0;
old_domain = xenstat_node_domain(prev_node, xenstat_domain_id(domain));
if(old_domain == NULL)
return 0.0;
/* Calculate the time elapsed in microseconds */
us_elapsed = ((curtime.tv_sec-oldtime.tv_sec)*1000000.0
+(curtime.tv_usec - oldtime.tv_usec));
/* In the following, nanoseconds must be multiplied by 1000.0 to
* convert to microseconds, then divided by 100.0 to get a percentage,
* resulting in a multiplication by 10.0 */
return ((xenstat_domain_cpu_ns(domain)
-xenstat_domain_cpu_ns(old_domain))/10.0)/us_elapsed;
}
static int compare_cpu_pct(xenstat_domain *domain1, xenstat_domain *domain2)
{
return -compare_dbl(get_cpu_pct(domain1), get_cpu_pct(domain2));
}
/* Prints cpu percentage statistic */
static void print_cpu_pct(xenstat_domain *domain)
{
print("%6.1f", get_cpu_pct(domain));
}
/* Compares current memory of two domains, returning -1,0,1 for <,=,> */
static int compare_mem(xenstat_domain *domain1, xenstat_domain *domain2)
{
return -compare(xenstat_domain_cur_mem(domain1),
xenstat_domain_cur_mem(domain2));
}
/* Prints current memory statistic */
static void print_mem(xenstat_domain *domain)
{
print("%10llu", xenstat_domain_cur_mem(domain)/1024);
}
/* Prints memory percentage statistic, ratio of current domain memory to total
* node memory */
static void print_mem_pct(xenstat_domain *domain)
{
print("%6.1f", (double)xenstat_domain_cur_mem(domain) /
(double)xenstat_node_tot_mem(cur_node) * 100);
}
/* Compares maximum memory of two domains, returning -1,0,1 for <,=,> */
static int compare_maxmem(xenstat_domain *domain1, xenstat_domain *domain2)
{
return -compare(xenstat_domain_max_mem(domain1),
xenstat_domain_max_mem(domain2));
}
/* Prints maximum domain memory statistic in KB */
static void print_maxmem(xenstat_domain *domain)
{
unsigned long long max_mem = xenstat_domain_max_mem(domain);
if(max_mem == ((unsigned long long)-1))
print("%10s", "no limit");
else
print("%10llu", max_mem/1024);
}
/* Prints memory percentage statistic, ratio of current domain memory to total
* node memory */
static void print_max_pct(xenstat_domain *domain)
{
if (xenstat_domain_max_mem(domain) == (unsigned long long)-1)
print("%9s", "n/a");
else
print("%9.1f", (double)xenstat_domain_max_mem(domain) /
(double)xenstat_node_tot_mem(cur_node) * 100);
}
/* Compares number of virtual CPUs of two domains, returning -1,0,1 for
* <,=,> */
static int compare_vcpus(xenstat_domain *domain1, xenstat_domain *domain2)
{
return -compare(xenstat_domain_num_vcpus(domain1),
xenstat_domain_num_vcpus(domain2));
}
/* Prints number of virtual CPUs statistic */
static void print_vcpus(xenstat_domain *domain)
{
print("%5u", xenstat_domain_num_vcpus(domain));
}
/* Compares number of virtual networks of two domains, returning -1,0,1 for
* <,=,> */
static int compare_nets(xenstat_domain *domain1, xenstat_domain *domain2)
{
return -compare(xenstat_domain_num_networks(domain1),
xenstat_domain_num_networks(domain2));
}
/* Prints number of virtual networks statistic */
static void print_nets(xenstat_domain *domain)
{
print("%4u", xenstat_domain_num_networks(domain));
}
/* Compares number of total network tx bytes of two domains, returning -1,0,1
* for <,=,> */
static int compare_net_tx(xenstat_domain *domain1, xenstat_domain *domain2)
{
return -compare(tot_net_bytes(domain1, FALSE),
tot_net_bytes(domain2, FALSE));
}
/* Prints number of total network tx bytes statistic */
static void print_net_tx(xenstat_domain *domain)
{
print("%*llu", fields[FIELD_NET_TX-1].default_width, tot_net_bytes(domain, FALSE)/1024);
}
/* Compares number of total network rx bytes of two domains, returning -1,0,1
* for <,=,> */
static int compare_net_rx(xenstat_domain *domain1, xenstat_domain *domain2)
{
return -compare(tot_net_bytes(domain1, TRUE),
tot_net_bytes(domain2, TRUE));
}
/* Prints number of total network rx bytes statistic */
static void print_net_rx(xenstat_domain *domain)
{
print("%*llu", fields[FIELD_NET_RX-1].default_width, tot_net_bytes(domain, TRUE)/1024);
}
/* Gets number of total network bytes statistic, if rx true, then rx bytes
* otherwise tx bytes
*/
static unsigned long long tot_net_bytes(xenstat_domain *domain, int rx_flag)
{
int i = 0;
xenstat_network *network;
unsigned num_networks = 0;
unsigned long long total = 0;
/* How many networks? */
num_networks = xenstat_domain_num_networks(domain);
/* Dump information for each network */
for (i=0; i < num_networks; i++) {
/* Next get the network information */
network = xenstat_domain_network(domain,i);
if (rx_flag)
total += xenstat_network_rbytes(network);
else
total += xenstat_network_tbytes(network);
}
return total;
}
/* Compares number of virtual block devices of two domains,
returning -1,0,1 for * <,=,> */
static int compare_vbds(xenstat_domain *domain1, xenstat_domain *domain2)
{
return -compare(xenstat_domain_num_vbds(domain1),
xenstat_domain_num_vbds(domain2));
}
/* Prints number of virtual block devices statistic */
static void print_vbds(xenstat_domain *domain)
{
print("%4u", xenstat_domain_num_vbds(domain));
}
/* Compares number of total VBD OO requests of two domains,
returning -1,0,1 * for <,=,> */
static int compare_vbd_oo(xenstat_domain *domain1, xenstat_domain *domain2)
{
unsigned long long dom1_vbd_oo = 0, dom2_vbd_oo = 0;
tot_vbd_reqs(domain1, FIELD_VBD_OO, &dom1_vbd_oo);
tot_vbd_reqs(domain2, FIELD_VBD_OO, &dom2_vbd_oo);
return -compare(dom1_vbd_oo, dom2_vbd_oo);
}
/* Prints number of total VBD OO requests statistic */
static void print_vbd_oo(xenstat_domain *domain)
{
unsigned long long vbd_oo;
if (tot_vbd_reqs(domain, FIELD_VBD_OO, &vbd_oo))
{
print("%8llu", vbd_oo);
}
else
{
print("%8c", '-');
}
}
/* Compares number of total VBD READ requests of two domains,
returning -1,0,1 * for <,=,> */
static int compare_vbd_rd(xenstat_domain *domain1, xenstat_domain *domain2)
{
unsigned long long dom1_vbd_rd = 0, dom2_vbd_rd = 0;
tot_vbd_reqs(domain1, FIELD_VBD_RD, &dom1_vbd_rd);
tot_vbd_reqs(domain2, FIELD_VBD_RD, &dom2_vbd_rd);
return -compare(dom1_vbd_rd, dom2_vbd_rd);
}
/* Prints number of total VBD READ requests statistic */
static void print_vbd_rd(xenstat_domain *domain)
{
unsigned long long vbd_rd;
if (tot_vbd_reqs(domain, FIELD_VBD_RD, &vbd_rd))
{
print("%*llu", fields[FIELD_VBD_RD-1].default_width, vbd_rd);
}
else
{
print("%*c", fields[FIELD_VBD_RD-1].default_width, '-');
}
}
/* Compares number of total VBD WRITE requests of two domains,
returning -1,0,1 * for <,=,> */
static int compare_vbd_wr(xenstat_domain *domain1, xenstat_domain *domain2)
{
unsigned long long dom1_vbd_wr = 0, dom2_vbd_wr = 0;
tot_vbd_reqs(domain1, FIELD_VBD_WR, &dom1_vbd_wr);
tot_vbd_reqs(domain2, FIELD_VBD_WR, &dom2_vbd_wr);
return -compare(dom1_vbd_wr, dom2_vbd_wr);
}
/* Prints number of total VBD WRITE requests statistic */
static void print_vbd_wr(xenstat_domain *domain)
{
unsigned long long vbd_wr;
if (tot_vbd_reqs(domain, FIELD_VBD_WR, &vbd_wr))
{
print("%*llu", fields[FIELD_VBD_WR-1].default_width, vbd_wr);
}
else
{
print("%*c", fields[FIELD_VBD_WR-1].default_width, '-');
}
}
/* Compares number of total VBD READ sectors of two domains,
returning -1,0,1 * for <,=,> */
static int compare_vbd_rsect(xenstat_domain *domain1, xenstat_domain *domain2)
{
unsigned long long dom1_vbd_rsect = 0, dom2_vbd_rsect = 0;
tot_vbd_reqs(domain1, FIELD_VBD_RSECT, &dom1_vbd_rsect);
tot_vbd_reqs(domain2, FIELD_VBD_RSECT, &dom2_vbd_rsect);
return -compare(dom1_vbd_rsect, dom2_vbd_rsect);
}
/* Prints number of total VBD READ sectors statistic */
static void print_vbd_rsect(xenstat_domain *domain)
{
unsigned long long vbd_rsect;
if (tot_vbd_reqs(domain, FIELD_VBD_RSECT, &vbd_rsect))
{
print("%*llu", fields[FIELD_VBD_RSECT-1].default_width, vbd_rsect);
}
else
{
print("%*c", fields[FIELD_VBD_RSECT-1].default_width, '-');
}
}
/* Compares number of total VBD WRITE sectors of two domains,
returning -1,0,1 * for <,=,> */
static int compare_vbd_wsect(xenstat_domain *domain1, xenstat_domain *domain2)
{
unsigned long long dom1_vbd_wsect = 0, dom2_vbd_wsect = 0;
tot_vbd_reqs(domain1, FIELD_VBD_WSECT, &dom1_vbd_wsect);
tot_vbd_reqs(domain2, FIELD_VBD_WSECT, &dom2_vbd_wsect);
return -compare(dom1_vbd_wsect, dom2_vbd_wsect);
}
/* Prints number of total VBD WRITE sectors statistic */
static void print_vbd_wsect(xenstat_domain *domain)
{
unsigned long long vbd_wsect;
if (tot_vbd_reqs(domain, FIELD_VBD_WSECT, &vbd_wsect))
{
print("%*llu", fields[FIELD_VBD_WSECT-1].default_width, vbd_wsect);
}
else
{
print("%*c", fields[FIELD_VBD_WSECT-1].default_width, '-');
}
}
/* Gets number of total VBD requests statistic,
* if flag is FIELD_VBD_OO, then OO requests,
* if flag is FIELD_VBD_RD, then READ requests,
* if flag is FIELD_VBD_WR, then WRITE requests,
* if flag is FIELD_VBD_RSECT, then READ sectors,
* if flag is FIELD_VBD_WSECT, then WRITE sectors.
*/
static bool tot_vbd_reqs(xenstat_domain *domain, int flag, unsigned long long *res)
{
int i = 0;
xenstat_vbd *vbd;
unsigned num_vbds = 0;
unsigned long long total = 0;
bool show_stats = false;
num_vbds = xenstat_domain_num_vbds(domain);
if (!num_vbds)
show_stats = true;
for ( i=0 ; i < num_vbds ; i++) {
vbd = xenstat_domain_vbd(domain,i);
if (xenstat_vbd_error(vbd))
continue;
else
show_stats = true;
switch(flag) {
case FIELD_VBD_OO:
total += xenstat_vbd_oo_reqs(vbd);
break;
case FIELD_VBD_RD:
total += xenstat_vbd_rd_reqs(vbd);
break;
case FIELD_VBD_WR:
total += xenstat_vbd_wr_reqs(vbd);
break;
case FIELD_VBD_RSECT:
total += xenstat_vbd_rd_sects(vbd);
break;
case FIELD_VBD_WSECT:
total += xenstat_vbd_wr_sects(vbd);
break;
default:
break;
}
}
*res = total;
return show_stats;
}
/* Compares security id (ssid) of two domains, returning -1,0,1 for <,=,> */
static int compare_ssid(xenstat_domain *domain1, xenstat_domain *domain2)
{
return compare(xenstat_domain_ssid(domain1),
xenstat_domain_ssid(domain2));
}
/* Prints ssid statistic */
static void print_ssid(xenstat_domain *domain)
{
print("%4u", xenstat_domain_ssid(domain));
}
/* Resets default_width for fields with potentially large numbers */
void reset_field_widths(void)
{
fields[FIELD_NET_TX-1].default_width = 8;
fields[FIELD_NET_RX-1].default_width = 8;
fields[FIELD_VBD_RD-1].default_width = 8;
fields[FIELD_VBD_WR-1].default_width = 8;
fields[FIELD_VBD_RSECT-1].default_width = 10;
fields[FIELD_VBD_WSECT-1].default_width = 10;
}
/* Adjusts default_width for fields with potentially large numbers */
void adjust_field_widths(xenstat_domain *domain)
{
unsigned int length;
unsigned long long vbd_rd, vbd_wr, vbd_rsect, vbd_wsect;
if (show_full_name) {
length = strlen(xenstat_domain_name(domain));
if (length > fields[FIELD_NAME-1].default_width)
fields[FIELD_NAME-1].default_width = length;
}
length = INT_FIELD_WIDTH((tot_net_bytes(domain, FALSE)/1024) + 1);
if (length > fields[FIELD_NET_TX-1].default_width)
fields[FIELD_NET_TX-1].default_width = length;
length = INT_FIELD_WIDTH((tot_net_bytes(domain, TRUE)/1024) + 1);
if (length > fields[FIELD_NET_RX-1].default_width)
fields[FIELD_NET_RX-1].default_width = length;
tot_vbd_reqs(domain, FIELD_VBD_RD, &vbd_rd);
length = INT_FIELD_WIDTH(vbd_rd + 1);
if (length > fields[FIELD_VBD_RD-1].default_width)
fields[FIELD_VBD_RD-1].default_width = length;
tot_vbd_reqs(domain, FIELD_VBD_WR, &vbd_wr);
length = INT_FIELD_WIDTH(vbd_wr + 1);
if (length > fields[FIELD_VBD_WR-1].default_width)
fields[FIELD_VBD_WR-1].default_width = length;
tot_vbd_reqs(domain, FIELD_VBD_RSECT, &vbd_rsect);
length = INT_FIELD_WIDTH(vbd_rsect + 1);
if (length > fields[FIELD_VBD_RSECT-1].default_width)
fields[FIELD_VBD_RSECT-1].default_width = length;
tot_vbd_reqs(domain, FIELD_VBD_WSECT, &vbd_wsect);
length = INT_FIELD_WIDTH(vbd_wsect + 1);
if (length > fields[FIELD_VBD_WSECT-1].default_width)
fields[FIELD_VBD_WSECT-1].default_width = length;
}
/* Section printing functions */
/* Prints the top summary, above the domain table */
void do_summary(void)
{
#define TIME_STR_LEN 9
const char *TIME_STR_FORMAT = "%H:%M:%S";
char time_str[TIME_STR_LEN];
const char *ver_str;
unsigned run = 0, block = 0, pause = 0,
crash = 0, dying = 0, shutdown = 0;
unsigned i, num_domains = 0;
unsigned long long used = 0;
xenstat_domain *domain;
time_t curt;
/* Print program name, current time, and number of domains */
curt = curtime.tv_sec;
strftime(time_str, TIME_STR_LEN, TIME_STR_FORMAT, localtime(&curt));
num_domains = xenstat_node_num_domains(cur_node);
ver_str = xenstat_node_xen_version(cur_node);
print("xentop - %s Xen %s\n", time_str, ver_str);
/* Tabulate what states domains are in for summary */
for (i=0; i < num_domains; i++) {
domain = xenstat_node_domain_by_index(cur_node,i);
if (xenstat_domain_running(domain)) run++;
else if (xenstat_domain_blocked(domain)) block++;
else if (xenstat_domain_paused(domain)) pause++;
else if (xenstat_domain_shutdown(domain)) shutdown++;
else if (xenstat_domain_crashed(domain)) crash++;
else if (xenstat_domain_dying(domain)) dying++;
}
print("%u domains: %u running, %u blocked, %u paused, "
"%u crashed, %u dying, %u shutdown \n",
num_domains, run, block, pause, crash, dying, shutdown);
used = xenstat_node_tot_mem(cur_node) - xenstat_node_free_mem(cur_node);
/* Dump node memory and cpu information */
print("Mem: %lluk total, %lluk used, %lluk free ",
xenstat_node_tot_mem(cur_node)/1024, used/1024,
xenstat_node_free_mem(cur_node)/1024);
print("CPUs: %u @ %lluMHz\n",
xenstat_node_num_cpus(cur_node),
xenstat_node_cpu_hz(cur_node)/1000000);
}
/* Display the top header for the domain table */
void do_header(void)
{
field_id i;
/* Turn on REVERSE highlight attribute for headings */
xentop_attron(A_REVERSE);
for(i = 0; i < NUM_FIELDS; i++) {
if (i != 0)
print(" ");
/* The BOLD attribute is turned on for the sort column */
if (i == sort_field)
xentop_attron(A_BOLD);
print("%*s", fields[i].default_width, fields[i].header);
if (i == sort_field)
xentop_attroff(A_BOLD);
}
xentop_attroff(A_REVERSE);
print("\n");
}
/* Displays bottom status line or current prompt */
void do_bottom_line(void)
{
move(lines()-1, 2);
if (prompt != NULL) {
printw("%s: %s", prompt, prompt_val);
} else {
addch(A_REVERSE | 'D'); addstr("elay ");
/* network */
addch(A_REVERSE | 'N');
attr_addstr(show_networks ? COLOR_PAIR(1) : 0, "etworks");
addstr(" ");
/* VBDs */
attr_addstr(show_vbds ? COLOR_PAIR(1) : 0, "v");
addch(A_REVERSE | 'B');
attr_addstr(show_vbds ? COLOR_PAIR(1) : 0, "ds");
addstr(" ");
/* vcpus */
addch(A_REVERSE | 'V');
attr_addstr(show_vcpus ? COLOR_PAIR(1) : 0, "CPUs");
addstr(" ");
/* repeat */
addch(A_REVERSE | 'R');
attr_addstr(repeat_header ? COLOR_PAIR(1) : 0, "epeat header");
addstr(" ");
/* sort order */
addch(A_REVERSE | 'S'); addstr("ort order ");
addch(A_REVERSE | 'Q'); addstr("uit ");
}
}
/* Prints Domain information */
void do_domain(xenstat_domain *domain)
{
unsigned int i;
for (i = 0; i < NUM_FIELDS; i++) {
if (i != 0)
print(" ");
if (i == sort_field)
xentop_attron(A_BOLD);
fields[i].print(domain);
if (i == sort_field)
xentop_attroff(A_BOLD);
}
print("\n");
}
/* Output all vcpu information */
void do_vcpu(xenstat_domain *domain)
{
int i = 0;
unsigned num_vcpus = 0;
xenstat_vcpu *vcpu;
print("VCPUs(sec): ");
num_vcpus = xenstat_domain_num_vcpus(domain);
/* for all online vcpus dump out values */
for (i=0; i< num_vcpus; i++) {
vcpu = xenstat_domain_vcpu(domain,i);
if (xenstat_vcpu_online(vcpu) > 0) {
if (i != 0 && (i%5)==0)
print("\n ");
print(" %2u: %10llus", i,
xenstat_vcpu_ns(vcpu)/1000000000);
}
}
print("\n");
}
/* Output all network information */
void do_network(xenstat_domain *domain)
{
int i = 0;
xenstat_network *network;
unsigned num_networks = 0;
/* How many networks? */
num_networks = xenstat_domain_num_networks(domain);
/* Dump information for each network */
for (i=0; i < num_networks; i++) {
/* Next get the network information */
network = xenstat_domain_network(domain,i);
print("Net%d RX: %8llubytes %8llupkts %8lluerr %8lludrop ",
i,
xenstat_network_rbytes(network),
xenstat_network_rpackets(network),
xenstat_network_rerrs(network),
xenstat_network_rdrop(network));
print("TX: %8llubytes %8llupkts %8lluerr %8lludrop\n",
xenstat_network_tbytes(network),
xenstat_network_tpackets(network),
xenstat_network_terrs(network),
xenstat_network_tdrop(network));
}
}
/* Output all VBD information */
void do_vbd(xenstat_domain *domain)
{
int i = 0;
xenstat_vbd *vbd;
unsigned num_vbds = 0;
const char *vbd_type[] = {
"Unidentified", /* number 0 */
"BlkBack", /* number 1 */
"BlkTap", /* number 2 */
"Tapdisk3" /* number 3 */
};
num_vbds = xenstat_domain_num_vbds(domain);
for (i=0 ; i< num_vbds; i++) {
char details[20];
vbd = xenstat_domain_vbd(domain,i);
#if !defined(__linux__)
details[0] = '\0';
#else
snprintf(details, 20, "[%2x:%2x] ",
MAJOR(xenstat_vbd_dev(vbd)),
MINOR(xenstat_vbd_dev(vbd)));
#endif
if (xenstat_vbd_error(vbd))
{
print("VBD %s %4d %s OO: %8c RD: %8c WR: %8c RSECT: %10c WSECT: %10c\n",
vbd_type[xenstat_vbd_type(vbd)],
xenstat_vbd_dev(vbd), details,
'-', '-', '-', '-','-');
}
else
{
print("VBD %s %4d %s OO: %8llu RD: %8llu WR: %8llu RSECT: %10llu WSECT: %10llu\n",
vbd_type[xenstat_vbd_type(vbd)],
xenstat_vbd_dev(vbd), details,
xenstat_vbd_oo_reqs(vbd),
xenstat_vbd_rd_reqs(vbd),
xenstat_vbd_wr_reqs(vbd),
xenstat_vbd_rd_sects(vbd),
xenstat_vbd_wr_sects(vbd));
}
}
}
static void top(void)
{
xenstat_domain **domains;
unsigned int i, num_domains = 0;
int dom0_index = -1;
int sort_start = 0, sort_count = 0;
/* Now get the node information */
if (prev_node != NULL)
xenstat_free_node(prev_node);
prev_node = cur_node;
cur_node = xenstat_get_node(xhandle, XENSTAT_ALL);
if (cur_node == NULL)
fail("Failed to retrieve statistics from libxenstat\n");
/* dump summary top information */
if (!batch)
do_summary();
/* Count the number of domains for which to report data */
num_domains = xenstat_node_num_domains(cur_node);
domains = calloc(num_domains, sizeof(xenstat_domain *));
if(domains == NULL)
fail("Failed to allocate memory\n");
for (i=0; i < num_domains; i++) {
domains[i] = xenstat_node_domain_by_index(cur_node, i);
if ( strcmp(xenstat_domain_name(domains[i]), "Domain-0") == 0 )
dom0_index = i;
}
/* Handle dom0 position, not for dom0-less */
if ( dom0_first == 1 && dom0_index != -1 ){
/* if dom0 is not first in domains, swap it there */
if ( dom0_index != 0 ){
xenstat_domain *tmp;
tmp = domains[0];
domains[0] = domains[dom0_index];
domains[dom0_index] = tmp;
}
sort_start = 1;
sort_count = 1;
}
/* Sort */
qsort((domains+sort_start), (num_domains-sort_count), sizeof(xenstat_domain *),
(int(*)(const void *, const void *))compare_domains);
if(first_domain_index >= num_domains)
first_domain_index = num_domains-1;
/* Adjust default_width for fields with potentially large numbers */
reset_field_widths();
for (i = first_domain_index; i < num_domains; i++) {
adjust_field_widths(domains[i]);
}
for (i = first_domain_index; i < num_domains; i++) {
if(!batch && current_row() == lines()-1)
break;
if (i == first_domain_index || repeat_header)
do_header();
do_domain(domains[i]);
if (show_vcpus)
do_vcpu(domains[i]);
if (show_networks)
do_network(domains[i]);
if (show_vbds)
do_vbd(domains[i]);
}
if (show_pcpus) {
if (update_pcpu_stats(&curtime, delay) == 0) {
print_pcpu_stats_display();
} else {
fail("Error getting PCPU stats\n");
}
}
if (!batch)
do_bottom_line();
free(domains);
}
static int signal_exit;
static void signal_exit_handler(int sig)
{
signal_exit = 1;
}
/* Parse a numeric command line argument as a plain decimal integer no
* smaller than min_val. Anything else - a sign, a fractional part,
* trailing junk, overflow - is fatal, rather than being silently
* accepted as a wrong value the way atoi() would.
*/
static unsigned int parse_uint_arg(const char *name, const char *arg,
unsigned int min_val)
{
unsigned long long val;
char *end;
errno = 0;
if (isdigit((unsigned char)arg[0])) {
val = strtoull(arg, &end, 10);
if (!errno && !*end && val >= min_val && val <= UINT_MAX)
return val;
}
fprintf(stderr, "xentop: invalid %s argument '%s': expected a %s decimal integer\n",
name, arg, min_val ? "positive" : "non-negative");
exit(1);
}
int main(int argc, char **argv)
{
int opt, optind = 0;
int ch = ERR;
struct option lopts[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "networks", no_argument, NULL, 'n' },
{ "vbds", no_argument, NULL, 'x' },
{ "repeat-header", no_argument, NULL, 'r' },
{ "vcpus", no_argument, NULL, 'v' },
{ "delay", required_argument, NULL, 'd' },
{ "batch", no_argument, NULL, 'b' },
{ "pcpus", no_argument, NULL, 'p' },
{ "iterations", required_argument, NULL, 'i' },
{ "full-name", no_argument, NULL, 'f' },
{ "dom0-first", no_argument, NULL, 'z' },
{ 0, 0, 0, 0 },
};
const char *sopts = "hVnxrvd:bpi:fz";
if (atexit(cleanup) != 0)
fail("Failed to install cleanup handler.\n");
while ((opt = getopt_long(argc, argv, sopts, lopts, &optind)) != -1) {
switch (opt) {
default:
usage(argv[0]);
exit(1);
case '?':
case 'h':
usage(argv[0]);
exit(0);
case 'V':
version();
exit(0);
case 'n':
show_networks = 1;
break;
case 'x':
show_vbds = 1;
break;
case 'r':
repeat_header = 1;
break;
case 'v':
show_vcpus = 1;
break;
case 'd':
delay = parse_uint_arg("--delay", optarg, 0);
break;
case 'b':
batch = 1;
break;
case 'p':
show_pcpus = 1;
break;
case 'i':
iterations = parse_uint_arg("--iterations", optarg, 1);
loop = 0;
break;
case 'f':
show_full_name = 1;
break;
case 'z':
dom0_first = 1;
break;
}
}
/* Get xenstat handle */
xhandle = xenstat_init();
if (xhandle == NULL)
fail("Failed to initialize xenstat library\n");
if (!batch) {
/* Begin curses stuff */
cwin = initscr();
start_color();
cbreak();
noecho();
nonl();
keypad(stdscr, TRUE);
halfdelay(5);
#ifndef __sun__
use_default_colors();
#endif
init_pair(1, -1, COLOR_YELLOW);
do {
gettimeofday(&curtime, NULL);
if(ch != ERR || (curtime.tv_sec - oldtime.tv_sec) >= delay) {
erase();
top();
oldtime = curtime;
refresh();
if ((!loop) && !(--iterations))
break;
}
ch = getch();
} while (handle_key(ch));
} else {
struct sigaction sa = {
.sa_handler = signal_exit_handler,
.sa_flags = 0
};
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
do {
gettimeofday(&curtime, NULL);
top();
fflush(stdout);
oldtime = curtime;
if ((!loop) && !(--iterations))
break;
sleep(delay);
} while (!signal_exit);
}
/* Cleanup occurs in cleanup(), so no work to do here. */
return 0;
}