8265 lines
185 KiB
Diff
8265 lines
185 KiB
Diff
From 096c2b87270417456ce9d92046838795ccd32ad1 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
|
Date: Fri, 28 Jan 2011 13:16:36 +0100
|
|
Subject: [PATCH 49/61] hyptop: Show hypervisor performance data on System z
|
|
|
|
Summary: hyptop: Show hypervisor performance data on System z
|
|
Description: hyptop provides a dynamic real-time view of a System z hypervisor
|
|
environment. It works with the z/VM and LPAR hypervisor. Depending
|
|
on the available data it shows e.g. CPU and memory consumption of
|
|
active LPARs or z/VM guests. The tool provides a curses based
|
|
user interface similar to the popular Linux 'top' command.
|
|
---
|
|
Makefile | 2 +-
|
|
hyptop/Makefile | 27 +
|
|
hyptop/dg_debugfs.c | 86 ++++
|
|
hyptop/dg_debugfs.h | 20 +
|
|
hyptop/dg_debugfs_lpar.c | 361 ++++++++++++++
|
|
hyptop/dg_debugfs_vm.c | 267 ++++++++++
|
|
hyptop/helper.c | 383 ++++++++++++++
|
|
hyptop/helper.h | 100 ++++
|
|
hyptop/hyptop.8 | 213 ++++++++
|
|
hyptop/hyptop.c | 352 +++++++++++++
|
|
hyptop/hyptop.h | 220 +++++++++
|
|
hyptop/nav_desc.c | 243 +++++++++
|
|
hyptop/nav_desc.h | 55 ++
|
|
hyptop/opts.c | 416 ++++++++++++++++
|
|
hyptop/opts.h | 20 +
|
|
hyptop/sd.h | 479 ++++++++++++++++++
|
|
hyptop/sd_core.c | 435 ++++++++++++++++
|
|
hyptop/sd_cpu_items.c | 178 +++++++
|
|
hyptop/sd_sys_items.c | 325 ++++++++++++
|
|
hyptop/table.c | 1231 ++++++++++++++++++++++++++++++++++++++++++++++
|
|
hyptop/table.h | 424 ++++++++++++++++
|
|
hyptop/table_col_unit.c | 369 ++++++++++++++
|
|
hyptop/tbox.c | 240 +++++++++
|
|
hyptop/tbox.h | 52 ++
|
|
hyptop/win_cpu_types.c | 248 ++++++++++
|
|
hyptop/win_cpu_types.h | 26 +
|
|
hyptop/win_fields.c | 272 ++++++++++
|
|
hyptop/win_fields.h | 31 ++
|
|
hyptop/win_help.c | 122 +++++
|
|
hyptop/win_help.h | 23 +
|
|
hyptop/win_sys.c | 387 +++++++++++++++
|
|
hyptop/win_sys_list.c | 380 ++++++++++++++
|
|
32 files changed, 7986 insertions(+), 1 deletions(-)
|
|
create mode 100644 hyptop/Makefile
|
|
create mode 100644 hyptop/dg_debugfs.c
|
|
create mode 100644 hyptop/dg_debugfs.h
|
|
create mode 100644 hyptop/dg_debugfs_lpar.c
|
|
create mode 100644 hyptop/dg_debugfs_vm.c
|
|
create mode 100644 hyptop/helper.c
|
|
create mode 100644 hyptop/helper.h
|
|
create mode 100644 hyptop/hyptop.8
|
|
create mode 100644 hyptop/hyptop.c
|
|
create mode 100644 hyptop/hyptop.h
|
|
create mode 100644 hyptop/nav_desc.c
|
|
create mode 100644 hyptop/nav_desc.h
|
|
create mode 100644 hyptop/opts.c
|
|
create mode 100644 hyptop/opts.h
|
|
create mode 100644 hyptop/sd.h
|
|
create mode 100644 hyptop/sd_core.c
|
|
create mode 100644 hyptop/sd_cpu_items.c
|
|
create mode 100644 hyptop/sd_sys_items.c
|
|
create mode 100644 hyptop/table.c
|
|
create mode 100644 hyptop/table.h
|
|
create mode 100644 hyptop/table_col_unit.c
|
|
create mode 100644 hyptop/tbox.c
|
|
create mode 100644 hyptop/tbox.h
|
|
create mode 100644 hyptop/win_cpu_types.c
|
|
create mode 100644 hyptop/win_cpu_types.h
|
|
create mode 100644 hyptop/win_fields.c
|
|
create mode 100644 hyptop/win_fields.h
|
|
create mode 100644 hyptop/win_help.c
|
|
create mode 100644 hyptop/win_help.h
|
|
create mode 100644 hyptop/win_sys.c
|
|
create mode 100644 hyptop/win_sys_list.c
|
|
|
|
diff --git a/Makefile b/Makefile
|
|
index 4b798bc..89c5fc5 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -7,7 +7,7 @@ LIB_DIRS = libvtoc libu2s
|
|
SUB_DIRS = $(LIB_DIRS) zipl zdump fdasd dasdfmt dasdview tunedasd \
|
|
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
|
vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
|
|
- ziomon iucvterm
|
|
+ ziomon iucvterm hyptop
|
|
|
|
all: subdirs_make
|
|
|
|
diff --git a/hyptop/Makefile b/hyptop/Makefile
|
|
new file mode 100644
|
|
index 0000000..43d9e0f
|
|
--- /dev/null
|
|
+++ b/hyptop/Makefile
|
|
@@ -0,0 +1,27 @@
|
|
+include ../common.mak
|
|
+
|
|
+CPPFLAGS += -I../include
|
|
+
|
|
+all: hyptop
|
|
+
|
|
+LDLIBS += -lncurses
|
|
+
|
|
+OBJECTS = hyptop.o opts.o helper.o \
|
|
+ sd_core.o sd_sys_items.o sd_cpu_items.o \
|
|
+ tbox.o table.o table_col_unit.o \
|
|
+ dg_debugfs.o dg_debugfs_lpar.o dg_debugfs_vm.o \
|
|
+ win_sys_list.o win_sys.o win_fields.o \
|
|
+ win_cpu_types.o win_help.o nav_desc.o
|
|
+
|
|
+$(OBJECTS): *.h Makefile
|
|
+
|
|
+hyptop: $(OBJECTS)
|
|
+
|
|
+install: all
|
|
+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 hyptop $(USRSBINDIR)
|
|
+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 hyptop.8 $(MANDIR)/man8
|
|
+
|
|
+clean:
|
|
+ rm -f *.o *~ hyptop core
|
|
+
|
|
+.PHONY: all install clean
|
|
diff --git a/hyptop/dg_debugfs.c b/hyptop/dg_debugfs.c
|
|
new file mode 100644
|
|
index 0000000..13a6342
|
|
--- /dev/null
|
|
+++ b/hyptop/dg_debugfs.c
|
|
@@ -0,0 +1,86 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Common functions for debugfs data gatherer
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <errno.h>
|
|
+#include <string.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <fcntl.h>
|
|
+#include "helper.h"
|
|
+#include "hyptop.h"
|
|
+#include "dg_debugfs.h"
|
|
+
|
|
+static char *l_debugfs_dir;
|
|
+
|
|
+static void l_check_rc(int rc, int exit_on_err)
|
|
+{
|
|
+ if (!exit_on_err)
|
|
+ return;
|
|
+ if (rc == -EACCES)
|
|
+ ERR_EXIT("Permission denied, check \"%s/s390_hypfs/\"\n",
|
|
+ l_debugfs_dir);
|
|
+ if (rc != -ENOENT)
|
|
+ ERR_EXIT("Could not initialize data gatherer (%s)\n",
|
|
+ strerror(-rc));
|
|
+}
|
|
+
|
|
+static void l_check_rc_final(int rc, int exit_on_err)
|
|
+{
|
|
+ if (!exit_on_err)
|
|
+ return;
|
|
+ l_check_rc(rc, exit_on_err);
|
|
+ ERR_EXIT("Could not initialize data gatherer (%s)\n", strerror(-rc));
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize debugfs data gatherer backend
|
|
+ */
|
|
+int dg_debugfs_init(int exit_on_err)
|
|
+{
|
|
+ int rc;
|
|
+
|
|
+ l_debugfs_dir = ht_mount_point_get("debugfs");
|
|
+ if (!l_debugfs_dir) {
|
|
+ if (!exit_on_err)
|
|
+ return -ENODEV;
|
|
+ ERR_EXIT("Debugfs is not mounted, try \"mount none -t debugfs "
|
|
+ "/sys/kernel/debug\"\n");
|
|
+ }
|
|
+ rc = dg_debugfs_vm_init();
|
|
+ if (rc == 0)
|
|
+ return 0;
|
|
+ else
|
|
+ l_check_rc(rc, exit_on_err);
|
|
+ rc = dg_debugfs_lpar_init();
|
|
+ if (rc == 0)
|
|
+ return 0;
|
|
+ else
|
|
+ l_check_rc_final(rc, exit_on_err);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Open a debugfs file
|
|
+ */
|
|
+int dg_debugfs_open(const char *file)
|
|
+{
|
|
+ char path[PATH_MAX];
|
|
+ int fh;
|
|
+
|
|
+ path[0] = 0;
|
|
+ strcat(path, l_debugfs_dir);
|
|
+ strcat(path, "/s390_hypfs/");
|
|
+ strcat(path, file);
|
|
+ fh = open(path, O_RDONLY);
|
|
+ if (fh == -1)
|
|
+ return -errno;
|
|
+ else
|
|
+ return fh;
|
|
+}
|
|
+
|
|
diff --git a/hyptop/dg_debugfs.h b/hyptop/dg_debugfs.h
|
|
new file mode 100644
|
|
index 0000000..f46956c
|
|
--- /dev/null
|
|
+++ b/hyptop/dg_debugfs.h
|
|
@@ -0,0 +1,20 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Common functions for debugfs data gatherer
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef DG_DEBUGFS_H
|
|
+#define DG_DEBUGFS_H
|
|
+
|
|
+#define DBFS_WAIT_TIME_US 10000
|
|
+
|
|
+extern int dg_debugfs_init(int exit_on_err);
|
|
+extern int dg_debugfs_vm_init(void);
|
|
+extern int dg_debugfs_lpar_init(void);
|
|
+extern int dg_debugfs_open(const char *file);
|
|
+
|
|
+#endif /* DG_DEBUGFS_H */
|
|
diff --git a/hyptop/dg_debugfs_lpar.c b/hyptop/dg_debugfs_lpar.c
|
|
new file mode 100644
|
|
index 0000000..4ae1b6d
|
|
--- /dev/null
|
|
+++ b/hyptop/dg_debugfs_lpar.c
|
|
@@ -0,0 +1,361 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Hyptop LPAR data gatherer that operates on debugfs
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <unistd.h>
|
|
+#include <string.h>
|
|
+#include <iconv.h>
|
|
+#include <errno.h>
|
|
+#include "hyptop.h"
|
|
+#include "sd.h"
|
|
+#include "helper.h"
|
|
+#include "dg_debugfs.h"
|
|
+
|
|
+#define LPAR_NAME_LEN 8
|
|
+#define TMP_SIZE 64
|
|
+#define LPAR_PHYS_FLG 0x80
|
|
+#define CPU_TYPE_LEN 16
|
|
+#define DEBUGFS_FILE "diag_204"
|
|
+
|
|
+static u64 l_update_time_us;
|
|
+static long l_204_buf_size;
|
|
+
|
|
+/*
|
|
+ * Diag data structure definition
|
|
+ */
|
|
+
|
|
+struct l_x_info_blk_hdr {
|
|
+ u8 npar;
|
|
+ u8 flags;
|
|
+ u8 reserved1[6];
|
|
+ u64 curtod1;
|
|
+ u64 curtod2;
|
|
+ u8 reserved[40];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct l_x_sys_hdr {
|
|
+ u8 reserved1;
|
|
+ u8 cpus;
|
|
+ u8 rcpus;
|
|
+ u8 reserved2[5];
|
|
+ char sys_name[LPAR_NAME_LEN];
|
|
+ u8 reserved3[80];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+static inline void l_sys_hdr__sys_name(struct l_x_sys_hdr *hdr, char *name)
|
|
+{
|
|
+ memcpy(name, hdr->sys_name, LPAR_NAME_LEN);
|
|
+ ht_ebcdic_to_ascii(name, LPAR_NAME_LEN);
|
|
+ name[LPAR_NAME_LEN] = 0;
|
|
+ ht_strstrip(name);
|
|
+}
|
|
+
|
|
+struct l_x_cpu_info {
|
|
+ u16 cpu_addr;
|
|
+ u8 reserved1[2];
|
|
+ u8 ctidx;
|
|
+ u8 reserved2[3];
|
|
+ u64 acc_time;
|
|
+ u64 lp_time;
|
|
+ u8 reserved3[6];
|
|
+ u8 reserved4[2];
|
|
+ u64 online_time;
|
|
+ u8 reserved5[56];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+static void l_idx2name(int index, char *name)
|
|
+{
|
|
+ switch (index) {
|
|
+ case 0:
|
|
+ strcpy(name, SD_CPU_TYPE_STR_CP);
|
|
+ break;
|
|
+ case 3:
|
|
+ strcpy(name, SD_CPU_TYPE_STR_IFL);
|
|
+ break;
|
|
+ default:
|
|
+ strcpy(name, SD_CPU_TYPE_STR_UN);
|
|
+ }
|
|
+}
|
|
+
|
|
+struct l_x_phys_hdr {
|
|
+ u8 reserved1[1];
|
|
+ u8 cpus;
|
|
+ u8 reserved2[94];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct l_x_phys_cpu {
|
|
+ u16 cpu_addr;
|
|
+ u8 reserved1[2];
|
|
+ u8 ctidx;
|
|
+ u8 reserved2[3];
|
|
+ u64 mgm_time;
|
|
+ u8 reserved3[80];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * Fill CPU with data
|
|
+ */
|
|
+static void l_sd_cpu_fill(struct sd_cpu *cpu, struct l_x_cpu_info *cpu_info)
|
|
+{
|
|
+ sd_cpu_cpu_time_us_set(cpu, cpu_info->lp_time);
|
|
+ sd_cpu_mgm_time_us_set(cpu, G0(cpu_info->acc_time - cpu_info->lp_time));
|
|
+ sd_cpu_online_time_us_set(cpu, cpu_info->online_time);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Fill system with data
|
|
+ */
|
|
+static void *l_sd_sys_fill(struct sd_sys *lpar, struct l_x_sys_hdr *sys_hdr)
|
|
+{
|
|
+ struct l_x_cpu_info *cpu_info;
|
|
+ int i;
|
|
+
|
|
+ cpu_info = (struct l_x_cpu_info *) (sys_hdr + 1);
|
|
+
|
|
+ for (i = 0; i < sys_hdr->rcpus; i++) {
|
|
+ char cpu_type[CPU_TYPE_LEN + 1];
|
|
+ struct sd_cpu *cpu;
|
|
+ char cpu_id[10];
|
|
+
|
|
+ sprintf(cpu_id, "%i", cpu_info->cpu_addr);
|
|
+
|
|
+ cpu = sd_cpu_get(lpar, cpu_id);
|
|
+ if (!cpu) {
|
|
+ l_idx2name(cpu_info->ctidx, cpu_type);
|
|
+ cpu = sd_cpu_new(lpar, cpu_id, cpu_type, 1);
|
|
+ }
|
|
+
|
|
+ l_sd_cpu_fill(cpu, cpu_info);
|
|
+
|
|
+ sd_cpu_commit(cpu);
|
|
+ cpu_info++;
|
|
+ }
|
|
+ return cpu_info;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Fill one physical CPU with data
|
|
+ */
|
|
+static void l_sd_cpu_phys_fill(struct sd_sys *sys,
|
|
+ struct l_x_phys_cpu *cpu_info)
|
|
+{
|
|
+ char cpu_type[CPU_TYPE_LEN + 1];
|
|
+ char cpu_id[TMP_SIZE];
|
|
+ struct sd_cpu *cpu;
|
|
+
|
|
+ snprintf(cpu_id, TMP_SIZE, "%i", cpu_info->cpu_addr);
|
|
+ cpu = sd_cpu_get(sys, cpu_id);
|
|
+ if (!cpu) {
|
|
+ l_idx2name(cpu_info->ctidx, cpu_type);
|
|
+ cpu = sd_cpu_new(sys, cpu_id, cpu_type, 1);
|
|
+ }
|
|
+ sd_cpu_mgm_time_us_set(cpu, cpu_info->mgm_time);
|
|
+ sd_cpu_real_type_set(cpu, cpu_type);
|
|
+ sd_cpu_commit(cpu);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Fill all physical CPUs with data
|
|
+ */
|
|
+static void l_sd_sys_root_cpu_phys_fill(struct sd_sys *sys,
|
|
+ struct l_x_phys_hdr *phys_hdr)
|
|
+{
|
|
+ struct l_x_phys_cpu *cpu_info;
|
|
+ int i;
|
|
+
|
|
+ cpu_info = (struct l_x_phys_cpu *) (phys_hdr + 1);
|
|
+ for (i = 0; i < phys_hdr->cpus; i++) {
|
|
+ l_sd_cpu_phys_fill(sys, cpu_info);
|
|
+ cpu_info++;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Header for debugfs file "diag_204"
|
|
+ */
|
|
+struct l_debugfs_d204_hdr {
|
|
+ u64 len;
|
|
+ u16 version;
|
|
+ u8 reserved[54];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct l_debugfs_d204 {
|
|
+ struct l_debugfs_d204_hdr h;
|
|
+ char buf[];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * Read debugfs file
|
|
+ */
|
|
+static void l_read_debugfs(struct l_debugfs_d204_hdr **hdr,
|
|
+ struct l_x_info_blk_hdr **data)
|
|
+{
|
|
+ long real_buf_size;
|
|
+ ssize_t rc;
|
|
+ void *buf;
|
|
+ int fh;
|
|
+
|
|
+ do {
|
|
+ fh = dg_debugfs_open(DEBUGFS_FILE);
|
|
+ *hdr = buf = ht_alloc(l_204_buf_size);
|
|
+ rc = read(fh, buf, l_204_buf_size);
|
|
+ if (rc == -1)
|
|
+ ERR_EXIT_ERRNO("Reading hypervisor data failed");
|
|
+ close(fh);
|
|
+ real_buf_size = (*hdr)->len + sizeof(struct l_debugfs_d204_hdr);
|
|
+ if (rc == real_buf_size)
|
|
+ break;
|
|
+ l_204_buf_size = real_buf_size;
|
|
+ ht_free(buf);
|
|
+ } while (1);
|
|
+ *data = buf + sizeof(struct l_debugfs_d204_hdr);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Fill System Data
|
|
+ */
|
|
+static void l_sd_sys_root_fill(struct sd_sys *sys)
|
|
+{
|
|
+ struct l_x_info_blk_hdr *time_hdr;
|
|
+ struct l_debugfs_d204_hdr *hdr;
|
|
+ struct l_x_sys_hdr *sys_hdr;
|
|
+ static int first = 1;
|
|
+ struct sd_sys *lpar;
|
|
+ char lpar_id[10];
|
|
+ int i;
|
|
+
|
|
+ do {
|
|
+ l_read_debugfs(&hdr, &time_hdr);
|
|
+ if (l_update_time_us != ht_ext_tod_2_us(&time_hdr->curtod1)) {
|
|
+ l_update_time_us = ht_ext_tod_2_us(&time_hdr->curtod1);
|
|
+ break;
|
|
+ }
|
|
+ /*
|
|
+ * Got old snapshot from kernel. Wait some time until
|
|
+ * new snapshot is available.
|
|
+ */
|
|
+ ht_free(hdr);
|
|
+ usleep(DBFS_WAIT_TIME_US);
|
|
+ } while (1);
|
|
+ sys_hdr = ((void *) time_hdr) + sizeof(struct l_x_info_blk_hdr);
|
|
+ for (i = 0; i < time_hdr->npar; i++) {
|
|
+ l_sys_hdr__sys_name(sys_hdr, lpar_id);
|
|
+ lpar = sd_sys_get(sys, lpar_id);
|
|
+ if (!lpar)
|
|
+ lpar = sd_sys_new(sys, lpar_id);
|
|
+ sys_hdr = l_sd_sys_fill(lpar, sys_hdr);
|
|
+ sd_sys_commit(lpar);
|
|
+ }
|
|
+
|
|
+ if (first && (time_hdr->flags & LPAR_PHYS_FLG)) {
|
|
+ l_sd_sys_root_cpu_phys_fill(sys, (void *) sys_hdr);
|
|
+ first = 0;
|
|
+ }
|
|
+ ht_free(hdr);
|
|
+ sd_sys_commit(sys);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Update system data
|
|
+ */
|
|
+static void l_sd_update(void)
|
|
+{
|
|
+ struct sd_sys *root = sd_sys_root_get();
|
|
+
|
|
+ sd_sys_update_start(root);
|
|
+ l_sd_sys_root_fill(root);
|
|
+ sd_sys_update_end(root, l_update_time_us);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Supported system items
|
|
+ */
|
|
+static struct sd_sys_item *l_sys_item_vec[] = {
|
|
+ &sd_sys_item_cpu_cnt,
|
|
+ &sd_sys_item_cpu_diff,
|
|
+ &sd_sys_item_mgm_diff,
|
|
+ &sd_sys_item_cpu,
|
|
+ &sd_sys_item_mgm,
|
|
+ &sd_sys_item_online,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Default system items
|
|
+ */
|
|
+static struct sd_sys_item *l_sys_item_enable_vec[] = {
|
|
+ &sd_sys_item_cpu_cnt,
|
|
+ &sd_sys_item_cpu_diff,
|
|
+ &sd_sys_item_mgm_diff,
|
|
+ &sd_sys_item_cpu,
|
|
+ &sd_sys_item_mgm,
|
|
+ &sd_sys_item_online,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Supported CPU items
|
|
+ */
|
|
+static struct sd_cpu_item *l_cpu_item_vec[] = {
|
|
+ &sd_cpu_item_type,
|
|
+ &sd_cpu_item_cpu_diff,
|
|
+ &sd_cpu_item_mgm_diff,
|
|
+ &sd_cpu_item_cpu,
|
|
+ &sd_cpu_item_mgm,
|
|
+ &sd_cpu_item_online,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Default CPU items
|
|
+ */
|
|
+static struct sd_cpu_item *l_cpu_item_enable_vec[] = {
|
|
+ &sd_cpu_item_type,
|
|
+ &sd_cpu_item_cpu_diff,
|
|
+ &sd_cpu_item_mgm_diff,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Supported CPU types
|
|
+ */
|
|
+static struct sd_cpu_type *l_cpu_type_vec[] = {
|
|
+ &sd_cpu_type_ifl,
|
|
+ &sd_cpu_type_cp,
|
|
+ &sd_cpu_type_un,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Define data gatherer structure
|
|
+ */
|
|
+static struct sd_dg l_sd_dg = {
|
|
+ .update_sys = l_sd_update,
|
|
+ .cpu_type_vec = l_cpu_type_vec,
|
|
+ .sys_item_vec = l_sys_item_vec,
|
|
+ .sys_item_enable_vec = l_sys_item_enable_vec,
|
|
+ .cpu_item_vec = l_cpu_item_vec,
|
|
+ .cpu_item_enable_vec = l_cpu_item_enable_vec,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Initialize LPAR debugfs data gatherer
|
|
+ */
|
|
+int dg_debugfs_lpar_init(void)
|
|
+{
|
|
+ int fh;
|
|
+
|
|
+ l_204_buf_size = sizeof(struct l_debugfs_d204_hdr);
|
|
+ fh = dg_debugfs_open(DEBUGFS_FILE);
|
|
+ if (fh < 0)
|
|
+ return fh;
|
|
+ else
|
|
+ close(fh);
|
|
+ sd_dg_register(&l_sd_dg);
|
|
+ return 0;
|
|
+}
|
|
diff --git a/hyptop/dg_debugfs_vm.c b/hyptop/dg_debugfs_vm.c
|
|
new file mode 100644
|
|
index 0000000..6aec28f
|
|
--- /dev/null
|
|
+++ b/hyptop/dg_debugfs_vm.c
|
|
@@ -0,0 +1,267 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Hyptop z/VM data gatherer that operates on debugfs
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <stdlib.h>
|
|
+#include <unistd.h>
|
|
+#include <errno.h>
|
|
+#include "hyptop.h"
|
|
+#include "sd.h"
|
|
+#include "helper.h"
|
|
+#include "dg_debugfs.h"
|
|
+
|
|
+#define VM_CPU_TYPE "UN"
|
|
+#define VM_CPU_ID "ALL"
|
|
+#define NAME_LEN 8
|
|
+#define DEBUGFS_FILE "diag_2fc"
|
|
+
|
|
+static u64 l_update_time_us;
|
|
+static long l_2fc_buf_size;
|
|
+
|
|
+/*
|
|
+ * Diag 2fc data structure definition
|
|
+ */
|
|
+struct l_diag2fc_data {
|
|
+ u32 version;
|
|
+ u32 flags;
|
|
+ u64 used_cpu;
|
|
+ u64 el_time;
|
|
+ u64 mem_min_kb;
|
|
+ u64 mem_max_kb;
|
|
+ u64 mem_share_kb;
|
|
+ u64 mem_used_kb;
|
|
+ u32 pcpus;
|
|
+ u32 lcpus;
|
|
+ u32 vcpus;
|
|
+ u32 cpu_min;
|
|
+ u32 cpu_max;
|
|
+ u32 cpu_shares;
|
|
+ u32 cpu_use_samp;
|
|
+ u32 cpu_delay_samp;
|
|
+ u32 page_wait_samp;
|
|
+ u32 idle_samp;
|
|
+ u32 other_samp;
|
|
+ u32 total_samp;
|
|
+ char guest_name[NAME_LEN];
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Header for debugfs file "diag_2fc"
|
|
+ */
|
|
+struct l_debugfs_d2fc_hdr {
|
|
+ u64 len;
|
|
+ u16 version;
|
|
+ char tod_ext[16];
|
|
+ u64 count;
|
|
+ char reserved[30];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct l_debugfs_d2fc {
|
|
+ struct l_debugfs_d2fc_hdr h;
|
|
+ char diag2fc_buf[];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * Fill "guest" with data
|
|
+ */
|
|
+static void l_sd_sys_fill(struct sd_sys *guest, struct l_diag2fc_data *data)
|
|
+{
|
|
+ struct sd_cpu *cpu;
|
|
+
|
|
+ cpu = sd_cpu_get(guest, VM_CPU_ID);
|
|
+ if (!cpu)
|
|
+ cpu = sd_cpu_new(guest, VM_CPU_ID, SD_CPU_TYPE_STR_UN,
|
|
+ data->vcpus);
|
|
+
|
|
+ sd_cpu_cnt(cpu) = data->vcpus;
|
|
+ sd_cpu_cpu_time_us_set(cpu, data->used_cpu);
|
|
+ sd_cpu_online_time_us_set(cpu, data->el_time);
|
|
+
|
|
+ sd_sys_weight_cur_set(guest, data->cpu_shares);
|
|
+ sd_sys_weight_min_set(guest, data->cpu_min);
|
|
+ sd_sys_weight_max_set(guest, data->cpu_max);
|
|
+
|
|
+ sd_sys_mem_min_kib_set(guest, data->mem_min_kb);
|
|
+ sd_sys_mem_max_kib_set(guest, data->mem_max_kb);
|
|
+ sd_sys_mem_use_kib_set(guest, data->mem_used_kb);
|
|
+
|
|
+ sd_sys_update_time_us_set(guest, l_update_time_us);
|
|
+ sd_sys_commit(guest);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read debugfs file
|
|
+ */
|
|
+static void l_read_debugfs(struct l_debugfs_d2fc_hdr **hdr,
|
|
+ struct l_diag2fc_data **data)
|
|
+{
|
|
+ long real_buf_size;
|
|
+ ssize_t rc;
|
|
+ void *buf;
|
|
+ int fh;
|
|
+
|
|
+ do {
|
|
+ fh = dg_debugfs_open(DEBUGFS_FILE);
|
|
+ *hdr = buf = ht_alloc(l_2fc_buf_size);
|
|
+ rc = read(fh, buf, l_2fc_buf_size);
|
|
+ if (rc == -1)
|
|
+ ERR_EXIT_ERRNO("Reading hypervisor data failed");
|
|
+ close(fh);
|
|
+ real_buf_size = (*hdr)->len + sizeof(struct l_debugfs_d2fc_hdr);
|
|
+ if (rc == real_buf_size)
|
|
+ break;
|
|
+ l_2fc_buf_size = real_buf_size;
|
|
+ ht_free(buf);
|
|
+ } while (1);
|
|
+ *data = buf + sizeof(struct l_debugfs_d2fc_hdr);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Fill System Data
|
|
+ */
|
|
+static void l_sd_sys_root_fill(struct sd_sys *sys)
|
|
+{
|
|
+ struct l_diag2fc_data *d2fc_data;
|
|
+ struct l_debugfs_d2fc_hdr *hdr;
|
|
+ struct sd_cpu *cpu;
|
|
+ unsigned int i;
|
|
+
|
|
+ do {
|
|
+ l_read_debugfs(&hdr, &d2fc_data);
|
|
+ if (l_update_time_us != ht_ext_tod_2_us(&hdr->tod_ext)) {
|
|
+ l_update_time_us = ht_ext_tod_2_us(&hdr->tod_ext);
|
|
+ break;
|
|
+ }
|
|
+ /*
|
|
+ * Got old snapshot from kernel. Wait some time until
|
|
+ * new snapshot is available.
|
|
+ */
|
|
+ ht_free(hdr);
|
|
+ usleep(DBFS_WAIT_TIME_US);
|
|
+ } while (1);
|
|
+
|
|
+ cpu = sd_cpu_get(sys, VM_CPU_ID);
|
|
+ if (!cpu)
|
|
+ cpu = sd_cpu_new(sys, VM_CPU_ID, SD_CPU_TYPE_STR_UN,
|
|
+ d2fc_data[0].lcpus);
|
|
+
|
|
+ for (i = 0; i < hdr->count; i++) {
|
|
+ struct l_diag2fc_data *data = &d2fc_data[i];
|
|
+ char guest_name[NAME_LEN + 1];
|
|
+ struct sd_sys *guest;
|
|
+
|
|
+ guest_name[NAME_LEN] = 0;
|
|
+ memcpy(guest_name, data->guest_name, NAME_LEN);
|
|
+ ht_ebcdic_to_ascii(guest_name, NAME_LEN);
|
|
+ ht_strstrip(guest_name);
|
|
+
|
|
+ guest = sd_sys_get(sys, guest_name);
|
|
+ if (!guest)
|
|
+ guest = sd_sys_new(sys, guest_name);
|
|
+ l_sd_sys_fill(guest, data);
|
|
+ }
|
|
+ ht_free(hdr);
|
|
+ sd_sys_commit(sys);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Update system data
|
|
+ */
|
|
+static void l_sd_update(void)
|
|
+{
|
|
+ struct sd_sys *root = sd_sys_root_get();
|
|
+
|
|
+ sd_sys_update_start(root);
|
|
+ l_sd_sys_root_fill(root);
|
|
+ sd_sys_update_end(root, l_update_time_us);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Supported system items
|
|
+ */
|
|
+static struct sd_sys_item *l_sys_item_vec[] = {
|
|
+ &sd_sys_item_cpu_cnt,
|
|
+ &sd_sys_item_cpu_diff,
|
|
+ &sd_sys_item_cpu,
|
|
+ &sd_sys_item_online,
|
|
+ &sd_sys_item_mem_use,
|
|
+ &sd_sys_item_mem_max,
|
|
+ &sd_sys_item_weight_min,
|
|
+ &sd_sys_item_weight_cur,
|
|
+ &sd_sys_item_weight_max,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Default system items
|
|
+ */
|
|
+static struct sd_sys_item *l_sys_item_enable_vec[] = {
|
|
+ &sd_sys_item_cpu_cnt,
|
|
+ &sd_sys_item_cpu_diff,
|
|
+ &sd_sys_item_cpu,
|
|
+ &sd_sys_item_online,
|
|
+ &sd_sys_item_mem_max,
|
|
+ &sd_sys_item_mem_use,
|
|
+ &sd_sys_item_weight_cur,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Supported CPU items
|
|
+ */
|
|
+static struct sd_cpu_item *l_cpu_item_vec[] = {
|
|
+ &sd_cpu_item_cpu_diff,
|
|
+ &sd_cpu_item_cpu,
|
|
+ &sd_cpu_item_online,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Default CPU items
|
|
+ */
|
|
+static struct sd_cpu_item *l_cpu_item_enable_vec[] = {
|
|
+ &sd_cpu_item_cpu_diff,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Supported CPU types
|
|
+ */
|
|
+static struct sd_cpu_type *l_cpu_type_vec[] = {
|
|
+ &sd_cpu_type_un,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Define data gatherer structure
|
|
+ */
|
|
+static struct sd_dg dg_debugfs_vm_dg = {
|
|
+ .update_sys = l_sd_update,
|
|
+ .cpu_type_vec = l_cpu_type_vec,
|
|
+ .sys_item_vec = l_sys_item_vec,
|
|
+ .sys_item_enable_vec = l_sys_item_enable_vec,
|
|
+ .cpu_item_vec = l_cpu_item_vec,
|
|
+ .cpu_item_enable_vec = l_cpu_item_enable_vec,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Initialize z/VM debugfs data gatherer
|
|
+ */
|
|
+int dg_debugfs_vm_init(void)
|
|
+{
|
|
+ int fh;
|
|
+
|
|
+ fh = dg_debugfs_open(DEBUGFS_FILE);
|
|
+ if (fh < 0)
|
|
+ return fh;
|
|
+ else
|
|
+ close(fh);
|
|
+ l_2fc_buf_size = sizeof(struct l_debugfs_d2fc_hdr);
|
|
+ sd_dg_register(&dg_debugfs_vm_dg);
|
|
+ return 0;
|
|
+}
|
|
diff --git a/hyptop/helper.c b/hyptop/helper.c
|
|
new file mode 100644
|
|
index 0000000..bcdea9f
|
|
--- /dev/null
|
|
+++ b/hyptop/helper.c
|
|
@@ -0,0 +1,383 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Helper functions
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ * Christian Borntraeger <borntraeger@de.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <mntent.h>
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <string.h>
|
|
+#include <unistd.h>
|
|
+#include <ctype.h>
|
|
+#include <iconv.h>
|
|
+#include <limits.h>
|
|
+#include <time.h>
|
|
+#include <sys/time.h>
|
|
+#include <mntent.h>
|
|
+#include <errno.h>
|
|
+#include <assert.h>
|
|
+#include <sys/stat.h>
|
|
+#include <sys/types.h>
|
|
+#include "helper.h"
|
|
+#include "hyptop.h"
|
|
+#include "sd.h"
|
|
+
|
|
+/*
|
|
+ * Globals
|
|
+ */
|
|
+static iconv_t l_iconv_ebcdic_ascii;
|
|
+static int l_underline_cnt;
|
|
+static int l_reverse_cnt;
|
|
+static int l_bold_cnt;
|
|
+
|
|
+/*
|
|
+ * Print time of day
|
|
+ */
|
|
+void ht_print_time(void)
|
|
+{
|
|
+ char time_str[40];
|
|
+ struct timeval tv;
|
|
+ struct tm *tm;
|
|
+
|
|
+ gettimeofday(&tv, NULL);
|
|
+ tm = localtime(&tv.tv_sec);
|
|
+ strftime(time_str, sizeof(time_str), "%H:%M:%S", tm);
|
|
+ hyptop_printf("%s", time_str);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Alloc uninitialized memory and exit on failure
|
|
+ */
|
|
+void *ht_alloc(size_t size)
|
|
+{
|
|
+ void *ptr;
|
|
+
|
|
+ ptr = malloc(size);
|
|
+ if (!ptr)
|
|
+ ERR_EXIT("Out of memory (%zu Kb)", size / 1024);
|
|
+ return ptr;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Alloc memory initialized with "0" and exit on failure
|
|
+ */
|
|
+void *ht_zalloc(size_t size)
|
|
+{
|
|
+ void *ptr;
|
|
+
|
|
+ ptr = calloc(1, size);
|
|
+ if (!ptr)
|
|
+ ERR_EXIT("Out of memory (%zu Kb)", size / 1024);
|
|
+ return ptr;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Realloc memory and exit on failure
|
|
+ */
|
|
+void *ht_realloc(void *old_ptr, size_t size)
|
|
+{
|
|
+ void *ptr;
|
|
+
|
|
+ assert(size != 0);
|
|
+ if (old_ptr)
|
|
+ ptr = realloc(old_ptr, size);
|
|
+ else
|
|
+ ptr = calloc(1, size);
|
|
+ if (!ptr)
|
|
+ ERR_EXIT("Out of memory (%lu Kb)", (unsigned long) size / 1024);
|
|
+ return ptr;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Convert EBCDIC string to ASCII
|
|
+ */
|
|
+void ht_ebcdic_to_ascii(char *inout, size_t len)
|
|
+{
|
|
+ iconv(l_iconv_ebcdic_ascii, &inout, &len, &inout, &len);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get mount point for file system tye "fs_type"
|
|
+ */
|
|
+char *ht_mount_point_get(const char *fs_type)
|
|
+{
|
|
+ struct mntent *mntbuf;
|
|
+ FILE *mounts;
|
|
+
|
|
+ mounts = setmntent(_PATH_MOUNTED, "r");
|
|
+ if (!mounts)
|
|
+ ERR_EXIT_ERRNO("Could not find \"%s\" mount point", fs_type);
|
|
+ while ((mntbuf = getmntent(mounts)) != NULL) {
|
|
+ if (strcmp(mntbuf->mnt_type, fs_type) == 0)
|
|
+ return ht_strdup(mntbuf->mnt_dir);
|
|
+ }
|
|
+ endmntent(mounts);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Remove all trailing blanks and reture pointer to first non blank character
|
|
+ */
|
|
+char *ht_strstrip(char *s)
|
|
+{
|
|
+ size_t size;
|
|
+ char *end;
|
|
+
|
|
+ size = strlen(s);
|
|
+
|
|
+ if (!size)
|
|
+ return s;
|
|
+
|
|
+ end = s + size - 1;
|
|
+ while (end >= s && isspace(*end))
|
|
+ end--;
|
|
+ *(end + 1) = '\0';
|
|
+
|
|
+ while (*s && isspace(*s))
|
|
+ s++;
|
|
+
|
|
+ return s;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return copy of string
|
|
+ */
|
|
+char *ht_strdup(const char *str)
|
|
+{
|
|
+ char *rc;
|
|
+
|
|
+ rc = ht_alloc(strlen(str) + 1);
|
|
+ strcpy(rc, str);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print help icon in current line
|
|
+ */
|
|
+void ht_print_help_icon(void)
|
|
+{
|
|
+ hyptop_print_seek_back(6);
|
|
+ ht_underline_on();
|
|
+ hyptop_printf("?");
|
|
+ ht_underline_off();
|
|
+ hyptop_printf("=help");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print headline
|
|
+ */
|
|
+void ht_print_head(const char *sys)
|
|
+{
|
|
+ struct sd_cpu_type *cpu_type;
|
|
+ int i;
|
|
+
|
|
+ ht_print_time();
|
|
+ hyptop_printf(" ");
|
|
+ if (sys) {
|
|
+ ht_bold_on();
|
|
+ hyptop_printf("%s", sys);
|
|
+ ht_bold_off();
|
|
+ hyptop_printf(" ");
|
|
+ }
|
|
+ hyptop_printf("CPU-");
|
|
+ ht_underline_on();
|
|
+ hyptop_printf("T");
|
|
+ ht_underline_off();
|
|
+ hyptop_printf(": ");
|
|
+
|
|
+ sd_cpu_type_iterate(cpu_type, i) {
|
|
+ if (!sd_cpu_type_selected(cpu_type))
|
|
+ continue;
|
|
+ hyptop_printf("%s(%i) ", sd_cpu_type_id(cpu_type),
|
|
+ sd_cpu_type_cpu_cnt(cpu_type));
|
|
+ }
|
|
+ ht_print_help_icon();
|
|
+ hyptop_print_nl();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Curses attribute functions
|
|
+ */
|
|
+static void ht_attr_on(int attr)
|
|
+{
|
|
+ if (g.o.batch_mode_specified)
|
|
+ return;
|
|
+ attron(attr);
|
|
+}
|
|
+
|
|
+static void ht_attr_off(int attr)
|
|
+{
|
|
+ if (g.o.batch_mode_specified)
|
|
+ return;
|
|
+ attroff(attr);
|
|
+}
|
|
+
|
|
+void ht_bold_on(void)
|
|
+{
|
|
+ if (l_bold_cnt == 0)
|
|
+ ht_attr_on(A_BOLD);
|
|
+ l_bold_cnt++;
|
|
+}
|
|
+
|
|
+void ht_bold_off(void)
|
|
+{
|
|
+
|
|
+ l_bold_cnt--;
|
|
+ if (l_bold_cnt == 0)
|
|
+ ht_attr_off(A_BOLD);
|
|
+}
|
|
+
|
|
+void ht_underline_on(void)
|
|
+{
|
|
+ if (l_underline_cnt == 0)
|
|
+ ht_attr_on(A_UNDERLINE);
|
|
+ l_underline_cnt++;
|
|
+}
|
|
+
|
|
+void ht_underline_off(void)
|
|
+{
|
|
+ l_underline_cnt--;
|
|
+ if (l_underline_cnt == 0)
|
|
+ ht_attr_off(A_UNDERLINE);
|
|
+}
|
|
+
|
|
+void ht_reverse_on(void)
|
|
+{
|
|
+ if (l_reverse_cnt == 0)
|
|
+ ht_attr_on(A_REVERSE);
|
|
+ l_reverse_cnt++;
|
|
+}
|
|
+
|
|
+void ht_reverse_off(void)
|
|
+{
|
|
+ l_reverse_cnt--;
|
|
+ if (l_reverse_cnt == 0)
|
|
+ ht_attr_off(A_REVERSE);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print scroll bar
|
|
+ */
|
|
+void ht_print_scroll_bar(int row_cnt, int row_start, int rows_add_top,
|
|
+ int rows_add_bottom, int can_scroll_up,
|
|
+ int can_scroll_down, int with_border)
|
|
+{
|
|
+ int row_cnt_displ, bar_len, start, i;
|
|
+ double scale1, scale2;
|
|
+
|
|
+ row_cnt_displ = MIN(row_cnt, g.c.row_cnt - rows_add_top
|
|
+ - rows_add_bottom);
|
|
+ if (row_cnt_displ <= 0)
|
|
+ return;
|
|
+ /* scale1: Scaling factor virtual screen to physical screen */
|
|
+ scale1 = ((double) row_cnt_displ) / ((double) row_cnt);
|
|
+ /* scale2: Scaling factor physical screen to scroll bar size */
|
|
+ scale2 = ((double) row_cnt_displ - 2) / row_cnt_displ;
|
|
+ bar_len = MAX(((double) row_cnt_displ * scale1 * scale2 + 0.5), 1);
|
|
+ /* start: Start row in scroll bar */
|
|
+ start = ((double) row_start) * scale1 * scale2 + 0.5;
|
|
+
|
|
+ if (row_cnt_displ - 2 - start < bar_len)
|
|
+ start = row_cnt_displ - 2 - bar_len;
|
|
+
|
|
+ ht_reverse_on();
|
|
+
|
|
+ if (with_border) {
|
|
+ ht_underline_on();
|
|
+ hyptop_printf_pos(rows_add_top - 1, g.c.col_cnt - 1, " ");
|
|
+ ht_underline_off();
|
|
+ hyptop_printf_pos(row_cnt_displ + rows_add_top,
|
|
+ g.c.col_cnt - 1, " ");
|
|
+ }
|
|
+
|
|
+ ht_underline_on();
|
|
+ if (can_scroll_up) {
|
|
+ ht_bold_on();
|
|
+ hyptop_printf_pos(rows_add_top, g.c.col_cnt - 1, "^");
|
|
+ ht_bold_off();
|
|
+ } else {
|
|
+ hyptop_printf_pos(rows_add_top, g.c.col_cnt - 1, "^");
|
|
+ }
|
|
+ ht_underline_off();
|
|
+
|
|
+ if (row_cnt_displ == 1)
|
|
+ goto out;
|
|
+
|
|
+ ht_underline_on();
|
|
+ if (can_scroll_down) {
|
|
+ ht_bold_on();
|
|
+ hyptop_printf_pos(row_cnt_displ - 1 + rows_add_top,
|
|
+ g.c.col_cnt - 1, "v");
|
|
+ ht_bold_off();
|
|
+ } else {
|
|
+ hyptop_printf_pos(row_cnt_displ - 1 + rows_add_top,
|
|
+ g.c.col_cnt - 1, "v");
|
|
+ }
|
|
+ ht_underline_off();
|
|
+
|
|
+ if (row_cnt_displ == 2)
|
|
+ goto out;
|
|
+
|
|
+ for (i = 0; i < row_cnt_displ - 2; i++)
|
|
+ hyptop_printf_pos(i + rows_add_top + 1, g.c.col_cnt - 1,
|
|
+ " ");
|
|
+ ht_underline_on();
|
|
+ hyptop_printf_pos(i + rows_add_top, g.c.col_cnt - 1, " ");
|
|
+ ht_underline_off();
|
|
+
|
|
+ ht_bold_on();
|
|
+ for (i = 0; i < bar_len; i++) {
|
|
+ if (i + start == row_cnt_displ - 3)
|
|
+ ht_underline_on();
|
|
+ hyptop_printf_pos(i + start + 1 + rows_add_top,
|
|
+ g.c.col_cnt - 1, "#");
|
|
+ if (i + start == row_cnt_displ - 3)
|
|
+ ht_underline_off();
|
|
+ }
|
|
+ ht_bold_off();
|
|
+out:
|
|
+ ht_reverse_off();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Convert string to uppercase
|
|
+ */
|
|
+void ht_str_to_upper(char *str)
|
|
+{
|
|
+ while (*str) {
|
|
+ *str = toupper(*str);
|
|
+ str++;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Convert ext TOD to microseconds
|
|
+ */
|
|
+u64 ht_ext_tod_2_us(void *tod_ext)
|
|
+{
|
|
+ char *tod_ptr = tod_ext;
|
|
+ u64 us, *tod1, *tod2;
|
|
+
|
|
+ tod1 = (u64 *) tod_ptr;
|
|
+ tod2 = (u64 *) &tod_ptr[8];
|
|
+ us = *tod1 << 8;
|
|
+ us |= *tod2 >> 58;
|
|
+ us = us >> 12;
|
|
+
|
|
+ return us;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize helper module
|
|
+ */
|
|
+void hyptop_helper_init(void)
|
|
+{
|
|
+ l_iconv_ebcdic_ascii = iconv_open("ISO-8859-1", "EBCDIC-US");
|
|
+ if (l_iconv_ebcdic_ascii == (iconv_t) -1)
|
|
+ ERR_EXIT("Could not initilize iconv\n");
|
|
+}
|
|
diff --git a/hyptop/helper.h b/hyptop/helper.h
|
|
new file mode 100644
|
|
index 0000000..61717f3
|
|
--- /dev/null
|
|
+++ b/hyptop/helper.h
|
|
@@ -0,0 +1,100 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Helper functions
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ * Christian Borntraeger <borntraeger@de.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef HELPER_H
|
|
+#define HELPER_H
|
|
+
|
|
+#include <stdlib.h>
|
|
+#include <limits.h>
|
|
+#include <sys/types.h>
|
|
+#include "zt_common.h"
|
|
+
|
|
+/*
|
|
+ * min/max macros
|
|
+ */
|
|
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
|
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
|
+#define G0(x) MAX(0, (s64) (x))
|
|
+
|
|
+/*
|
|
+ * Helper Prototypes
|
|
+ */
|
|
+extern void hyptop_helper_init(void);
|
|
+extern char *ht_strstrip(char *str);
|
|
+extern char *ht_strdup(const char *str);
|
|
+extern void ht_print_head(const char *sys);
|
|
+extern void ht_print_help_icon(void);
|
|
+extern void ht_ebcdic_to_ascii(char *inout, size_t len);
|
|
+extern char *ht_mount_point_get(const char *fs_type);
|
|
+extern u64 ht_ext_tod_2_us(void *tod_ext);
|
|
+extern void ht_print_time(void);
|
|
+
|
|
+/*
|
|
+ * Memory alloc functions
|
|
+ */
|
|
+extern void *ht_zalloc(size_t size);
|
|
+extern void *ht_alloc(size_t size);
|
|
+extern void *ht_realloc(void *ptr, size_t size);
|
|
+static inline void ht_free(void *ptr)
|
|
+{
|
|
+ free(ptr);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Curses extensions
|
|
+ */
|
|
+
|
|
+#define KEY_RETURN 0012
|
|
+#define KEY_ESCAPE 0033
|
|
+
|
|
+void ht_bold_on(void);
|
|
+void ht_bold_off(void);
|
|
+void ht_reverse_on(void);
|
|
+void ht_reverse_off(void);
|
|
+void ht_underline_on(void);
|
|
+void ht_underline_off(void);
|
|
+void ht_str_to_upper(char *str);
|
|
+
|
|
+void ht_print_scroll_bar(int row_cnt, int row_start, int row_bar_start,
|
|
+ int row_bar_bottom, int can_scroll_up,
|
|
+ int can_scroll_down, int with_boder);
|
|
+
|
|
+/*
|
|
+ * Error Macros
|
|
+ */
|
|
+#define ERR_MSG(x...) \
|
|
+do { \
|
|
+ hyptop_text_mode(); \
|
|
+ fflush(stdout); \
|
|
+ fprintf(stderr, "%s: ", g.prog_name);\
|
|
+ fprintf(stderr, x); \
|
|
+} while (0)
|
|
+
|
|
+#define ERR_EXIT(x...) \
|
|
+do { \
|
|
+ hyptop_text_mode(); \
|
|
+ fflush(stdout); \
|
|
+ fprintf(stderr, "%s: ", g.prog_name); \
|
|
+ fprintf(stderr, x); \
|
|
+ hyptop_exit(1); \
|
|
+ exit(1); \
|
|
+} while (0)
|
|
+
|
|
+#define ERR_EXIT_ERRNO(x...) \
|
|
+do { \
|
|
+ fflush(stdout); \
|
|
+ fprintf(stderr, "%s: ", g.prog_name); \
|
|
+ fprintf(stderr, x); \
|
|
+ fprintf(stderr, " (%s)", strerror(errno)); \
|
|
+ fprintf(stderr, "\n"); \
|
|
+ hyptop_exit(1); \
|
|
+} while (0)
|
|
+
|
|
+#endif /* HELPER_H */
|
|
diff --git a/hyptop/hyptop.8 b/hyptop/hyptop.8
|
|
new file mode 100644
|
|
index 0000000..99a729c
|
|
--- /dev/null
|
|
+++ b/hyptop/hyptop.8
|
|
@@ -0,0 +1,213 @@
|
|
+.TH HYPTOP 8 "Nov 2009" "s390-tools"
|
|
+.SH NAME
|
|
+hyptop \- Show hypervisor performance data on System z
|
|
+
|
|
+.SH SYNOPSIS
|
|
+.B hyptop
|
|
+[OPTIONS]
|
|
+
|
|
+.SH DESCRIPTION
|
|
+.B hyptop
|
|
+provides a dynamic real-time view of a hypervisor environment on System z.
|
|
+It works with either the z/VM or the LPAR hypervisor. Depending on the available
|
|
+data it shows for example CPU and memory information about running LPARs or
|
|
+z/VM guests.
|
|
+
|
|
+hyptop provides two windows:
|
|
+.IP " -"
|
|
+sys_list: Shows a list of systems that the hypervisor is currently running
|
|
+.IP " -"
|
|
+sys: Shows one system in more detail.
|
|
+
|
|
+.PP
|
|
+You can run hyptop in interactive mode (default) or in batch mode with
|
|
+the "\-b" option. For how to use the interactive mode, see the online help
|
|
+(enter "?" after hyptop is started).
|
|
+
|
|
+.SH OPTIONS
|
|
+.TP
|
|
+.BR "\-h" " or " "\-\-help"
|
|
+Print usage information, then exit.
|
|
+
|
|
+.TP
|
|
+.BR "\-v" " or " "\-\-version"
|
|
+Print version information, then exit.
|
|
+
|
|
+.TP
|
|
+.BR "\-w <WINDOW NAME>" " or " "\-\-window=<WINDOW NAME>"
|
|
+Select current window. Use the options "--sys", "--fields", and "--sort" to
|
|
+modify the current window. The last window specified with the "--window" option
|
|
+will be used as start window. The default window is "sys_list".
|
|
+.TP
|
|
+.BR "\-s <SYSTEM>,..." " or " "\-\-sys=<SYSTEM>,..."
|
|
+Select systems for current window. If this option is specified, only the
|
|
+selected systems are shown for the window. For window "sys" only one
|
|
+system can be specified.
|
|
+.TP
|
|
+.BR "\-f <F_LETTER>[:<UNIT>],..." " or " "\-\-fields=<F_LETTER>[:<UNIT>],..."
|
|
+Select fields and units in the current window. "F_LETTER" is the field
|
|
+letter that identifies uniquely a field (for example "c" for CPU time).
|
|
+"UNIT" is the used entity for displaying data for the field (for example "us"
|
|
+for microseconds). See FIELDS and UNITS below for definitions.
|
|
+If the "--fields" option is specified, only the selected fields are
|
|
+shown.
|
|
+.TP
|
|
+.BR "\-S <F_LETTER>" " or " "\-\-sort=<F_LETTER>"
|
|
+Select sort field for current window. To reverse the sort order, specify the
|
|
+option twice. See FIELDS below for definitions.
|
|
+.TP
|
|
+.BR "\-t <TYPE>,..." " or " "\-\-cpu_types=<TYPE>,..."
|
|
+Select CPU types that are used for CPU time calculations. See CPU TYPES
|
|
+below for definitions.
|
|
+.TP
|
|
+.BR "\-b" " or " "\-\-batch_mode"
|
|
+Use batch mode (no curses). This can be useful for sending output from hyptop
|
|
+to another program, a file, or a line mode terminal.
|
|
+In this mode no user input is accepted.
|
|
+.TP
|
|
+.BR "\-d <SECONDS>" " or " "\-\-delay=<SECONDS>"
|
|
+Specifies the delay between screen updates.
|
|
+.TP
|
|
+.BR "\-n <ITERATIONS>" " or " "\-\-iterations=<ITERATIONS>"
|
|
+Specifies the maximum number of iterations before ending.
|
|
+
|
|
+.SH PREREQUISITES
|
|
+The following things are required to run hyptop:
|
|
+
|
|
+.IP " -"
|
|
+The Linux kernel must have the required support to provide the
|
|
+performance data.
|
|
+.IP " -"
|
|
+debugfs has to be mounted.
|
|
+.IP " -"
|
|
+The hyptop user must have read permission for the required debugfs files.
|
|
+
|
|
+.PP
|
|
+To mount debugfs, you can use this command:
|
|
+
|
|
+# mount none -t debugfs /sys/kernel/debug
|
|
+
|
|
+To make this persistent, add the following to "/etc/fstab":
|
|
+
|
|
+none /sys/kernel/debug debugfs defaults 0 0
|
|
+
|
|
+
|
|
+.SH FIELDS
|
|
+The supported fields depend on the available data on the hypervisor.
|
|
+This is different between LPAR and z/VM. It might also depend on
|
|
+machine type, z/VM version and kernel version. Each field has a unique
|
|
+field letter that can be used to select the field in interactive mode
|
|
+or through the "--fields" command line option.
|
|
+
|
|
+The following fields are available under LPAR:
|
|
+
|
|
+ In "sys_list" and "sys" window:
|
|
+ 'c' - CPU time per second
|
|
+ 'm' - Management time per second
|
|
+ 'C' - Total CPU time
|
|
+ 'M' - Total management time
|
|
+ 'o' - Online time
|
|
+
|
|
+ In "sys_list" window:
|
|
+ '#' - Number of CPUs
|
|
+
|
|
+ In "sys" window:
|
|
+ 'p' - CPU type
|
|
+ 'v' - Visualization of CPU time per second
|
|
+
|
|
+The following fields are available under z/VM:
|
|
+
|
|
+ In "sys_list" and "sys" window:
|
|
+ 'c' - CPU time per second
|
|
+ 'C' - Total CPU time
|
|
+ 'o' - Online time
|
|
+
|
|
+ In "sys_list" window:
|
|
+ '#' - Number of CPUs
|
|
+ 'u' - Used memory
|
|
+ 'a' - Maximum memory
|
|
+ 'n' - Minimum weight
|
|
+ 't' - Current weight
|
|
+ 'x' - Maximum weight
|
|
+
|
|
+ In "sys" window:
|
|
+ 'v' - Visualization of CPU time per second
|
|
+
|
|
+.SH UNITS
|
|
+Depending on the field type the values can be displayed in different units.
|
|
+The following units are supported:
|
|
+
|
|
+ Time:
|
|
+ 'us' - Microseconds (10^-6 seconds)
|
|
+ 'ms' - Millisconds (10^-3 seconds)
|
|
+ '%' - Hundreds of a second (10^-2 seconds) or percent
|
|
+ 's' - Seconds
|
|
+ 'm' - Minutes
|
|
+ 'hm' - Hours & Minutes
|
|
+ 'dhm' - Days & Hours & Minutes
|
|
+
|
|
+ Memory:
|
|
+ 'kib' - Kibibytes (1.024 bytes)
|
|
+ 'mib' - Mebibytes (1.048.576 bytes)
|
|
+ 'gib' - Gibibytes (1.073.741.824 bytes)
|
|
+
|
|
+ Miscellaneous:
|
|
+ 'str' - String
|
|
+ '#' - Count/Number
|
|
+ 'vis' - Visualization
|
|
+
|
|
+.SH CPU TYPES
|
|
+Depending on the hypervisor different CPU types are supported. These CPU
|
|
+types can be selected either interactively or with the "--cpu_types"
|
|
+command line option. The calculation of the CPU data only uses CPUs of
|
|
+the specified types.
|
|
+
|
|
+On LPAR the following CPU types are supported:
|
|
+ 'IFL' - Integrated Facility for Linux
|
|
+ 'CP' - CP processor type
|
|
+ 'UN' - Unspecified processor type (other than CP or IFL)
|
|
+
|
|
+NOTE: It is possible that on older machines also IFLs are shown as CPs.
|
|
+On z/VM currently only the processor type 'UN' is available.
|
|
+
|
|
+.SH EXAMPLES
|
|
+To start hyptop with the "sys_list" window in interactive mode, enter:
|
|
+.br
|
|
+
|
|
+ # hyptop
|
|
+
|
|
+.br
|
|
+To start hyptop with the "sys_list" window in batch mode, enter:
|
|
+.br
|
|
+
|
|
+ # hyptop -b
|
|
+
|
|
+.br
|
|
+To start hyptop with the "sys_list" window in interactive mode with the fields
|
|
+CPU time (in milliseconds) and online time (unit default) and sort the
|
|
+output according to online time, enter:
|
|
+.br
|
|
+
|
|
+ # hyptop -f c:ms,o -S o
|
|
+
|
|
+.br
|
|
+To start hyptop with the "sys" window with system "MYLPAR" with the fields CPU
|
|
+time (unit milliseconds) and online time (unit default) and sort the
|
|
+output reverse according the online time, enter:
|
|
+.br
|
|
+
|
|
+ # hyptop -w sys -s MYLPAR -f c:ms,o -S o -S o
|
|
+
|
|
+.br
|
|
+To start hyptop with the "sys_list" window in batch mode with update delay 5
|
|
+seconds and 10 iterations, enter:
|
|
+.br
|
|
+
|
|
+ # hyptop -b -d 5 -n 10
|
|
+
|
|
+.br
|
|
+To start hyptop with the "sys_list" window and use only CPU types IFL and CP
|
|
+for CPU time calculation, enter:
|
|
+.br
|
|
+
|
|
+ # hyptop -t ifl,cp
|
|
diff --git a/hyptop/hyptop.c b/hyptop/hyptop.c
|
|
new file mode 100644
|
|
index 0000000..c42e8b0
|
|
--- /dev/null
|
|
+++ b/hyptop/hyptop.c
|
|
@@ -0,0 +1,352 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Main & init functions
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <errno.h>
|
|
+#include <stdlib.h>
|
|
+#include <ncurses.h>
|
|
+#include <time.h>
|
|
+#include <signal.h>
|
|
+#include <sys/ioctl.h>
|
|
+#include <sys/stat.h>
|
|
+#include <sys/types.h>
|
|
+#include "helper.h"
|
|
+#include "sd.h"
|
|
+#include "hyptop.h"
|
|
+#include "win_cpu_types.h"
|
|
+#include "opts.h"
|
|
+#include "dg_debugfs.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;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Init curses
|
|
+ */
|
|
+static void l_term_init(void)
|
|
+{
|
|
+ if (g.o.batch_mode_specified)
|
|
+ return;
|
|
+
|
|
+ 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 \"--batchmode\"\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
|
|
+ */
|
|
+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
|
|
+ */
|
|
+void l_update_term_batch(void)
|
|
+{
|
|
+ g.w.cur->update_term(g.w.cur);
|
|
+ printf("\n");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * 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 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);
|
|
+ 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;
|
|
+}
|
|
diff --git a/hyptop/hyptop.h b/hyptop/hyptop.h
|
|
new file mode 100644
|
|
index 0000000..fe39976
|
|
--- /dev/null
|
|
+++ b/hyptop/hyptop.h
|
|
@@ -0,0 +1,220 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Command line options, window definition, print functions, etc.
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef HYPTOP_H
|
|
+#define HYPTOP_H
|
|
+
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <ncurses.h>
|
|
+#include <termios.h>
|
|
+#include "list.h"
|
|
+#include "helper.h"
|
|
+#include "table.h"
|
|
+#include "nav_desc.h"
|
|
+
|
|
+#define HYPTOP_OPT_DEFAULT_DELAY 2
|
|
+#define HYPTOP_MAX_WIN_DEPTH 4
|
|
+#define HYPTOP_MAX_LINE 512
|
|
+#define PROG_NAME "hyptop"
|
|
+
|
|
+/*
|
|
+ * Options info
|
|
+ */
|
|
+struct hyptop_str_vec_opt {
|
|
+ unsigned int specified;
|
|
+ char **vec;
|
|
+ unsigned int cnt;
|
|
+};
|
|
+
|
|
+struct hyptop_col_vec_opt {
|
|
+ unsigned int specified;
|
|
+ struct table_col_spec **vec;
|
|
+ unsigned int cnt;
|
|
+};
|
|
+
|
|
+struct hyptop_win_opts {
|
|
+ struct hyptop_str_vec_opt sys;
|
|
+ struct hyptop_col_vec_opt fields;
|
|
+ unsigned int sort_field_specified;
|
|
+ char sort_field;
|
|
+};
|
|
+
|
|
+struct hyptop_opts {
|
|
+ unsigned int win_specified;
|
|
+ unsigned int batch_mode_specified;
|
|
+ unsigned int iterations_specified;
|
|
+ unsigned int iterations;
|
|
+ unsigned int iterations_act;
|
|
+
|
|
+ struct hyptop_win *cur_win;
|
|
+ struct hyptop_str_vec_opt cpu_types;
|
|
+
|
|
+ int delay_s;
|
|
+ int delay_us;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Curses info
|
|
+ */
|
|
+struct hyptop_curses {
|
|
+ int row_cnt;
|
|
+ int col_cnt;
|
|
+ char line[HYPTOP_MAX_LINE];
|
|
+ int x;
|
|
+ int y;
|
|
+ int initialized;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Window info
|
|
+ */
|
|
+struct hyptop_win_info {
|
|
+ struct hyptop_win *cur;
|
|
+ struct hyptop_win *prev[HYPTOP_MAX_WIN_DEPTH];
|
|
+ unsigned int prev_cnt;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Globals definition
|
|
+ */
|
|
+struct hyptop_globals {
|
|
+ struct hyptop_opts o;
|
|
+ struct hyptop_curses c;
|
|
+ struct hyptop_win_info w;
|
|
+ const char *prog_name;
|
|
+ struct hyptop_win *win_cpu_types;
|
|
+};
|
|
+
|
|
+extern struct hyptop_globals g;
|
|
+
|
|
+/*
|
|
+ * Print functions
|
|
+ */
|
|
+#define hyptop_printf_pos(y, x, p...) \
|
|
+ do { \
|
|
+ if (g.o.batch_mode_specified) \
|
|
+ printf(p); \
|
|
+ else { \
|
|
+ int len; \
|
|
+ len = snprintf(g.c.line, sizeof(g.c.line) - 1, p); \
|
|
+ len = MIN(len, (g.c.col_cnt - (x))); \
|
|
+ if (len > 0) { \
|
|
+ mvaddnstr((y), (x), g.c.line, len); \
|
|
+ } \
|
|
+ } \
|
|
+ } while (0)
|
|
+
|
|
+#define hyptop_printf(p...) \
|
|
+ do { \
|
|
+ if (g.o.batch_mode_specified) \
|
|
+ printf(p); \
|
|
+ else { \
|
|
+ int len; \
|
|
+ len = snprintf(g.c.line, sizeof(g.c.line) - 1, p); \
|
|
+ len = MIN(len, (g.c.col_cnt - g.c.x)); \
|
|
+ if (len > 0) { \
|
|
+ mvaddnstr(g.c.y, g.c.x, g.c.line, len); \
|
|
+ g.c.x += len; \
|
|
+ } \
|
|
+ } \
|
|
+ } while (0)
|
|
+
|
|
+static inline void hyptop_printf_init(void)
|
|
+{
|
|
+ g.c.x = 0;
|
|
+ g.c.y = 0;
|
|
+}
|
|
+
|
|
+static inline void hyptop_print_seek_back(int i)
|
|
+{
|
|
+ unsigned int cnt = MAX(g.c.col_cnt - g.c.x - i, 0);
|
|
+
|
|
+ if (g.o.batch_mode_specified)
|
|
+ return;
|
|
+ if (cnt) {
|
|
+ memset(g.c.line, ' ', cnt);
|
|
+ assert(cnt < sizeof(g.c.line));
|
|
+ g.c.line[cnt] = 0;
|
|
+ addstr(g.c.line);
|
|
+ }
|
|
+ g.c.x = g.c.col_cnt - i;
|
|
+}
|
|
+
|
|
+static inline void hyptop_print_nl(void)
|
|
+{
|
|
+ unsigned int cnt = MAX(g.c.col_cnt - g.c.x, 0);
|
|
+
|
|
+ if (g.o.batch_mode_specified) {
|
|
+ printf("\n");
|
|
+ return;
|
|
+ }
|
|
+ if (cnt) {
|
|
+ memset(g.c.line, ' ', g.c.col_cnt - g.c.x);
|
|
+ assert(cnt < sizeof(g.c.line));
|
|
+ g.c.line[cnt] = 0;
|
|
+ addstr(g.c.line);
|
|
+ }
|
|
+ g.c.x = 0;
|
|
+ g.c.y++;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * hyptop windows
|
|
+ */
|
|
+
|
|
+enum hyptop_win_action {
|
|
+ WIN_SWITCH,
|
|
+ WIN_KEEP,
|
|
+};
|
|
+
|
|
+extern enum hyptop_win_action hyptop_process_input_timeout(void);
|
|
+extern enum hyptop_win_action hyptop_process_input(void);
|
|
+extern enum hyptop_win_action win_switch(struct hyptop_win *w);
|
|
+extern enum hyptop_win_action win_back(void);
|
|
+
|
|
+struct hyptop_win;
|
|
+struct hyptop_win {
|
|
+ enum hyptop_win_action (*process_input)(struct hyptop_win *w, int c);
|
|
+ void (*update_term)(struct hyptop_win *w);
|
|
+ void (*run)(struct hyptop_win *w);
|
|
+ const char *id;
|
|
+ const char *desc;
|
|
+ struct nav_desc **desc_normal_vec;
|
|
+ struct nav_desc **desc_select_vec;
|
|
+ struct nav_desc **desc_general_vec;
|
|
+ struct hyptop_win_opts opts;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Window sys_list
|
|
+ */
|
|
+extern struct hyptop_win win_sys_list;
|
|
+extern void win_sys_list_init(void);
|
|
+
|
|
+/*
|
|
+ * Window sys
|
|
+ */
|
|
+extern struct hyptop_win win_sys;
|
|
+extern void win_sys_set(const char *sys_id);
|
|
+extern void win_sys_init(void);
|
|
+
|
|
+/*
|
|
+ * Window cpu_types
|
|
+ */
|
|
+extern void win_cpu_types_init(void);
|
|
+
|
|
+/*
|
|
+ * Misc functions
|
|
+ */
|
|
+extern void hyptop_update_term(void);
|
|
+extern void hyptop_exit(int rc);
|
|
+extern void hyptop_text_mode(void);
|
|
+
|
|
+#endif /* HYPTOP_H */
|
|
diff --git a/hyptop/nav_desc.c b/hyptop/nav_desc.c
|
|
new file mode 100644
|
|
index 0000000..703489f
|
|
--- /dev/null
|
|
+++ b/hyptop/nav_desc.c
|
|
@@ -0,0 +1,243 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Description of navigation keys
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
+#include "nav_desc.h"
|
|
+#include "tbox.h"
|
|
+
|
|
+#define L_KEY_LEN 14
|
|
+#define L_KEY_FMT "%-14s"
|
|
+
|
|
+/* Select mode */
|
|
+
|
|
+struct nav_desc nav_desc_select_mode_enter = {
|
|
+ .desc = "Enter select mode",
|
|
+ .keys = {"RIGHT", "l", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_select_mode_leave = {
|
|
+ .desc = "Leave select mode",
|
|
+ .keys = {"LEFT", "h", NULL},
|
|
+};
|
|
+
|
|
+/* "sys" Window */
|
|
+
|
|
+struct nav_desc nav_desc_win_enter_sys = {
|
|
+ .desc = "Go to the \"sys\" window for selected system",
|
|
+ .keys = {"RIGHT", "l", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_win_leave_sys = {
|
|
+ .desc = "Go to the previous window",
|
|
+ .keys = {"LEFT", "h", "q", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_win_leave_sys_fast = {
|
|
+ .desc = "Go to the previous window",
|
|
+ .keys = {"q", NULL},
|
|
+};
|
|
+
|
|
+/* "fields" window */
|
|
+
|
|
+struct nav_desc nav_desc_win_enter_fields = {
|
|
+ .desc = "Go to the \"fields\" window",
|
|
+ .keys = {"f", NULL},
|
|
+} ;
|
|
+
|
|
+struct nav_desc nav_desc_win_leave_fields = {
|
|
+ .desc = "Go to the previous window",
|
|
+ .keys = {"LEFT", "ENTER", "h", "f", "q", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_win_leave_fields_fast = {
|
|
+ .desc = "Go to the previous window",
|
|
+ .keys = {"f", "q", NULL},
|
|
+};
|
|
+
|
|
+/* "cpu_types" window */
|
|
+
|
|
+struct nav_desc nav_desc_win_enter_cpu_types = {
|
|
+ .desc = "Go to the \"cpu_types\" window",
|
|
+ .keys = {"t", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_win_leave_cpu_types = {
|
|
+ .desc = "Go to the previous window",
|
|
+ .keys = {"LEFT", "ENTER", "h", "t", "q", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_win_leave_cpu_types_fast = {
|
|
+ .desc = "Go to the previous window",
|
|
+ .keys = {"t", "q", NULL},
|
|
+};
|
|
+
|
|
+/* Marks */
|
|
+
|
|
+struct nav_desc nav_desc_marks_clear = {
|
|
+ .desc = "Clear all marked rows",
|
|
+ .keys = {"SPACE", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_mark_toggle = {
|
|
+ .desc = "Toggle mark for selected row",
|
|
+ .keys = {"SPACE", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_mark_toggle_view = {
|
|
+ .desc = "Toggle view for marked rows",
|
|
+ .keys = {".", NULL},
|
|
+};
|
|
+
|
|
+/* Units */
|
|
+
|
|
+struct nav_desc nav_desc_col_unit_increase = {
|
|
+ .desc = "Increase unit type of selected column",
|
|
+ .keys = {"+", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_col_unit_decrease = {
|
|
+ .desc = "Decrease unit type of selected column",
|
|
+ .keys = {"-", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_row_unit_increase = {
|
|
+ .desc = "Increase unit type of selected row",
|
|
+ .keys = {"+", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_row_unit_decrease = {
|
|
+ .desc = "Decrease unit type of selected row",
|
|
+ .keys = {"-", NULL},
|
|
+};
|
|
+
|
|
+/* Select columns */
|
|
+
|
|
+struct nav_desc nav_desc_select_col_next = {
|
|
+ .desc = "Select next column",
|
|
+ .keys = {">", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_select_col_prev = {
|
|
+ .desc = "Select previous column",
|
|
+ .keys = {"<", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_select_col_hotkey = {
|
|
+ .desc = "Select column with hotkey",
|
|
+ .keys = {"<key>", NULL},
|
|
+};
|
|
+
|
|
+/* Quit */
|
|
+
|
|
+struct nav_desc nav_desc_quit = {
|
|
+ .desc = "Quit program",
|
|
+ .keys = {"q", NULL},
|
|
+};
|
|
+
|
|
+/* Select rows */
|
|
+
|
|
+struct nav_desc nav_desc_toggle_mark_hotkey = {
|
|
+ .desc = "Toggle mark for row with hotkey",
|
|
+ .keys = {"<key>", NULL},
|
|
+};
|
|
+
|
|
+/* Navigation */
|
|
+
|
|
+struct nav_desc nav_desc_scroll_up_line = {
|
|
+ .desc = "Scroll up one line",
|
|
+ .keys = {"UP", "k", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_scroll_down_line = {
|
|
+ .desc = "Scroll down one line",
|
|
+ .keys = {"DOWN", "j", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_scroll_up_page = {
|
|
+ .desc = "Scroll up one page",
|
|
+ .keys = {"PGUP", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_scroll_down_page = {
|
|
+ .desc = "Scroll down one page",
|
|
+ .keys = {"PGDOWN", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_scroll_up_head = {
|
|
+ .desc = "Scroll up to head of window",
|
|
+ .keys = {"g", NULL},
|
|
+};
|
|
+
|
|
+struct nav_desc nav_desc_scroll_down_tail = {
|
|
+ .desc = "Scroll down to tail of window",
|
|
+ .keys = {"G", NULL},
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Add navigation descriptons to text box
|
|
+ */
|
|
+static void l_nav_desc_add(struct tbox *tb, struct nav_desc *desc)
|
|
+{
|
|
+ char keys_str[L_KEY_LEN + 1];
|
|
+ unsigned int i, first;
|
|
+ char *key;
|
|
+
|
|
+ first = 1;
|
|
+ keys_str[0] = 0;
|
|
+ for (i = 0; (key = desc->keys[i]); i++) {
|
|
+ /*
|
|
+ * If we have used the whole space for the keys,
|
|
+ * we write the line and begin a new one
|
|
+ */
|
|
+ if (strlen(desc->keys[i]) + strlen(keys_str) + 1 > L_KEY_LEN) {
|
|
+ tbox_printf(tb, " " L_KEY_FMT ": %s", keys_str,
|
|
+ desc->desc);
|
|
+ keys_str[0] = 0;
|
|
+ first = 1;
|
|
+ }
|
|
+ if (!first)
|
|
+ strcat(keys_str, ",");
|
|
+ else
|
|
+ first = 0;
|
|
+ strcat(keys_str, "'");
|
|
+ strcat(keys_str, desc->keys[i]);
|
|
+ strcat(keys_str, "'");
|
|
+ assert(strlen(keys_str) <= L_KEY_LEN);
|
|
+ }
|
|
+ tbox_printf(tb, " " L_KEY_FMT ": %s", keys_str, desc->desc);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add navigation descriptions for "normal", "select" and "general" to text box
|
|
+ */
|
|
+void nav_desc_add(struct tbox *tb,
|
|
+ struct nav_desc **desc_normal,
|
|
+ struct nav_desc **desc_select,
|
|
+ struct nav_desc **desc_general)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ tbox_printf(tb, "\\BSupported keys in this window\\B");
|
|
+ tbox_printf(tb, " ");
|
|
+
|
|
+ tbox_printf(tb, "NORMAL MODE:");
|
|
+ for (i = 0; (desc_normal[i]); i++)
|
|
+ l_nav_desc_add(tb, desc_normal[i]);
|
|
+ tbox_printf(tb, " ");
|
|
+ tbox_printf(tb, "SELECT MODE:");
|
|
+ for (i = 0; (desc_select[i]); i++)
|
|
+ l_nav_desc_add(tb, desc_select[i]);
|
|
+ tbox_printf(tb, " ");
|
|
+ tbox_printf(tb, "GENERAL:");
|
|
+ for (i = 0; (desc_general[i]); i++)
|
|
+ l_nav_desc_add(tb, desc_general[i]);
|
|
+ tbox_printf(tb, " ");
|
|
+}
|
|
diff --git a/hyptop/nav_desc.h b/hyptop/nav_desc.h
|
|
new file mode 100644
|
|
index 0000000..05fcde9
|
|
--- /dev/null
|
|
+++ b/hyptop/nav_desc.h
|
|
@@ -0,0 +1,55 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Description of navigation keys
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef NAV_DESC_H
|
|
+#define NAV_DESC_H
|
|
+
|
|
+#include "tbox.h"
|
|
+
|
|
+struct nav_desc {
|
|
+ char *desc;
|
|
+ char *keys[];
|
|
+};
|
|
+
|
|
+void nav_desc_add(struct tbox *tb,
|
|
+ struct nav_desc **desc_normal,
|
|
+ struct nav_desc **desc_select,
|
|
+ struct nav_desc **desc_general);
|
|
+
|
|
+struct nav_desc nav_desc_quit;
|
|
+struct nav_desc nav_desc_select_mode_enter;
|
|
+struct nav_desc nav_desc_select_mode_leave;
|
|
+struct nav_desc nav_desc_win_enter_sys;
|
|
+struct nav_desc nav_desc_win_leave_sys;
|
|
+struct nav_desc nav_desc_win_leave_sys_fast;
|
|
+struct nav_desc nav_desc_win_enter_fields;
|
|
+struct nav_desc nav_desc_win_leave_fields;
|
|
+struct nav_desc nav_desc_win_leave_fields_fast;
|
|
+struct nav_desc nav_desc_win_enter_cpu_types;
|
|
+struct nav_desc nav_desc_win_leave_cpu_types;
|
|
+struct nav_desc nav_desc_win_leave_cpu_types_fast;
|
|
+struct nav_desc nav_desc_marks_clear;
|
|
+struct nav_desc nav_desc_mark_toggle;
|
|
+struct nav_desc nav_desc_mark_toggle_view;
|
|
+struct nav_desc nav_desc_col_unit_increase;
|
|
+struct nav_desc nav_desc_col_unit_decrease;
|
|
+struct nav_desc nav_desc_row_unit_increase;
|
|
+struct nav_desc nav_desc_row_unit_decrease;
|
|
+struct nav_desc nav_desc_select_col_next;
|
|
+struct nav_desc nav_desc_select_col_prev;
|
|
+struct nav_desc nav_desc_select_col_hotkey;
|
|
+struct nav_desc nav_desc_toggle_mark_hotkey;
|
|
+struct nav_desc nav_desc_scroll_up_line;
|
|
+struct nav_desc nav_desc_scroll_down_line;
|
|
+struct nav_desc nav_desc_scroll_up_page;
|
|
+struct nav_desc nav_desc_scroll_down_page;
|
|
+struct nav_desc nav_desc_scroll_up_head;
|
|
+struct nav_desc nav_desc_scroll_down_tail;
|
|
+
|
|
+#endif /* NAV_DESC_H */
|
|
diff --git a/hyptop/opts.c b/hyptop/opts.c
|
|
new file mode 100644
|
|
index 0000000..05a4126
|
|
--- /dev/null
|
|
+++ b/hyptop/opts.c
|
|
@@ -0,0 +1,416 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Command line parsing
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <stdio.h>
|
|
+#include <ctype.h>
|
|
+#include "zt_common.h"
|
|
+#include "helper.h"
|
|
+#include "hyptop.h"
|
|
+#include "getopt.h"
|
|
+#include "sd.h"
|
|
+
|
|
+static const char l_copyright_str[] = "Copyright IBM Corp. 2010";
|
|
+
|
|
+/*
|
|
+ * Help text for tool
|
|
+ */
|
|
+static char HELP_TEXT[] =
|
|
+"Usage: hyptop [OPTIONS]\n"
|
|
+"\n"
|
|
+"Show hypervisor performance data on System z.\n"
|
|
+"\n"
|
|
+"-h, --help Print this help, then exit\n"
|
|
+"-v, --version Print version information, then exit\n"
|
|
+"-w, --window WIN_NAME Current window (\"sys\" or \"sys_list\")\n"
|
|
+"-s, --sys SYSTEM[,..] Systems for current window\n"
|
|
+"-f, --fields LETTER[:UNIT][,..] Fields and units for current window\n"
|
|
+"-S, --sort LETTER Sort field for current window\n"
|
|
+"-t, --cpu_types TYPE[,..] CPU types used for time calculations\n"
|
|
+"-b, --batch_mode Use batch mode (no curses)\n"
|
|
+"-d, --delay SECONDS Delay time between screen updates\n"
|
|
+"-n, --iterations NUMBER Number of iterations before ending\n";
|
|
+
|
|
+/*
|
|
+ * Initialize default settings
|
|
+ */
|
|
+static void l_init_defaults(void)
|
|
+{
|
|
+ g.prog_name = PROG_NAME;
|
|
+ g.o.delay_s = HYPTOP_OPT_DEFAULT_DELAY;
|
|
+ g.w.cur = &win_sys_list;
|
|
+ g.o.cur_win = &win_sys_list;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print "help" hint
|
|
+ */
|
|
+static void l_std_usage_exit(void)
|
|
+{
|
|
+ fprintf(stderr, "Try '%s --help' for more information.\n",
|
|
+ g.prog_name);
|
|
+ hyptop_exit(1);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print help text
|
|
+ */
|
|
+static void l_usage(void)
|
|
+{
|
|
+ printf("%s", HELP_TEXT);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print version information
|
|
+ */
|
|
+static void l_print_version(void)
|
|
+{
|
|
+ printf("%s: Hypervisor Top version %s\n", g.prog_name, RELEASE_STRING);
|
|
+ printf("%s\n", l_copyright_str);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Check if string is a number
|
|
+ */
|
|
+static int l_number_check(const char *str)
|
|
+{
|
|
+ const char *ptr = str;
|
|
+ while (*ptr) {
|
|
+ if (!isdigit(*ptr))
|
|
+ ERR_EXIT("The argument \"%s\" is not an integer\n",
|
|
+ str);
|
|
+ ptr++;
|
|
+ }
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set delay option
|
|
+ */
|
|
+static void l_delay_set(char *delay_string)
|
|
+{
|
|
+ int secs;
|
|
+
|
|
+ l_number_check(delay_string);
|
|
+ if (sscanf(delay_string, "%i", &secs) != 1)
|
|
+ ERR_EXIT("The delay value \"%s\" is invalid\n", delay_string);
|
|
+ g.o.delay_s = secs;
|
|
+ g.o.delay_us = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get number of occurances of character 'c' in "str"
|
|
+ */
|
|
+static int l_get_char_cnt(char *str, char c)
|
|
+{
|
|
+ unsigned int i;
|
|
+ int cnt = 0;
|
|
+
|
|
+ for (i = 0; str[i] != 0; i++) {
|
|
+ if (str[i] == c)
|
|
+ cnt++;
|
|
+ }
|
|
+ return cnt;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return copy of string with removed trailing and leading blanks
|
|
+ */
|
|
+static char *l_trim_str_new(char *str)
|
|
+{
|
|
+ char *rc;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; *(str + i) == ' '; i++) {}
|
|
+ rc = ht_strdup(str + i);
|
|
+ ht_strstrip(rc);
|
|
+ if (strlen(rc) == 0)
|
|
+ ERR_EXIT("The argument \"%s\" is invalid\n", str);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get column specification for string
|
|
+ */
|
|
+static struct table_col_spec *l_get_col_spec(char *str)
|
|
+{
|
|
+ struct table_col_spec *col_spec;
|
|
+ unsigned int i;
|
|
+ char *key_str;
|
|
+
|
|
+ col_spec = ht_zalloc(sizeof(*col_spec));
|
|
+
|
|
+ for (i = strlen(str); i > 0; i--) {
|
|
+ if (str[i] == ':') {
|
|
+ col_spec->unit_str = l_trim_str_new(&str[i + 1]);
|
|
+ str[i] = 0;
|
|
+ }
|
|
+ }
|
|
+ key_str = l_trim_str_new(str);
|
|
+ if (strlen(key_str) > 1)
|
|
+ ERR_EXIT("The field key \"%s\" is invalid\n", key_str);
|
|
+ col_spec->hotkey = key_str[0];
|
|
+ ht_free(key_str);
|
|
+ return col_spec;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set the "--fields" option
|
|
+ */
|
|
+static void l_fields_set(char *str)
|
|
+{
|
|
+ struct hyptop_col_vec_opt *opt = &g.o.cur_win->opts.fields;
|
|
+ unsigned int i, j;
|
|
+
|
|
+ opt->cnt = l_get_char_cnt(str, ',') + 1;
|
|
+ opt->vec = ht_zalloc(sizeof(void *) * (opt->cnt + 1));
|
|
+
|
|
+ j = 0;
|
|
+ for (i = strlen(str); i > 0; i--) {
|
|
+ if (str[i] != ',')
|
|
+ continue;
|
|
+ opt->vec[j] = l_get_col_spec(&str[i + 1]);
|
|
+ str[i] = 0;
|
|
+ j++;
|
|
+ }
|
|
+ opt->vec[j] = l_get_col_spec(str);
|
|
+ opt->specified = 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set the "--sort_field" option
|
|
+ */
|
|
+static void l_sort_field_set(char *str)
|
|
+{
|
|
+ if (strlen(str) > 1)
|
|
+ ERR_EXIT("The sort field \"%s\" is invalid\n", str);
|
|
+ if (g.o.cur_win->opts.sort_field_specified &&
|
|
+ g.o.cur_win->opts.sort_field != str[0])
|
|
+ g.o.cur_win->opts.sort_field_specified = 0;
|
|
+ g.o.cur_win->opts.sort_field_specified++;
|
|
+ g.o.cur_win->opts.sort_field = str[0];
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Setup a string vector out of a comma separated list in "str"
|
|
+ */
|
|
+static void l_str_vec_set(char *str, struct hyptop_str_vec_opt *opt)
|
|
+{
|
|
+ unsigned int i, j;
|
|
+
|
|
+ opt->cnt = l_get_char_cnt(str, ',') + 1;
|
|
+ opt->vec = ht_zalloc(sizeof(void *) * (opt->cnt + 1));
|
|
+
|
|
+ j = 0;
|
|
+ for (i = strlen(str); i > 0; i--) {
|
|
+ if (str[i] != ',')
|
|
+ continue;
|
|
+ opt->vec[j] = l_trim_str_new(&str[i + 1]);
|
|
+ str[i] = 0;
|
|
+ j++;
|
|
+ }
|
|
+ opt->vec[j] = l_trim_str_new(str);
|
|
+ opt->specified = 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set the "--sys" option
|
|
+ */
|
|
+static void l_sys_set(char *str)
|
|
+{
|
|
+ l_str_vec_set(str, &g.o.cur_win->opts.sys);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set the "--cpu_types" option
|
|
+ */
|
|
+static void l_cpu_types_set(char *str)
|
|
+{
|
|
+ l_str_vec_set(str, &g.o.cpu_types);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set the "--window" option
|
|
+ */
|
|
+static void l_window_set(const char *str)
|
|
+{
|
|
+ g.o.win_specified = 1;
|
|
+ if (strcmp(str, win_sys_list.id) == 0)
|
|
+ g.o.cur_win = &win_sys_list;
|
|
+ else if (strcmp(str, win_sys.id) == 0)
|
|
+ g.o.cur_win = &win_sys;
|
|
+ else
|
|
+ ERR_EXIT("The window \"%s\" is unknown\n", str);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set the "--iterations" option
|
|
+ */
|
|
+static void l_iterations_set(const char *str)
|
|
+{
|
|
+ l_number_check(str);
|
|
+ g.o.iterations_specified = 1;
|
|
+ g.o.iterations = atoi(str);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set the "--batch_mode" option
|
|
+ */
|
|
+static void l_batch_mode_set(void)
|
|
+{
|
|
+ g.o.batch_mode_specified = 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Make option consisteny checks at end of command line parsing
|
|
+ */
|
|
+static void l_parse_finish(void)
|
|
+{
|
|
+ if (g.o.iterations_specified && g.o.iterations == 0)
|
|
+ hyptop_exit(0);
|
|
+ if (g.o.cur_win != &win_sys)
|
|
+ return;
|
|
+ if (!win_sys.opts.sys.specified)
|
|
+ ERR_EXIT("Specify a system for window \"sys\"\n");
|
|
+ if (win_sys.opts.sys.cnt != 1)
|
|
+ ERR_EXIT("More than one system for window \"sys\" has been "
|
|
+ "specified\n");
|
|
+ win_switch(&win_sys);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Main command line parsing function
|
|
+ */
|
|
+void opts_parse(int argc, char *argv[])
|
|
+{
|
|
+ int opt, index;
|
|
+ static struct option long_options[] = {
|
|
+ { "version", no_argument, NULL, 'v'},
|
|
+ { "help", no_argument, NULL, 'h'},
|
|
+ { "batch_mode", no_argument, NULL, 'b'},
|
|
+ { "delay", required_argument, NULL, 'd'},
|
|
+ { "window", required_argument, NULL, 'w'},
|
|
+ { "sys", required_argument, NULL, 's'},
|
|
+ { "iterations", required_argument, NULL, 'n'},
|
|
+ { "fields", required_argument, NULL, 'f'},
|
|
+ { "sort_field", required_argument, NULL, 'S'},
|
|
+ { "cpu_types", required_argument, NULL, 't'},
|
|
+ { 0, 0, 0, 0 }
|
|
+ };
|
|
+ static const char option_string[] = "vhbd:w:s:n:f:t:S:";
|
|
+
|
|
+ l_init_defaults();
|
|
+ while (1) {
|
|
+ opt = getopt_long(argc, argv, option_string,
|
|
+ long_options, &index);
|
|
+ if (opt == -1)
|
|
+ break;
|
|
+ switch (opt) {
|
|
+ case 'v':
|
|
+ l_print_version();
|
|
+ hyptop_exit(0);
|
|
+ case 'h':
|
|
+ l_usage();
|
|
+ hyptop_exit(0);
|
|
+ case 'b':
|
|
+ l_batch_mode_set();
|
|
+ break;
|
|
+ case 'd':
|
|
+ l_delay_set(optarg);
|
|
+ break;
|
|
+ case 'w':
|
|
+ l_window_set(optarg);
|
|
+ break;
|
|
+ case 's':
|
|
+ l_sys_set(optarg);
|
|
+ break;
|
|
+ case 'n':
|
|
+ l_iterations_set(optarg);
|
|
+ break;
|
|
+ case 't':
|
|
+ l_cpu_types_set(optarg);
|
|
+ break;
|
|
+ case 'f':
|
|
+ l_fields_set(optarg);
|
|
+ break;
|
|
+ case 'S':
|
|
+ l_sort_field_set(optarg);
|
|
+ break;
|
|
+ default:
|
|
+ l_std_usage_exit();
|
|
+ }
|
|
+ }
|
|
+ if (optind != argc)
|
|
+ ERR_EXIT("Invalid positional parameter \"%s\" specified\n",
|
|
+ argv[optind]);
|
|
+ l_parse_finish();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Has "sys_name" been specified on command line?
|
|
+ */
|
|
+int opts_sys_specified(struct hyptop_win *win, const char* sys_name)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ if (!win->opts.sys.specified)
|
|
+ return 1;
|
|
+ for (i = 0; i < win->opts.sys.cnt; i++) {
|
|
+ if (strcmp(win->opts.sys.vec[i], sys_name) == 0)
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Verify that all specified systems are available for window
|
|
+ */
|
|
+static void l_verify_systems(struct hyptop_win *win)
|
|
+{
|
|
+ char *sys_name;
|
|
+ unsigned int i;
|
|
+
|
|
+ for (i = 0; i < win->opts.sys.cnt; i++) {
|
|
+ if (sd_sys_get(sd_sys_root_get(), win->opts.sys.vec[i]))
|
|
+ continue;
|
|
+ sys_name = ht_strdup(win->opts.sys.vec[i]);
|
|
+ ht_str_to_upper(win->opts.sys.vec[i]);
|
|
+ if (sd_sys_get(sd_sys_root_get(), win->opts.sys.vec[i])) {
|
|
+ ht_free(sys_name);
|
|
+ continue;
|
|
+ }
|
|
+ ERR_EXIT("System \"%s\" is not available\n", sys_name);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Verify that all specified systems are available for all windows
|
|
+ */
|
|
+void opt_verify_systems(void)
|
|
+{
|
|
+ l_verify_systems(&win_sys_list);
|
|
+ l_verify_systems(&win_sys);
|
|
+ if (g.o.cur_win == &win_sys)
|
|
+ win_sys_set(win_sys.opts.sys.vec[0]);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Increase iterations count and exit if necessary
|
|
+ */
|
|
+void opts_iterations_next(void)
|
|
+{
|
|
+ if (g.o.iterations_specified) {
|
|
+ g.o.iterations_act++;
|
|
+ if (g.o.iterations_act >= g.o.iterations)
|
|
+ hyptop_exit(0);
|
|
+ }
|
|
+ if (g.o.batch_mode_specified)
|
|
+ printf("---------------------------------------------------"
|
|
+ "----------------------------\n");
|
|
+}
|
|
+
|
|
diff --git a/hyptop/opts.h b/hyptop/opts.h
|
|
new file mode 100644
|
|
index 0000000..babeda1
|
|
--- /dev/null
|
|
+++ b/hyptop/opts.h
|
|
@@ -0,0 +1,20 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Command line parsing
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef OPTS_H
|
|
+#define OPTS_H
|
|
+
|
|
+#include "hyptop.h"
|
|
+
|
|
+extern void opts_parse(int argc, char *argv[]);
|
|
+extern void opts_iterations_next(void);
|
|
+extern int opts_sys_specified(struct hyptop_win *win, const char* sys_name);
|
|
+extern void opt_verify_systems(void);
|
|
+
|
|
+#endif /* OPTS_H */
|
|
diff --git a/hyptop/sd.h b/hyptop/sd.h
|
|
new file mode 100644
|
|
index 0000000..7dd4c93
|
|
--- /dev/null
|
|
+++ b/hyptop/sd.h
|
|
@@ -0,0 +1,479 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * System data module: Provide database for system data (e.g. CPU and memory)
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef SD_H
|
|
+#define SD_H
|
|
+
|
|
+#include "helper.h"
|
|
+#include "table.h"
|
|
+
|
|
+#define SD_DG_INIT_INTERVAL_MS 200
|
|
+#define SD_SYS_ID_SIZE 9
|
|
+
|
|
+/*
|
|
+ * CPU info
|
|
+ */
|
|
+struct sd_cpu_info {
|
|
+ u64 cpu_time_us;
|
|
+ u64 mgm_time_us;
|
|
+ u64 wait_time_us;
|
|
+ s64 steal_time_us;
|
|
+ u64 online_time_us;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Memory Info
|
|
+ */
|
|
+struct sd_mem {
|
|
+ u64 min_kib;
|
|
+ u64 max_kib;
|
|
+ u64 use_kib;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Weight
|
|
+ */
|
|
+struct sd_weight {
|
|
+ u16 cur;
|
|
+ u16 min;
|
|
+ u16 max;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * System Name
|
|
+ */
|
|
+struct sd_sys_name {
|
|
+ char os[9];
|
|
+};
|
|
+
|
|
+struct sd_sys;
|
|
+
|
|
+/*
|
|
+ * SD info
|
|
+ */
|
|
+struct sd_info {
|
|
+ u8 active;
|
|
+ struct sd_sys *parent;
|
|
+};
|
|
+
|
|
+struct sd_cpu;
|
|
+
|
|
+/*
|
|
+ * SD System (can be e.g. CEC, VM or guest/LPAR)
|
|
+ */
|
|
+struct sd_sys {
|
|
+ struct list list;
|
|
+ struct sd_info i;
|
|
+ u64 update_time_us;
|
|
+ u32 child_cnt;
|
|
+ u32 child_cnt_active;
|
|
+ struct list child_list;
|
|
+ u32 cpu_cnt;
|
|
+ u32 cpu_cnt_active;
|
|
+ struct list cpu_list;
|
|
+ char id[SD_SYS_ID_SIZE];
|
|
+ struct sd_sys_name name;
|
|
+ struct sd_mem mem;
|
|
+ struct sd_weight weight;
|
|
+};
|
|
+
|
|
+#define sd_sys_id(sys) ((sys)->id)
|
|
+#define sd_sys_name_os(sys) ((sys)->name.os)
|
|
+
|
|
+void sd_sys_update_start(struct sd_sys *sys);
|
|
+void sd_sys_update_end(struct sd_sys *sys, u64 update_time_us);
|
|
+struct sd_sys *sd_sys_root_get(void);
|
|
+struct sd_sys *sd_sys_get(struct sd_sys *parent, const char *id);
|
|
+struct sd_sys *sd_sys_new(struct sd_sys *parent, const char *id);
|
|
+
|
|
+static inline void sd_sys_weight_cur_set(struct sd_sys *sys, u64 value)
|
|
+{
|
|
+ sys->weight.cur = value;
|
|
+}
|
|
+
|
|
+static inline void sd_sys_weight_min_set(struct sd_sys *sys, u64 value)
|
|
+{
|
|
+ sys->weight.min = value;
|
|
+}
|
|
+
|
|
+static inline void sd_sys_weight_max_set(struct sd_sys *sys, u64 value)
|
|
+{
|
|
+ sys->weight.max = value;
|
|
+}
|
|
+
|
|
+static inline void sd_sys_mem_use_kib_set(struct sd_sys *sys, u64 value)
|
|
+{
|
|
+ sys->mem.use_kib = value;
|
|
+}
|
|
+
|
|
+static inline void sd_sys_mem_min_kib_set(struct sd_sys *sys, u64 value)
|
|
+{
|
|
+ sys->mem.min_kib = value;
|
|
+}
|
|
+
|
|
+static inline void sd_sys_mem_max_kib_set(struct sd_sys *sys, u64 value)
|
|
+{
|
|
+ sys->mem.max_kib = value;
|
|
+}
|
|
+
|
|
+static inline void sd_sys_update_time_us_set(struct sd_sys *sys, u64 value)
|
|
+{
|
|
+ sys->update_time_us = value;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * CPU type
|
|
+ */
|
|
+#define CPU_TYPE_ID_LEN 16
|
|
+#define CPU_TYPE_DESC_LEN 64
|
|
+
|
|
+#define SD_CPU_TYPE_STR_IFL "IFL"
|
|
+#define SD_CPU_TYPE_STR_CP "CP"
|
|
+#define SD_CPU_TYPE_STR_UN "UN"
|
|
+
|
|
+struct sd_cpu_type {
|
|
+ char id[CPU_TYPE_ID_LEN];
|
|
+ char desc[CPU_TYPE_DESC_LEN];
|
|
+ u32 idx;
|
|
+ int cpu_cnt;
|
|
+ char hotkey;
|
|
+};
|
|
+
|
|
+#define sd_cpu_type_id(type) (type->id)
|
|
+#define sd_cpu_type_desc(type) (type->desc)
|
|
+
|
|
+int sd_cpu_type_selected(struct sd_cpu_type *cpu_type);
|
|
+void sd_cpu_type_select_toggle(struct sd_cpu_type *cpu_type);
|
|
+void sd_cpu_type_select(struct sd_cpu_type *cpu_type);
|
|
+void sd_cpu_type_select_all(void);
|
|
+void sd_cpu_type_select_none(void);
|
|
+struct sd_cpu_type *sd_cpu_type_by_id(const char *id);
|
|
+
|
|
+static inline int sd_cpu_type_cpu_cnt(struct sd_cpu_type *type)
|
|
+{
|
|
+ return type->cpu_cnt;
|
|
+}
|
|
+
|
|
+static inline void sd_sys_commit(struct sd_sys *sys)
|
|
+{
|
|
+ struct sd_sys *parent = sys->i.parent;
|
|
+
|
|
+ sys->i.active = 1;
|
|
+ if (parent)
|
|
+ parent->child_cnt_active++;
|
|
+}
|
|
+
|
|
+extern struct sd_cpu_type sd_cpu_type_ifl;
|
|
+extern struct sd_cpu_type sd_cpu_type_cp;
|
|
+extern struct sd_cpu_type sd_cpu_type_un;
|
|
+
|
|
+/*
|
|
+ * SD CPU
|
|
+ */
|
|
+enum sd_cpu_state {
|
|
+ SD_CPU_STATE_UNKNOWN = 0,
|
|
+ SD_CPU_STATE_OPERATING = 1,
|
|
+ SD_CPU_STATE_STOPPED = 2,
|
|
+ SD_CPU_STATE_DECONFIG = 3,
|
|
+};
|
|
+
|
|
+struct sd_cpu {
|
|
+ struct list list;
|
|
+ struct sd_info i;
|
|
+ char id[9];
|
|
+ struct sd_cpu_type *type;
|
|
+ char real_type[CPU_TYPE_ID_LEN];
|
|
+ struct sd_cpu_info d1;
|
|
+ struct sd_cpu_info d2;
|
|
+ struct sd_cpu_info *d_cur;
|
|
+ struct sd_cpu_info *d_prev;
|
|
+ u16 cnt;
|
|
+ enum sd_cpu_state state;
|
|
+};
|
|
+
|
|
+static inline char *sd_cpu_state_str(enum sd_cpu_state state)
|
|
+{
|
|
+ static char *state_str[] = {"UK", "OP", "ST", "DC"};
|
|
+
|
|
+ return state_str[(int) state];
|
|
+}
|
|
+
|
|
+#define sd_cpu_has_diff(cpu) (cpu->d_prev != NULL)
|
|
+#define sd_cpu_diff(cpu, member) (cpu->d_cur->member - cpu->d_prev->member)
|
|
+
|
|
+#define sd_cpu_id(cpu) (cpu->id)
|
|
+#define sd_cpu_cnt(cpu) (cpu->cnt)
|
|
+#define sd_cpu_type_str(cpu) (cpu->type->id)
|
|
+#define sd_cpu_state(cpu) (cpu->state)
|
|
+
|
|
+struct sd_cpu *sd_cpu_get(struct sd_sys *sys, const char *cpu_id);
|
|
+struct sd_cpu *sd_cpu_new(struct sd_sys *parent, const char *id,
|
|
+ const char *type, int cnt);
|
|
+
|
|
+static inline void sd_cpu_state_set(struct sd_cpu *cpu, enum sd_cpu_state state)
|
|
+{
|
|
+ cpu->state = state;
|
|
+}
|
|
+
|
|
+static inline void sd_cpu_real_type_set(struct sd_cpu *cpu, const char *type)
|
|
+{
|
|
+ strncpy(cpu->real_type, type, sizeof(cpu->real_type));
|
|
+}
|
|
+
|
|
+static inline void sd_cpu_cpu_time_us_set(struct sd_cpu *cpu, u64 value)
|
|
+{
|
|
+ cpu->d_cur->cpu_time_us = value;
|
|
+}
|
|
+
|
|
+static inline void sd_cpu_mgm_time_us_set(struct sd_cpu *cpu, u64 value)
|
|
+{
|
|
+ cpu->d_cur->mgm_time_us = value;
|
|
+}
|
|
+
|
|
+static inline void sd_cpu_wait_time_us_set(struct sd_cpu *cpu, u64 value)
|
|
+{
|
|
+ cpu->d_cur->wait_time_us = value;
|
|
+}
|
|
+
|
|
+static inline void sd_cpu_steal_time_us_set(struct sd_cpu *cpu, s64 value)
|
|
+{
|
|
+ cpu->d_cur->steal_time_us = value;
|
|
+}
|
|
+
|
|
+static inline void sd_cpu_online_time_us_set(struct sd_cpu *cpu, u64 value)
|
|
+{
|
|
+ cpu->d_cur->online_time_us = value;
|
|
+}
|
|
+
|
|
+static inline void sd_cpu_commit(struct sd_cpu *cpu)
|
|
+{
|
|
+ struct sd_sys *parent = cpu->i.parent;
|
|
+
|
|
+ cpu->i.active = 1;
|
|
+ if (parent)
|
|
+ parent->cpu_cnt_active++;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Item types
|
|
+ */
|
|
+enum sd_item_type {
|
|
+ SD_TYPE_U16,
|
|
+ SD_TYPE_U32,
|
|
+ SD_TYPE_U64,
|
|
+ SD_TYPE_S64,
|
|
+ SD_TYPE_STR,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * CPU item
|
|
+ */
|
|
+struct sd_cpu_item {
|
|
+ struct table_col table_col;
|
|
+ enum sd_item_type type;
|
|
+ int offset;
|
|
+ char *desc;
|
|
+ u64 (*fn_u64)(struct sd_cpu_item *, struct sd_cpu *);
|
|
+ s64 (*fn_s64)(struct sd_cpu_item *, struct sd_cpu *);
|
|
+ char *(*fn_str)(struct sd_cpu_item *, struct sd_cpu *);
|
|
+};
|
|
+
|
|
+#define sd_cpu_item_type(x) ((x)->type)
|
|
+#define sd_cpu_item_table_col(item) (&(item)->table_col)
|
|
+
|
|
+extern int sd_cpu_item_available(struct sd_cpu_item *item);
|
|
+extern int sd_cpu_item_cnt(void);
|
|
+
|
|
+/*
|
|
+ * Item access functions
|
|
+ */
|
|
+static inline u64 sd_cpu_item_u64(struct sd_cpu_item *item,
|
|
+ struct sd_cpu *cpu)
|
|
+{
|
|
+ return item->fn_u64(item, cpu);
|
|
+}
|
|
+
|
|
+static inline u64 sd_cpu_item_s64(struct sd_cpu_item *item,
|
|
+ struct sd_cpu *cpu)
|
|
+{
|
|
+ return item->fn_s64(item, cpu);
|
|
+}
|
|
+
|
|
+static inline char *sd_cpu_item_str(struct sd_cpu_item *item,
|
|
+ struct sd_cpu *cpu)
|
|
+{
|
|
+ if (item->fn_str)
|
|
+ return item->fn_str(item, cpu);
|
|
+ else
|
|
+ return ((char *) cpu) + item->offset;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Predefined CPU items
|
|
+ */
|
|
+extern struct sd_cpu_item sd_cpu_item_type;
|
|
+extern struct sd_cpu_item sd_cpu_item_state;
|
|
+extern struct sd_cpu_item sd_cpu_item_cpu_diff;
|
|
+extern struct sd_cpu_item sd_cpu_item_mgm_diff;
|
|
+extern struct sd_cpu_item sd_cpu_item_wait_diff;
|
|
+extern struct sd_cpu_item sd_cpu_item_steal_diff;
|
|
+extern struct sd_cpu_item sd_cpu_item_cpu;
|
|
+extern struct sd_cpu_item sd_cpu_item_mgm;
|
|
+extern struct sd_cpu_item sd_cpu_item_wait;
|
|
+extern struct sd_cpu_item sd_cpu_item_steal;
|
|
+extern struct sd_cpu_item sd_cpu_item_online;
|
|
+
|
|
+/*
|
|
+ * System item
|
|
+ */
|
|
+struct sd_sys_item {
|
|
+ struct table_col table_col;
|
|
+ enum sd_item_type type;
|
|
+ int offset;
|
|
+ char *desc;
|
|
+ int info;
|
|
+ u64 (*fn_u64)(struct sd_sys_item *, struct sd_sys *);
|
|
+ s64 (*fn_s64)(struct sd_sys_item *, struct sd_sys *);
|
|
+};
|
|
+
|
|
+#define sd_sys_item_table_col(item) (&item->table_col)
|
|
+#define sd_sys_item_type(item) (item->type)
|
|
+
|
|
+extern int sd_sys_item_available(struct sd_sys_item *item);
|
|
+extern int sd_sys_item_cnt(void);
|
|
+
|
|
+/*
|
|
+ * Item access functions
|
|
+ */
|
|
+static inline u64 sd_sys_item_u64(struct sd_sys *sys,
|
|
+ struct sd_sys_item *item)
|
|
+{
|
|
+ return item->fn_u64(item, sys);
|
|
+}
|
|
+
|
|
+static inline s64 sd_sys_item_s64(struct sd_sys *sys,
|
|
+ struct sd_sys_item *item)
|
|
+{
|
|
+ return item->fn_s64(item, sys);
|
|
+}
|
|
+
|
|
+static inline char *sd_sys_item_str(struct sd_sys *sys,
|
|
+ struct sd_sys_item *item)
|
|
+{
|
|
+ return ((char *) sys) + item->offset;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Predefined System items
|
|
+ */
|
|
+extern struct sd_sys_item sd_sys_item_cpu_cnt;
|
|
+extern struct sd_sys_item sd_sys_item_cpu_oper_cnt;
|
|
+extern struct sd_sys_item sd_sys_item_cpu_deconf_cnt;
|
|
+extern struct sd_sys_item sd_sys_item_cpu_stop_cnt;
|
|
+extern struct sd_sys_item sd_sys_item_cpu_diff;
|
|
+extern struct sd_sys_item sd_sys_item_mgm_diff;
|
|
+extern struct sd_sys_item sd_sys_item_wait_diff;
|
|
+extern struct sd_sys_item sd_sys_item_steal_diff;
|
|
+
|
|
+extern struct sd_sys_item sd_sys_item_cpu;
|
|
+extern struct sd_sys_item sd_sys_item_mgm;
|
|
+extern struct sd_sys_item sd_sys_item_wait;
|
|
+extern struct sd_sys_item sd_sys_item_steal;
|
|
+extern struct sd_sys_item sd_sys_item_online;
|
|
+
|
|
+extern struct sd_sys_item sd_sys_item_mem_max;
|
|
+extern struct sd_sys_item sd_sys_item_mem_min;
|
|
+extern struct sd_sys_item sd_sys_item_mem_use;
|
|
+
|
|
+extern struct sd_sys_item sd_sys_item_weight_cur;
|
|
+extern struct sd_sys_item sd_sys_item_weight_min;
|
|
+extern struct sd_sys_item sd_sys_item_weight_max;
|
|
+
|
|
+extern struct sd_sys_item sd_sys_item_os_name;
|
|
+
|
|
+extern struct sd_sys_item sd_sys_item_samples_total;
|
|
+extern struct sd_sys_item sd_sys_item_samples_cpu_using;
|
|
+
|
|
+/*
|
|
+ * Data gatherer backend
|
|
+ */
|
|
+struct sd_dg {
|
|
+ void (*update_sys)(void);
|
|
+ struct sd_cpu_type **cpu_type_vec;
|
|
+ struct sd_sys_item **sys_item_vec;
|
|
+ struct sd_sys_item **sys_item_enable_vec;
|
|
+ struct sd_cpu_item **cpu_item_vec;
|
|
+ struct sd_cpu_item **cpu_item_enable_vec;
|
|
+};
|
|
+
|
|
+void sd_dg_register(struct sd_dg *);
|
|
+
|
|
+/*
|
|
+ * Iterators
|
|
+ */
|
|
+#define sd_sys_iterate(parent, sys) \
|
|
+ list_iterate(sys, &parent->child_list, list)
|
|
+
|
|
+#define sd_cpu_iterate(parent, cpuptr) \
|
|
+ list_iterate(cpu, &parent->cpu_list, list)
|
|
+
|
|
+#define sd_sys_item_iterate(ptr, i) \
|
|
+ for (i = 0; (ptr = sd.dg->sys_item_vec[i]); i++)
|
|
+
|
|
+#define sd_sys_item_enable_iterate(ptr, i) \
|
|
+ for (i = 0; (ptr = sd.dg->sys_item_enable_vec[i]); i++)
|
|
+
|
|
+#define sd_cpu_item_iterate(ptr, i) \
|
|
+ for (i = 0; (ptr = sd.dg->cpu_item_vec[i]); i++)
|
|
+
|
|
+#define sd_cpu_item_enable_iterate(ptr, i) \
|
|
+ for (i = 0; (ptr = sd.dg->cpu_item_enable_vec[i]); i++)
|
|
+
|
|
+#define sd_cpu_type_iterate(ptr, i) \
|
|
+ for (i = 0; (ptr = sd.dg->cpu_type_vec[i]); i++)
|
|
+
|
|
+
|
|
+/*
|
|
+ * Offset macros
|
|
+ */
|
|
+#define SD_SYSTEM_OFFSET(x) \
|
|
+ ((unsigned long)(void *)&(((struct sd_sys *) NULL)->x))
|
|
+#define SD_CPU_INFO_OFFSET(x) \
|
|
+ ((unsigned long)(void *)&(((struct sd_cpu_info *) NULL)->x))
|
|
+
|
|
+static inline u64 l_cpu_info_u64(struct sd_cpu_info *info,
|
|
+ unsigned long offset)
|
|
+{
|
|
+ return *(u64 *)(((char *) info) + offset);
|
|
+}
|
|
+
|
|
+static inline s64 l_cpu_info_s64(struct sd_cpu_info *info,
|
|
+ unsigned long offset)
|
|
+{
|
|
+ return *(s64 *)(((char *) info) + offset);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Misc
|
|
+ */
|
|
+void sd_update(void);
|
|
+extern void sd_init(void);
|
|
+
|
|
+static inline u64 l_sub_64(u64 x, u64 y)
|
|
+{
|
|
+ return x < y ? 0 : x - y;
|
|
+}
|
|
+
|
|
+struct sd_globals {
|
|
+ struct sd_dg *dg;
|
|
+};
|
|
+
|
|
+extern struct sd_globals sd;
|
|
+
|
|
+#endif /* SD_H */
|
|
diff --git a/hyptop/sd_core.c b/hyptop/sd_core.c
|
|
new file mode 100644
|
|
index 0000000..c3aeaa0
|
|
--- /dev/null
|
|
+++ b/hyptop/sd_core.c
|
|
@@ -0,0 +1,435 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * System data module: Provide backend independent database for system data
|
|
+ * (e.g. for CPU and memory data)
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <string.h>
|
|
+#include <time.h>
|
|
+#include "sd.h"
|
|
+#include "hyptop.h"
|
|
+#include "helper.h"
|
|
+#include "opts.h"
|
|
+
|
|
+/*
|
|
+ * Internal globals for system data
|
|
+ */
|
|
+static u32 l_cpu_type_selected_mask;
|
|
+static int l_cpu_type_cnt;
|
|
+static int l_sys_item_cnt;
|
|
+static int l_cpu_item_cnt;
|
|
+static struct sd_sys *l_root_sys;
|
|
+
|
|
+/*
|
|
+ * External globals for system data
|
|
+ */
|
|
+struct sd_globals sd;
|
|
+
|
|
+/*
|
|
+ * Get root system
|
|
+ */
|
|
+struct sd_sys *sd_sys_root_get(void)
|
|
+{
|
|
+ return l_root_sys;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get CPU type by it's ID
|
|
+ */
|
|
+struct sd_cpu_type *sd_cpu_type_by_id(const char *id)
|
|
+{
|
|
+ struct sd_cpu_type *type;
|
|
+ unsigned int i;
|
|
+
|
|
+ sd_cpu_type_iterate(type, i) {
|
|
+ if (strcasecmp(id, type->id) == 0)
|
|
+ return type;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Is CPU type selected?
|
|
+ */
|
|
+int sd_cpu_type_selected(struct sd_cpu_type *cpu_type)
|
|
+{
|
|
+ return l_cpu_type_selected_mask & cpu_type->idx;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Toggle selection of CPU type
|
|
+ */
|
|
+void sd_cpu_type_select_toggle(struct sd_cpu_type *cpu_type)
|
|
+{
|
|
+ if (l_cpu_type_selected_mask & cpu_type->idx)
|
|
+ l_cpu_type_selected_mask &= ~cpu_type->idx;
|
|
+ else
|
|
+ l_cpu_type_selected_mask |= cpu_type->idx;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Select exactly specified CPU type
|
|
+ */
|
|
+void sd_cpu_type_select(struct sd_cpu_type *cpu_type)
|
|
+{
|
|
+ l_cpu_type_selected_mask = cpu_type->idx;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Select all available CPU types
|
|
+ */
|
|
+void sd_cpu_type_select_all(void)
|
|
+{
|
|
+ l_cpu_type_selected_mask = (u32)-1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Deselect all CPU types
|
|
+ */
|
|
+void sd_cpu_type_select_none(void)
|
|
+{
|
|
+ l_cpu_type_selected_mask = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Setup CPU types specified on command line
|
|
+ */
|
|
+static void l_opts_cpu_types_init(void)
|
|
+{
|
|
+ struct sd_cpu_type *type;
|
|
+ unsigned int i;
|
|
+
|
|
+ if (!g.o.cpu_types.specified)
|
|
+ return;
|
|
+
|
|
+ sd_cpu_type_select_none();
|
|
+ for (i = 0; i < g.o.cpu_types.cnt; i++) {
|
|
+ type = sd_cpu_type_by_id(g.o.cpu_types.vec[i]);
|
|
+ if (!type)
|
|
+ ERR_EXIT("Invalid CPU type \"%s\"\n",
|
|
+ g.o.cpu_types.vec[i]);
|
|
+ sd_cpu_type_select_toggle(type);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Init CPU count for all CPU types
|
|
+ */
|
|
+static void l_cpu_types_init(void)
|
|
+{
|
|
+ struct sd_sys *sys = sd_sys_root_get();
|
|
+ struct sd_cpu_type *cpu_type;
|
|
+ unsigned int i;
|
|
+
|
|
+ sd_cpu_type_iterate(cpu_type, i) {
|
|
+ sd_cpu_type_select(cpu_type);
|
|
+ cpu_type->cpu_cnt = sd_sys_item_u64(sys, &sd_sys_item_cpu_cnt);
|
|
+ }
|
|
+ sd_cpu_type_select_all();
|
|
+ l_opts_cpu_types_init();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Update system data using the data gatherer
|
|
+ */
|
|
+void sd_update(void)
|
|
+{
|
|
+ sd.dg->update_sys();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Register a data gatherer
|
|
+ */
|
|
+void sd_dg_register(struct sd_dg *dg)
|
|
+{
|
|
+ struct timespec ts = {0, SD_DG_INIT_INTERVAL_MS * 1000000};
|
|
+ struct sd_sys_item *sys_item;
|
|
+ struct sd_cpu_item *cpu_item;
|
|
+ unsigned int i;
|
|
+
|
|
+ sd.dg = dg;
|
|
+
|
|
+ for (i = 0; dg->cpu_type_vec[i]; i++)
|
|
+ dg->cpu_type_vec[i]->idx = (1UL << i);
|
|
+ l_cpu_type_cnt = i;
|
|
+ sd_sys_item_iterate(sys_item, i)
|
|
+ l_sys_item_cnt++;
|
|
+ sd_cpu_item_iterate(cpu_item, i)
|
|
+ l_cpu_item_cnt++;
|
|
+
|
|
+ sd_update();
|
|
+ nanosleep(&ts, NULL);
|
|
+ sd_update();
|
|
+
|
|
+ l_cpu_types_init();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get CPU from sys by ID
|
|
+ */
|
|
+struct sd_cpu *sd_cpu_get(struct sd_sys *sys, const char* id)
|
|
+{
|
|
+ struct sd_cpu *cpu;
|
|
+
|
|
+ list_iterate(cpu, &sys->cpu_list, list) {
|
|
+ if (strcmp(cpu->id, id) == 0)
|
|
+ return cpu;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get CPU type by ID
|
|
+ */
|
|
+static struct sd_cpu_type *l_cpu_type_by_id(const char *id)
|
|
+{
|
|
+ struct sd_cpu_type **cpu_type_vec = sd.dg->cpu_type_vec;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < l_cpu_type_cnt; i++) {
|
|
+ if (strcmp(cpu_type_vec[i]->id, id) == 0)
|
|
+ return cpu_type_vec[i];
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Allocate and initialize new CPU
|
|
+ */
|
|
+struct sd_cpu *sd_cpu_new(struct sd_sys *parent, const char *id,
|
|
+ const char *type, int cnt)
|
|
+{
|
|
+ struct sd_cpu *cpu;
|
|
+
|
|
+ cpu = ht_zalloc(sizeof(*cpu));
|
|
+ cpu->i.parent = parent;
|
|
+ strncpy(cpu->id, id, sizeof(cpu->id));
|
|
+ cpu->type = l_cpu_type_by_id(type);
|
|
+ cpu->d_cur = &cpu->d1;
|
|
+ cpu->cnt = cnt;
|
|
+
|
|
+ list_add_end(&cpu->list, &parent->cpu_list);
|
|
+
|
|
+ return cpu;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get system by ID
|
|
+ */
|
|
+struct sd_sys *sd_sys_get(struct sd_sys *parent, const char* id)
|
|
+{
|
|
+ struct sd_sys *sys;
|
|
+
|
|
+ list_iterate(sys, &parent->child_list, list) {
|
|
+ if (strcmp(sys->id, id) == 0)
|
|
+ return sys;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Allocate and initialize new system
|
|
+ */
|
|
+struct sd_sys *sd_sys_new(struct sd_sys *parent, const char *id)
|
|
+{
|
|
+ struct sd_sys *sys_new;
|
|
+
|
|
+ sys_new = ht_zalloc(sizeof(*sys_new));
|
|
+ strncpy(sys_new->id, id, sizeof(sys_new->id));
|
|
+ list_init(&sys_new->child_list);
|
|
+ list_init(&sys_new->cpu_list);
|
|
+ list_init(&sys_new->list);
|
|
+
|
|
+ if (parent) {
|
|
+ sys_new->i.parent = parent;
|
|
+ parent->child_cnt++;
|
|
+ list_add_end(&sys_new->list, &parent->child_list);
|
|
+ }
|
|
+ return sys_new;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Free system
|
|
+ */
|
|
+static void sd_sys_free(struct sd_sys *sys)
|
|
+{
|
|
+ ht_free(sys);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Free CPU
|
|
+ */
|
|
+static void sd_cpu_free(struct sd_cpu *cpu)
|
|
+{
|
|
+ ht_free(cpu);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Start update cycle for CPU
|
|
+ */
|
|
+static void l_cpu_update_start(struct sd_cpu *cpu)
|
|
+{
|
|
+ struct sd_cpu_info *tmp;
|
|
+
|
|
+ cpu->i.active = 0;
|
|
+ if (!cpu->d_prev) {
|
|
+ cpu->d_prev = &cpu->d1;
|
|
+ cpu->d_cur = &cpu->d2;
|
|
+ } else {
|
|
+ tmp = cpu->d_prev;
|
|
+ cpu->d_prev = cpu->d_cur;
|
|
+ cpu->d_cur = tmp;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Start update cycle for system
|
|
+ */
|
|
+void sd_sys_update_start(struct sd_sys *sys)
|
|
+{
|
|
+ struct sd_sys *child;
|
|
+ struct sd_cpu *cpu;
|
|
+
|
|
+ sys->i.active = 0;
|
|
+ sys->child_cnt_active = 0;
|
|
+ sys->cpu_cnt_active = 0;
|
|
+
|
|
+ list_iterate(cpu, &sys->cpu_list, list)
|
|
+ l_cpu_update_start(cpu);
|
|
+ list_iterate(child, &sys->child_list, list)
|
|
+ sd_sys_update_start(child);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * End update cycle for CPUs of a system
|
|
+ */
|
|
+static void l_cpu_update_end(struct sd_sys *sys)
|
|
+{
|
|
+ struct sd_cpu *cpu, *tmp;
|
|
+
|
|
+ /* Has system not lost any CPU? */
|
|
+ if (sys->cpu_cnt_active == sys->cpu_cnt)
|
|
+ return;
|
|
+
|
|
+ list_iterate_safe(cpu, &sys->cpu_list, list, tmp) {
|
|
+ if (!cpu->i.active) {
|
|
+ /* CPU has not been updated, remove it */
|
|
+ list_del(&cpu->list);
|
|
+ sd_cpu_free(cpu);
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * End update cycle for system
|
|
+ */
|
|
+static void l_sys_update_end(struct sd_sys *sys)
|
|
+{
|
|
+ struct sd_sys *child, *tmp;
|
|
+
|
|
+ if (sys->child_cnt_active == sys->child_cnt)
|
|
+ return;
|
|
+
|
|
+ l_cpu_update_end(sys);
|
|
+
|
|
+ list_iterate_safe(child, &sys->child_list, list, tmp) {
|
|
+ if (!child->i.active) {
|
|
+ /* child has not been updated, remove it */
|
|
+ list_del(&child->list);
|
|
+ sd_sys_free(child);
|
|
+ continue;
|
|
+ }
|
|
+ /* Recursively update child */
|
|
+ l_sys_update_end(child);
|
|
+ }
|
|
+ sys->child_cnt = sys->child_cnt_active;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * End update cycle for system
|
|
+ */
|
|
+void sd_sys_update_end(struct sd_sys *sys, u64 update_time_us)
|
|
+{
|
|
+ sys->update_time_us = update_time_us;
|
|
+ l_sys_update_end(sys);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Is system item available?
|
|
+ */
|
|
+int sd_sys_item_available(struct sd_sys_item *item)
|
|
+{
|
|
+ struct sd_sys_item *ptr;
|
|
+ unsigned int i;
|
|
+
|
|
+ sd_sys_item_iterate(ptr, i) {
|
|
+ if (item == ptr)
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Number of system items
|
|
+ */
|
|
+int sd_sys_item_cnt(void)
|
|
+{
|
|
+ return l_sys_item_cnt;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Is CPU item avaiable?
|
|
+ */
|
|
+int sd_cpu_item_available(struct sd_cpu_item *item)
|
|
+{
|
|
+ struct sd_cpu_item *ptr;
|
|
+ unsigned int i;
|
|
+
|
|
+ sd_cpu_item_iterate(ptr, i) {
|
|
+ if (item == ptr)
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Number of CPU items
|
|
+ */
|
|
+int sd_cpu_item_cnt(void)
|
|
+{
|
|
+ return l_cpu_item_cnt;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Init system data module
|
|
+ */
|
|
+void sd_init(void)
|
|
+{
|
|
+ l_root_sys = sd_sys_new(NULL, "root");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * CPU Types
|
|
+ */
|
|
+struct sd_cpu_type sd_cpu_type_ifl = {
|
|
+ .id = SD_CPU_TYPE_STR_IFL,
|
|
+ .desc = "Integrated Facility for Linux",
|
|
+ .hotkey = 'i',
|
|
+};
|
|
+
|
|
+struct sd_cpu_type sd_cpu_type_cp = {
|
|
+ .id = SD_CPU_TYPE_STR_CP,
|
|
+ .desc = "Central processor",
|
|
+ .hotkey = 'p',
|
|
+};
|
|
+
|
|
+struct sd_cpu_type sd_cpu_type_un = {
|
|
+ .id = SD_CPU_TYPE_STR_UN,
|
|
+ .desc = "Unspecified processor type",
|
|
+ .hotkey = 'u',
|
|
+};
|
|
diff --git a/hyptop/sd_cpu_items.c b/hyptop/sd_cpu_items.c
|
|
new file mode 100644
|
|
index 0000000..803a9b9
|
|
--- /dev/null
|
|
+++ b/hyptop/sd_cpu_items.c
|
|
@@ -0,0 +1,178 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Provide CPU Items
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include "sd.h"
|
|
+
|
|
+/*
|
|
+ * Return CPU type of "cpu"
|
|
+ */
|
|
+static char *l_cpu_type(struct sd_cpu_item *item, struct sd_cpu *cpu)
|
|
+{
|
|
+ (void) item;
|
|
+ return sd_cpu_type_str(cpu);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return CPU state of "cpu"
|
|
+ */
|
|
+static char *l_cpu_state(struct sd_cpu_item *item, struct sd_cpu *cpu)
|
|
+{
|
|
+ (void) item;
|
|
+ return sd_cpu_state_str(sd_cpu_state(cpu));
|
|
+}
|
|
+
|
|
+/*
|
|
+ * value = (value_current - value_prev) / online_time_diff
|
|
+ */
|
|
+static double l_cpu_diff(struct sd_cpu_item *item, struct sd_cpu *cpu, int sign)
|
|
+{
|
|
+ u64 online_time_diff_us;
|
|
+ double factor, diff_us;
|
|
+
|
|
+ if (sd_cpu_state(cpu) == SD_CPU_STATE_STOPPED)
|
|
+ return 0;
|
|
+ if (!cpu->d_prev || !cpu->d_cur)
|
|
+ return 0;
|
|
+ if (!sd_cpu_type_selected(cpu->type))
|
|
+ return 0;
|
|
+ online_time_diff_us = l_sub_64(cpu->d_cur->online_time_us,
|
|
+ cpu->d_prev->online_time_us);
|
|
+ if (online_time_diff_us == 0)
|
|
+ return 0;
|
|
+
|
|
+ factor = ((double) online_time_diff_us) / 1000000;
|
|
+ if (sign)
|
|
+ diff_us = l_cpu_info_s64(cpu->d_cur, item->offset) -
|
|
+ l_cpu_info_s64(cpu->d_prev, item->offset);
|
|
+ else
|
|
+ diff_us = l_sub_64(l_cpu_info_u64(cpu->d_cur, item->offset),
|
|
+ l_cpu_info_u64(cpu->d_prev, item->offset));
|
|
+ diff_us /= factor;
|
|
+ return diff_us;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * unsigned value = (value_current - value_prev) / online_time_diff
|
|
+ */
|
|
+static u64 l_cpu_diff_u64(struct sd_cpu_item *item, struct sd_cpu *cpu)
|
|
+{
|
|
+ return l_cpu_diff(item, cpu, 0);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * signed value = (value_current - value_prev) / online_time_diff
|
|
+ */
|
|
+static s64 l_cpu_diff_s64(struct sd_cpu_item *item, struct sd_cpu *cpu)
|
|
+{
|
|
+ return l_cpu_diff(item, cpu, 1);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return cpu item value
|
|
+ */
|
|
+static u64 l_cpu_item_64(struct sd_cpu_item *item, struct sd_cpu *cpu)
|
|
+{
|
|
+ if (sd_cpu_state(cpu) == SD_CPU_STATE_STOPPED)
|
|
+ return 0;
|
|
+ if (!cpu->d_cur)
|
|
+ return 0;
|
|
+ if (!sd_cpu_type_selected(cpu->type))
|
|
+ return 0;
|
|
+ return l_cpu_info_u64(cpu->d_cur, item->offset);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * CPU item definitions
|
|
+ */
|
|
+struct sd_cpu_item sd_cpu_item_type = {
|
|
+ .table_col = TABLE_COL_STR('p', "type"),
|
|
+ .type = SD_TYPE_STR,
|
|
+ .desc = "CPU type",
|
|
+ .fn_str = l_cpu_type,
|
|
+};
|
|
+
|
|
+struct sd_cpu_item sd_cpu_item_state = {
|
|
+ .table_col = TABLE_COL_STR('a', "stat"),
|
|
+ .type = SD_TYPE_STR,
|
|
+ .desc = "CPU state",
|
|
+ .fn_str = l_cpu_state,
|
|
+};
|
|
+
|
|
+struct sd_cpu_item sd_cpu_item_cpu_diff = {
|
|
+ .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'c', "cpu"),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .offset = SD_CPU_INFO_OFFSET(cpu_time_us),
|
|
+ .desc = "CPU time per second",
|
|
+ .fn_u64 = l_cpu_diff_u64,
|
|
+};
|
|
+
|
|
+struct sd_cpu_item sd_cpu_item_mgm_diff = {
|
|
+ .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'm', "mgm"),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .offset = SD_CPU_INFO_OFFSET(mgm_time_us),
|
|
+ .desc = "Management time per second",
|
|
+ .fn_u64 = l_cpu_diff_u64,
|
|
+};
|
|
+
|
|
+struct sd_cpu_item sd_cpu_item_wait_diff = {
|
|
+ .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'w', "wait"),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .offset = SD_CPU_INFO_OFFSET(wait_time_us),
|
|
+ .desc = "Wait time per second",
|
|
+ .fn_u64 = l_cpu_diff_u64,
|
|
+};
|
|
+
|
|
+struct sd_cpu_item sd_cpu_item_steal_diff = {
|
|
+ .table_col = TABLE_COL_STIME_DIFF_SUM(table_col_unit_perc, 's',
|
|
+ "steal"),
|
|
+ .type = SD_TYPE_S64,
|
|
+ .offset = SD_CPU_INFO_OFFSET(steal_time_us),
|
|
+ .desc = "Steal time per second",
|
|
+ .fn_s64 = l_cpu_diff_s64,
|
|
+};
|
|
+
|
|
+struct sd_cpu_item sd_cpu_item_cpu = {
|
|
+ .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'C', "cpu+"),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .offset = SD_CPU_INFO_OFFSET(cpu_time_us),
|
|
+ .desc = "Total CPU time",
|
|
+ .fn_u64 = l_cpu_item_64,
|
|
+};
|
|
+
|
|
+struct sd_cpu_item sd_cpu_item_mgm = {
|
|
+ .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'M', "mgm+"),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .offset = SD_CPU_INFO_OFFSET(mgm_time_us),
|
|
+ .desc = "Total management time",
|
|
+ .fn_u64 = l_cpu_item_64,
|
|
+};
|
|
+
|
|
+struct sd_cpu_item sd_cpu_item_wait = {
|
|
+ .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'W', "wait+"),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .offset = SD_CPU_INFO_OFFSET(wait_time_us),
|
|
+ .desc = "Total wait time",
|
|
+ .fn_u64 = l_cpu_item_64,
|
|
+};
|
|
+
|
|
+struct sd_cpu_item sd_cpu_item_steal = {
|
|
+ .table_col = TABLE_COL_STIME_SUM(table_col_unit_hm, 'S', "steal+"),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .offset = SD_CPU_INFO_OFFSET(steal_time_us),
|
|
+ .desc = "Total steal time",
|
|
+ .fn_u64 = l_cpu_item_64,
|
|
+};
|
|
+
|
|
+struct sd_cpu_item sd_cpu_item_online = {
|
|
+ .table_col = TABLE_COL_TIME_MAX(table_col_unit_dhm, 'o', "online"),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .offset = SD_CPU_INFO_OFFSET(online_time_us),
|
|
+ .desc = "Online time",
|
|
+ .fn_u64 = l_cpu_item_64,
|
|
+};
|
|
diff --git a/hyptop/sd_sys_items.c b/hyptop/sd_sys_items.c
|
|
new file mode 100644
|
|
index 0000000..046faf4
|
|
--- /dev/null
|
|
+++ b/hyptop/sd_sys_items.c
|
|
@@ -0,0 +1,325 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Provide System Items
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include "sd.h"
|
|
+
|
|
+/*
|
|
+ * Count CPUs of system according to active CPU types and requested CPU state
|
|
+ */
|
|
+static u64 l_sys_cpu_cnt_gen(struct sd_sys *sys, enum sd_cpu_state state,
|
|
+ int all)
|
|
+{
|
|
+ struct sd_cpu *cpu;
|
|
+ u32 cnt = 0;
|
|
+
|
|
+ sd_cpu_iterate(sys, cpu) {
|
|
+ if (!sd_cpu_type_selected(cpu->type))
|
|
+ continue;
|
|
+ if (all || sd_cpu_state(cpu) == state)
|
|
+ cnt += cpu->cnt;
|
|
+ }
|
|
+ return cnt;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Count all CPUs of system
|
|
+ */
|
|
+static u64 l_sys_cpu_cnt(struct sd_sys_item *item, struct sd_sys *sys)
|
|
+{
|
|
+ (void) item;
|
|
+ return l_sys_cpu_cnt_gen(sys, SD_CPU_STATE_UNKNOWN, 1);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Count CPUs of system with state stopped
|
|
+ */
|
|
+static u64 l_sys_cpu_st_cnt(struct sd_sys_item *item, struct sd_sys *sys)
|
|
+{
|
|
+ (void) item;
|
|
+
|
|
+ return l_sys_cpu_cnt_gen(sys, SD_CPU_STATE_STOPPED, 0);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Count CPUs of system with state operating
|
|
+ */
|
|
+static u64 l_sys_cpu_op_cnt(struct sd_sys_item *item, struct sd_sys *sys)
|
|
+{
|
|
+ (void) item;
|
|
+
|
|
+ return l_sys_cpu_cnt_gen(sys, SD_CPU_STATE_OPERATING, 0);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Count CPUs of system with state deconfigured
|
|
+ */
|
|
+static u64 l_sys_cpu_dc_cnt(struct sd_sys_item *item,
|
|
+ struct sd_sys *sys)
|
|
+{
|
|
+ (void) item;
|
|
+
|
|
+ return l_sys_cpu_cnt_gen(sys, SD_CPU_STATE_DECONFIG, 0);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get u64 system item value from "sys"
|
|
+ */
|
|
+static u64 l_sys_item_u64(struct sd_sys_item *item, struct sd_sys *sys)
|
|
+{
|
|
+ switch (item->type) {
|
|
+ case SD_TYPE_U16:
|
|
+ return *(u16 *)(((char *) sys) + item->offset);
|
|
+ case SD_TYPE_U32:
|
|
+ return *(u32 *)(((char *) sys) + item->offset);
|
|
+ case SD_TYPE_U64:
|
|
+ return *(u64 *)(((char *) sys) + item->offset);
|
|
+ case SD_TYPE_S64:
|
|
+ case SD_TYPE_STR:
|
|
+ break;
|
|
+ }
|
|
+ assert(0);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Calculate system item out of sum of CPU info
|
|
+ */
|
|
+static u64 l_sys_cpu_info_sum_u64(struct sd_sys_item *item, struct sd_sys *sys)
|
|
+{
|
|
+ struct sd_cpu *cpu;
|
|
+ u64 rc = 0;
|
|
+
|
|
+ sd_cpu_iterate(sys, cpu) {
|
|
+ if (!sd_cpu_type_selected(cpu->type))
|
|
+ continue;
|
|
+ rc += l_cpu_info_u64(cpu->d_cur, item->offset);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Calculate system item out of MAX of CPU info
|
|
+ */
|
|
+static u64 l_sys_cpu_info_max_u64(struct sd_sys_item *item, struct sd_sys *sys)
|
|
+{
|
|
+ struct sd_cpu *cpu;
|
|
+ u64 rc = 0;
|
|
+
|
|
+ sd_cpu_iterate(sys, cpu) {
|
|
+ if (!sd_cpu_type_selected(cpu->type))
|
|
+ continue;
|
|
+ rc = MAX(rc, l_cpu_info_u64(cpu->d_cur, item->offset));
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * value = (value_current - value_prev) / online_time_diff
|
|
+ */
|
|
+static double l_cpu_info_diff_u64(struct sd_sys_item *item, struct sd_cpu *cpu,
|
|
+ int sign)
|
|
+{
|
|
+ u64 online_time_diff_us;
|
|
+ double factor, diff_us;
|
|
+
|
|
+ if (!sd_cpu_type_selected(cpu->type))
|
|
+ return 0;
|
|
+ if (sd_cpu_state(cpu) == SD_CPU_STATE_STOPPED)
|
|
+ return 0;
|
|
+ online_time_diff_us = l_sub_64(cpu->d_cur->online_time_us,
|
|
+ cpu->d_prev->online_time_us);
|
|
+ if (online_time_diff_us == 0)
|
|
+ return 0;
|
|
+ if (sign) {
|
|
+ diff_us = l_cpu_info_s64(cpu->d_cur, item->offset) -
|
|
+ l_cpu_info_s64(cpu->d_prev, item->offset);
|
|
+ } else {
|
|
+ diff_us = l_sub_64(l_cpu_info_u64(cpu->d_cur, item->offset),
|
|
+ l_cpu_info_u64(cpu->d_prev, item->offset));
|
|
+ }
|
|
+ factor = ((double) online_time_diff_us) / 1000000;
|
|
+ diff_us /= factor;
|
|
+ return diff_us;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * SUM over all CPUs: value = (value_current - value_prev) / online_time_diff
|
|
+ */
|
|
+static u64 l_sys_cpu_info_diff_u64(struct sd_sys_item *item, struct sd_sys *sys)
|
|
+{
|
|
+ struct sd_cpu *cpu;
|
|
+ u64 rc = 0;
|
|
+
|
|
+ sd_cpu_iterate(sys, cpu) {
|
|
+ if (!cpu->d_prev || !cpu->d_cur)
|
|
+ return 0;
|
|
+ rc += l_cpu_info_diff_u64(item, cpu, 0);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * SUM over all CPUs: value = (value_current - value_prev) / online_time_diff
|
|
+ */
|
|
+static s64 l_sys_cpu_info_diff_s64(struct sd_sys_item *item, struct sd_sys *sys)
|
|
+{
|
|
+ struct sd_cpu *cpu;
|
|
+ s64 rc = 0;
|
|
+
|
|
+ sd_cpu_iterate(sys, cpu) {
|
|
+ if (!cpu->d_prev || !cpu->d_cur)
|
|
+ return 0;
|
|
+ rc += l_cpu_info_diff_u64(item, cpu, 1);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * System item definitions
|
|
+ */
|
|
+struct sd_sys_item sd_sys_item_cpu_cnt = {
|
|
+ .table_col = TABLE_COL_CNT_SUM('#', "#cpu"),
|
|
+ .type = SD_TYPE_U32,
|
|
+ .desc = "Number of CPUs",
|
|
+ .fn_u64 = l_sys_cpu_cnt,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_cpu_oper_cnt = {
|
|
+ .table_col = TABLE_COL_CNT_SUM('e', "#cpuope"),
|
|
+ .type = SD_TYPE_U32,
|
|
+ .desc = "Number of operating CPUs",
|
|
+ .fn_u64 = l_sys_cpu_op_cnt,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_cpu_stop_cnt = {
|
|
+ .table_col = TABLE_COL_CNT_SUM('p', "#cpusp"),
|
|
+ .type = SD_TYPE_U32,
|
|
+ .desc = "Number of stopped CPUs",
|
|
+ .fn_u64 = l_sys_cpu_st_cnt,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_cpu_deconf_cnt = {
|
|
+ .table_col = TABLE_COL_CNT_SUM('d', "#cpudc"),
|
|
+ .type = SD_TYPE_U32,
|
|
+ .desc = "Number of deconfigured CPUs",
|
|
+ .fn_u64 = l_sys_cpu_dc_cnt,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_cpu_diff = {
|
|
+ .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'c', "cpu"),
|
|
+ .offset = SD_CPU_INFO_OFFSET(cpu_time_us),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .desc = "CPU time per second",
|
|
+ .fn_u64 = l_sys_cpu_info_diff_u64,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_mgm_diff = {
|
|
+ .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'm', "mgm"),
|
|
+ .offset = SD_CPU_INFO_OFFSET(mgm_time_us),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .desc = "Management time per second",
|
|
+ .fn_u64 = l_sys_cpu_info_diff_u64,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_wait_diff = {
|
|
+ .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'w', "wait"),
|
|
+ .offset = SD_CPU_INFO_OFFSET(wait_time_us),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .desc = "Wait time per second",
|
|
+ .fn_u64 = l_sys_cpu_info_diff_u64,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_steal_diff = {
|
|
+ .table_col = TABLE_COL_STIME_DIFF_SUM(table_col_unit_perc, 's',
|
|
+ "steal"),
|
|
+ .offset = SD_CPU_INFO_OFFSET(steal_time_us),
|
|
+ .type = SD_TYPE_S64,
|
|
+ .desc = "Steal time per second",
|
|
+ .fn_s64 = l_sys_cpu_info_diff_s64,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_cpu = {
|
|
+ .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'C', "cpu+"),
|
|
+ .offset = SD_CPU_INFO_OFFSET(cpu_time_us),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .desc = "Total CPU time",
|
|
+ .fn_u64 = l_sys_cpu_info_sum_u64,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_wait = {
|
|
+ .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'W', "wait+"),
|
|
+ .offset = SD_CPU_INFO_OFFSET(wait_time_us),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .desc = "Total wait time",
|
|
+ .fn_u64 = l_sys_cpu_info_sum_u64,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_mgm = {
|
|
+ .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'M', "mgm+"),
|
|
+ .offset = SD_CPU_INFO_OFFSET(mgm_time_us),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .desc = "Total management time",
|
|
+ .fn_u64 = l_sys_cpu_info_sum_u64,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_steal = {
|
|
+ .table_col = TABLE_COL_STIME_SUM(table_col_unit_hm, 'S', "steal+"),
|
|
+ .offset = SD_CPU_INFO_OFFSET(steal_time_us),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .desc = "Total steal time",
|
|
+ .fn_u64 = l_sys_cpu_info_sum_u64,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_online = {
|
|
+ .table_col = TABLE_COL_TIME_MAX(table_col_unit_dhm, 'o', "online"),
|
|
+ .offset = SD_CPU_INFO_OFFSET(online_time_us),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .desc = "Online time",
|
|
+ .fn_u64 = l_sys_cpu_info_max_u64,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_mem_max = {
|
|
+ .table_col = TABLE_COL_MEM_SUM(table_col_unit_gib, 'a', "memmax"),
|
|
+ .offset = SD_SYSTEM_OFFSET(mem.max_kib),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .desc = "Maximum memory",
|
|
+ .fn_u64 = l_sys_item_u64,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_mem_use = {
|
|
+ .table_col = TABLE_COL_MEM_SUM(table_col_unit_gib, 'u', "memuse"),
|
|
+ .offset = SD_SYSTEM_OFFSET(mem.use_kib),
|
|
+ .type = SD_TYPE_U64,
|
|
+ .desc = "Used memory",
|
|
+ .fn_u64 = l_sys_item_u64,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_weight_cur = {
|
|
+ .table_col = TABLE_COL_CNT_MAX('r', "wcur"),
|
|
+ .offset = SD_SYSTEM_OFFSET(weight.cur),
|
|
+ .type = SD_TYPE_U16,
|
|
+ .desc = "Current weight",
|
|
+ .fn_u64 = l_sys_item_u64,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_weight_min = {
|
|
+ .table_col = TABLE_COL_CNT_MAX('n', "wmin"),
|
|
+ .offset = SD_SYSTEM_OFFSET(weight.min),
|
|
+ .type = SD_TYPE_U16,
|
|
+ .desc = "Minimum weight",
|
|
+ .fn_u64 = l_sys_item_u64,
|
|
+};
|
|
+
|
|
+struct sd_sys_item sd_sys_item_weight_max = {
|
|
+ .table_col = TABLE_COL_CNT_MAX('x', "wmax"),
|
|
+ .offset = SD_SYSTEM_OFFSET(weight.max),
|
|
+ .type = SD_TYPE_U16,
|
|
+ .desc = "Maximum weight",
|
|
+ .fn_u64 = l_sys_item_u64,
|
|
+};
|
|
diff --git a/hyptop/table.c b/hyptop/table.c
|
|
new file mode 100644
|
|
index 0000000..352960f
|
|
--- /dev/null
|
|
+++ b/hyptop/table.c
|
|
@@ -0,0 +1,1231 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Table module: Provide line mode and curses base table
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <stdlib.h>
|
|
+#include <stdio.h>
|
|
+#include <ncurses.h>
|
|
+#include <string.h>
|
|
+#include <errno.h>
|
|
+#include <ctype.h>
|
|
+#include "table.h"
|
|
+#include "hyptop.h"
|
|
+#include "helper.h"
|
|
+
|
|
+#define L_ROWS_EXTRA 2 /* head + last */
|
|
+
|
|
+#define table_col_iterate(t, col, i) \
|
|
+ for (i = 0, col = t->col_vec[0]; col != NULL; col = t->col_vec[++i])
|
|
+
|
|
+/*
|
|
+ * Is row marked?
|
|
+ */
|
|
+static int l_row_is_marked(struct table *t, struct table_row *row)
|
|
+{
|
|
+ struct table_mark_key *key;
|
|
+
|
|
+ list_iterate(key, &t->mark_key_list, list) {
|
|
+ if (strcmp(row->entries[0].str, key->str) == 0)
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add mark key to table
|
|
+ */
|
|
+static void l_mark_key_add(struct table *t, char *str)
|
|
+{
|
|
+ struct table_mark_key *key;
|
|
+
|
|
+ key = ht_zalloc(sizeof(*key));
|
|
+ strncpy(key->str, str, sizeof(key->str));
|
|
+ list_add_end(&key->list, &t->mark_key_list);
|
|
+ t->mark_keys_cnt++;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Remove mark key from table
|
|
+ */
|
|
+static void l_mark_key_remove(struct table *t, char *str)
|
|
+{
|
|
+ struct table_mark_key *key;
|
|
+
|
|
+ list_iterate(key, &t->mark_key_list, list) {
|
|
+ if (strcmp(str, key->str) == 0) {
|
|
+ list_del(&key->list);
|
|
+ ht_free(key);
|
|
+ t->mark_keys_cnt--;
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Delete all mark keys from table
|
|
+ */
|
|
+void table_row_mark_del_all(struct table *t)
|
|
+{
|
|
+ struct table_mark_key *key, *tmp;
|
|
+ struct table_row *row;
|
|
+
|
|
+ list_iterate(row, &t->row_list, list)
|
|
+ row->marked = 0;
|
|
+ list_iterate_safe(key, &t->mark_key_list, list, tmp) {
|
|
+ list_del(&key->list);
|
|
+ ht_free(key);
|
|
+ }
|
|
+ t->mark_keys_cnt = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Toggle mark for "row"
|
|
+ */
|
|
+void table_row_mark_toggle(struct table *t, struct table_row *row)
|
|
+{
|
|
+ if (row->marked) {
|
|
+ l_mark_key_remove(t, row->entries[0].str);
|
|
+ row->marked = 0;
|
|
+ t->row_cnt_marked--;
|
|
+ if (t->row_cnt_marked == 0)
|
|
+ t->mode_hide_unmarked = 0;
|
|
+ } else {
|
|
+ l_mark_key_add(t, row->entries[0].str);
|
|
+ row->marked = 1;
|
|
+ t->row_cnt_marked++;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Toggle mark by key
|
|
+ */
|
|
+void table_row_mark_toggle_by_key(struct table *t, const char *str)
|
|
+{
|
|
+ struct table_row *row;
|
|
+
|
|
+ list_iterate(row, &t->row_list, list) {
|
|
+ if (strcmp(str, row->entries[0].str) == 0)
|
|
+ table_row_mark_toggle(t, row);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Is column selected?
|
|
+ */
|
|
+static int l_col_selected(struct table *t, struct table_col *col)
|
|
+{
|
|
+ return t->col_selected == col;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get number of rows for table
|
|
+ */
|
|
+static int l_row_cnt(struct table *t)
|
|
+{
|
|
+ return t->mode_hide_unmarked ? t->row_cnt_marked : t->row_cnt;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get number of data rows that we can display on screen
|
|
+ */
|
|
+static int l_row_cnt_displ(struct table *t)
|
|
+{
|
|
+ return g.c.row_cnt - t->row_cnt_extra;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Alloc a new row for table
|
|
+ */
|
|
+struct table_row *table_row_alloc(struct table *t)
|
|
+{
|
|
+ struct table_row *table_row;
|
|
+
|
|
+ table_row = ht_zalloc(sizeof(*table_row));
|
|
+ table_row->entries = ht_zalloc(sizeof(*table_row->entries) *
|
|
+ t->col_cnt);
|
|
+ list_init(&table_row->list);
|
|
+ return table_row;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Free table row
|
|
+ */
|
|
+static void table_row_free(struct table_row *table_row)
|
|
+{
|
|
+ ht_free(table_row->entries);
|
|
+ ht_free(table_row);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Allocate and initialize a new table
|
|
+ */
|
|
+struct table *table_new(int extra_rows, int sorted, int first_bold,
|
|
+ int with_units)
|
|
+{
|
|
+ struct table *t = ht_zalloc(sizeof(*t));
|
|
+
|
|
+ list_init(&t->row_list);
|
|
+ list_init(&t->mark_key_list);
|
|
+ t->row_cnt_marked = 0;
|
|
+ if (with_units)
|
|
+ t->row_cnt_extra = extra_rows + L_ROWS_EXTRA + 1;
|
|
+ else
|
|
+ t->row_cnt_extra = extra_rows + L_ROWS_EXTRA;
|
|
+ t->attr_with_units = with_units;
|
|
+ t->attr_sorted_table = sorted;
|
|
+ t->attr_first_bold = first_bold;
|
|
+
|
|
+ return t;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize headline for one column
|
|
+ */
|
|
+static void l_col_headline_init(struct table *t, struct table_col *col)
|
|
+{
|
|
+ char *ptr;
|
|
+
|
|
+ strcpy(col->p->head_first, col->head);
|
|
+ ptr = strchr(col->p->head_first, tolower(col->hotkey));
|
|
+ assert(ptr != NULL);
|
|
+ *ptr = 0;
|
|
+ col->p->head_char[0] = col->hotkey;
|
|
+ strcpy(col->p->head_last, ++ptr);
|
|
+ if (!t->attr_sorted_table) {
|
|
+ ht_str_to_upper(col->p->head_first);
|
|
+ ht_str_to_upper(col->p->head_last);
|
|
+ col->p->head_char[0] = toupper(col->p->head_char[0]);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize the max width values for a column
|
|
+ */
|
|
+static void l_col_max_width_init(struct table *t, struct table_col *col)
|
|
+{
|
|
+ /* Units are displayed with brackets, therefore (+2) */
|
|
+ if (t->attr_with_units)
|
|
+ col->p->max_width = MAX(strlen(col->head),
|
|
+ strlen(col->unit->str) + 2);
|
|
+ else
|
|
+ col->p->max_width = strlen(col->head);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add a new column to table
|
|
+ */
|
|
+void table_col_add(struct table *t, struct table_col *col)
|
|
+{
|
|
+ col->p = ht_zalloc(sizeof(*col->p));
|
|
+ col->p->col_nr = t->col_cnt;
|
|
+ col->p->enabled = 1;
|
|
+ t->col_cnt++;
|
|
+ t->col_vec = ht_realloc(t->col_vec, sizeof(void *) *
|
|
+ (t->col_cnt + 1));
|
|
+ t->col_vec[t->col_cnt - 1] = col;
|
|
+ t->col_vec[t->col_cnt] = NULL;
|
|
+ if (!t->col_selected && t->attr_sorted_table)
|
|
+ t->col_selected = col;
|
|
+ if (t->row_last)
|
|
+ table_row_free(t->row_last);
|
|
+ t->row_last = table_row_alloc(t);
|
|
+ l_col_headline_init(t, col);
|
|
+ l_col_max_width_init(t, col);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize last row
|
|
+ */
|
|
+static void l_row_last_init(struct table *t)
|
|
+{
|
|
+ memset(t->row_last->entries, 0,
|
|
+ t->col_cnt * sizeof(struct table_entry));
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Delete all rows of a table
|
|
+ */
|
|
+void table_row_del_all(struct table *t)
|
|
+{
|
|
+ struct table_row *row, *tmp;
|
|
+
|
|
+ list_iterate_safe(row, &t->row_list, list, tmp) {
|
|
+ list_del(&row->list);
|
|
+ table_row_free(row);
|
|
+ }
|
|
+ l_row_last_init(t);
|
|
+ t->row_cnt_marked = 0;
|
|
+ t->ready = 0;
|
|
+ t->row_cnt = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Reset table
|
|
+ */
|
|
+void table_reset(struct table *t)
|
|
+{
|
|
+ table_row_mark_del_all(t);
|
|
+ table_row_del_all(t);
|
|
+ t->mode_sort_inverse = 0;
|
|
+ t->mode_select = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return true, if "e1" is less than "e2"
|
|
+ */
|
|
+static int l_entry_less_than(enum table_col_type type, struct table_entry *e1,
|
|
+ struct table_entry *e2)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TABLE_COL_TYPE_U64:
|
|
+ return (e1->d.u64.v1 < e2->d.u64.v1);
|
|
+ case TABLE_COL_TYPE_S64:
|
|
+ return (e1->d.s64.v1 < e2->d.s64.v1);
|
|
+ case TABLE_COL_TYPE_STR:
|
|
+ return (strcmp(e1->str, e2->str) > 0);
|
|
+ }
|
|
+ return 0; /* Keep gcc quite */
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return true, if "row1" is less than "row2"
|
|
+ */
|
|
+static int l_row_less_than(struct table *t, struct table_row *row1,
|
|
+ struct table_row *row2)
|
|
+{
|
|
+ struct table_col *col = t->col_selected;
|
|
+ struct table_entry *e1 = &row1->entries[col->p->col_nr];
|
|
+ struct table_entry *e2 = &row2->entries[col->p->col_nr];
|
|
+
|
|
+ if ((t->mode_sort_inverse && !col->p->rsort) ||
|
|
+ (!t->mode_sort_inverse && col->p->rsort))
|
|
+ return !l_entry_less_than(col->type, e1, e2);
|
|
+ else
|
|
+ return l_entry_less_than(col->type, e1, e2);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Calculate: e1 = e1 + e2
|
|
+ */
|
|
+static void l_entry_sum(enum table_col_type type, struct table_entry *e1,
|
|
+ struct table_entry *e2)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TABLE_COL_TYPE_U64:
|
|
+ e1->d.u64.v1 += e2->d.u64.v1;
|
|
+ return;
|
|
+ case TABLE_COL_TYPE_S64:
|
|
+ e1->d.s64.v1 += e2->d.s64.v1;
|
|
+ return;
|
|
+ default:
|
|
+ assert(0);
|
|
+ return;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Calculate: e1 = MAX(e1, e2)
|
|
+ */
|
|
+static void l_entry_max(enum table_col_type type, struct table_entry *e1,
|
|
+ struct table_entry *e2)
|
|
+{
|
|
+ switch (type) {
|
|
+ case TABLE_COL_TYPE_U64:
|
|
+ e1->d.u64.v1 = MAX(e1->d.u64.v1, e2->d.u64.v1);
|
|
+ return;
|
|
+ case TABLE_COL_TYPE_S64:
|
|
+ e1->d.s64.v1 = MAX(e1->d.s64.v1, e2->d.s64.v1);
|
|
+ return;
|
|
+ default:
|
|
+ assert(0);
|
|
+ return;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Aggregate "row" to "last row"
|
|
+ */
|
|
+static void l_row_last_agg(struct table *t, struct table_row *table_row)
|
|
+{
|
|
+ struct table_col *col;
|
|
+ int col_nr;
|
|
+
|
|
+ table_col_iterate(t, col, col_nr) {
|
|
+ struct table_entry *e_last = &t->row_last->entries[col_nr];
|
|
+ struct table_entry *e_new = &table_row->entries[col_nr];
|
|
+
|
|
+ switch (col->agg) {
|
|
+ case TABLE_COL_AGG_SUM:
|
|
+ l_entry_sum(col->type, e_last, e_new);
|
|
+ break;
|
|
+ case TABLE_COL_AGG_MAX:
|
|
+ l_entry_max(col->type, e_last, e_new);
|
|
+ break;
|
|
+ case TABLE_COL_AGG_NONE:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Format row: Invoke unit callback and adjust max width of column
|
|
+ */
|
|
+static void l_row_format(struct table *t, struct table_row *row)
|
|
+{
|
|
+ unsigned int len, col_nr;
|
|
+ struct table_col *col;
|
|
+
|
|
+ table_col_iterate(t, col, col_nr) {
|
|
+ struct table_entry *e = &row->entries[col_nr];
|
|
+ if (col->agg == TABLE_COL_AGG_NONE && row == t->row_last)
|
|
+ len = 0;
|
|
+ else
|
|
+ len = col->unit->fn(col, e);
|
|
+ assert(len < TABLE_STR_MAX);
|
|
+ if (len > col->p->max_width)
|
|
+ col->p->max_width = len;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Calculate last row
|
|
+ */
|
|
+static void l_row_last_calc(struct table *t)
|
|
+{
|
|
+ struct table_row *row;
|
|
+
|
|
+ l_row_last_init(t);
|
|
+ list_iterate(row, &t->row_list, list) {
|
|
+ if (t->mode_hide_unmarked && !row->marked)
|
|
+ continue;
|
|
+ l_row_last_agg(t, row);
|
|
+ }
|
|
+ l_row_format(t, t->row_last);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Finish table after all rows have been added
|
|
+ */
|
|
+void table_finish(struct table *t)
|
|
+{
|
|
+ l_row_last_calc(t);
|
|
+ t->ready = 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add new row to table
|
|
+ */
|
|
+void table_row_add(struct table *t, struct table_row *row)
|
|
+{
|
|
+ struct table_row *tmp;
|
|
+
|
|
+ l_row_format(t, row);
|
|
+
|
|
+ if (list_is_empty(&t->row_list) || !t->attr_sorted_table) {
|
|
+ list_add_end(&row->list, &t->row_list);
|
|
+ } else {
|
|
+ list_iterate(tmp, &t->row_list, list) {
|
|
+ if (l_row_less_than(t, tmp, row))
|
|
+ break;
|
|
+ }
|
|
+ list_add_end(&row->list, &tmp->list);
|
|
+ }
|
|
+ if (l_row_is_marked(t, row)) {
|
|
+ row->marked = 1;
|
|
+ t->row_cnt_marked++;
|
|
+ }
|
|
+ t->row_cnt++;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Rebuild table: Reformat all rows and adjust max width values
|
|
+ */
|
|
+void table_rebuild(struct table *t)
|
|
+{
|
|
+ struct table_col *col;
|
|
+ struct table_row *row;
|
|
+ unsigned int i;
|
|
+
|
|
+ table_col_iterate(t, col, i)
|
|
+ l_col_max_width_init(t, col);
|
|
+ list_iterate(row, &t->row_list, list)
|
|
+ l_row_format(t, row);
|
|
+ l_row_format(t, t->row_last);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Sort table (TODO: Use better sorting algorithm)
|
|
+ */
|
|
+static void l_table_sort(struct table *t)
|
|
+{
|
|
+ struct table_row *row_min;
|
|
+ struct table_row *row;
|
|
+ struct table_row *tmp;
|
|
+ struct list list;
|
|
+
|
|
+ list_init(&list);
|
|
+
|
|
+ /*
|
|
+ * Sort row list into temp list
|
|
+ */
|
|
+ while (!list_is_empty(&t->row_list)) {
|
|
+ row_min = NULL;
|
|
+
|
|
+ list_iterate(row, &t->row_list, list) {
|
|
+ if (row_min == NULL)
|
|
+ row_min = row;
|
|
+ else if (l_row_less_than(t, row, row_min))
|
|
+ row_min = row;
|
|
+ }
|
|
+ list_del(&row_min->list);
|
|
+ list_add(&row_min->list, &list);
|
|
+ }
|
|
+ /*
|
|
+ * Copy temp list to original list
|
|
+ */
|
|
+ list_iterate_safe(row, &list, list, tmp) {
|
|
+ list_del(&row->list);
|
|
+ list_add_end(&row->list, &t->row_list);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Adjust table values for select mode (e.g. for window resize or scrolling)
|
|
+ */
|
|
+static void l_adjust_values_select_mode(struct table *t)
|
|
+{
|
|
+ int row_cnt_displ = l_row_cnt_displ(t);
|
|
+ int row_cnt = l_row_cnt(t);
|
|
+
|
|
+ /* We went out of range with row selection */
|
|
+ if (t->row_nr_select >= row_cnt)
|
|
+ t->row_nr_select = row_cnt - 1;
|
|
+
|
|
+ /* Is selected row within visible area? */
|
|
+ if (t->row_nr_select < t->row_nr_begin) {
|
|
+ /* Selected row is above area: Scroll up */
|
|
+ t->row_nr_begin = t->row_nr_select;
|
|
+ } else if (t->row_nr_select - t->row_nr_begin >= row_cnt_displ) {
|
|
+ /* Selected row is below area: Scroll down */
|
|
+ t->row_nr_begin = MAX(t->row_nr_select - row_cnt_displ + 1, 0);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Adjust table values (e.g. for window resize or scrolling)
|
|
+ */
|
|
+static void l_adjust_values(struct table *t)
|
|
+{
|
|
+ int row_cnt_displ = l_row_cnt_displ(t);
|
|
+ int row_cnt = l_row_cnt(t);
|
|
+
|
|
+ if (t->mode_select)
|
|
+ l_adjust_values_select_mode(t);
|
|
+ /* If we do not use the whole screen, scroll up */
|
|
+ if (row_cnt - t->row_nr_begin < row_cnt_displ)
|
|
+ t->row_nr_begin = MAX(row_cnt - row_cnt_displ, 0);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Number of rows to be scrolled for page scroll
|
|
+ */
|
|
+static int l_scroll_page_row_cnt(struct table *t)
|
|
+{
|
|
+ /* We have two rows overlap for scrolling pages */
|
|
+ return l_row_cnt_displ(t) - 2;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Scroll table down
|
|
+ */
|
|
+void table_scroll_down(struct table *t, enum table_scroll_unit scroll_unit)
|
|
+{
|
|
+ switch (scroll_unit) {
|
|
+ case TABLE_SCROLL_LINE:
|
|
+ t->row_nr_begin++;
|
|
+ break;
|
|
+ case TABLE_SCROLL_PAGE:
|
|
+ t->row_nr_begin += l_scroll_page_row_cnt(t);
|
|
+ break;
|
|
+ case TABLE_SCROLL_LAST:
|
|
+ t->row_nr_begin = t->row_cnt;
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Scroll table up
|
|
+ */
|
|
+void table_scroll_up(struct table *t, enum table_scroll_unit scroll_unit)
|
|
+{
|
|
+ switch (scroll_unit) {
|
|
+ case TABLE_SCROLL_LINE:
|
|
+ t->row_nr_begin = MAX(t->row_nr_begin - 1, 0);
|
|
+ break;
|
|
+ case TABLE_SCROLL_PAGE:
|
|
+ t->row_nr_begin =
|
|
+ MAX(t->row_nr_begin - l_scroll_page_row_cnt(t), 0);
|
|
+ break;
|
|
+ case TABLE_SCROLL_LAST:
|
|
+ t->row_nr_begin = 0;
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return selected row
|
|
+ */
|
|
+static struct table_row *l_selected_row(struct table *t)
|
|
+{
|
|
+ struct table_row *row;
|
|
+ int row_nr = 0;
|
|
+
|
|
+ list_iterate(row, &t->row_list, list) {
|
|
+ if (t->mode_hide_unmarked && !row->marked)
|
|
+ continue;
|
|
+ if (row_nr == t->row_nr_select)
|
|
+ return row;
|
|
+ row_nr++;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Toggle mark for selected row
|
|
+ */
|
|
+static void l_row_select_mark_toggle(struct table *t)
|
|
+{
|
|
+ struct table_row *row;
|
|
+
|
|
+ row = l_selected_row(t);
|
|
+ table_row_mark_toggle(t, row);
|
|
+ l_row_last_calc(t);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Switch select mode off
|
|
+ */
|
|
+static void l_select_mode_off(struct table *t)
|
|
+{
|
|
+ t->mode_select = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Switch select mode on
|
|
+ */
|
|
+static void l_select_mode_on(struct table *t)
|
|
+{
|
|
+ t->mode_select = 1;
|
|
+ t->row_nr_select = t->row_nr_begin;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get key for selected row
|
|
+ */
|
|
+void table_row_select_key_get(struct table *t, char str[TABLE_STR_MAX])
|
|
+{
|
|
+ struct table_row *row;
|
|
+
|
|
+ row = l_selected_row(t);
|
|
+ strncpy(str, row->entries[0].str, TABLE_STR_MAX);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Select row one page down
|
|
+ */
|
|
+void table_row_select_down(struct table *t, enum table_scroll_unit scroll_unit)
|
|
+{
|
|
+ switch (scroll_unit) {
|
|
+ case TABLE_SCROLL_LINE:
|
|
+ t->row_nr_select++;
|
|
+ break;
|
|
+ case TABLE_SCROLL_PAGE:
|
|
+ t->row_nr_select += g.c.row_cnt - t->row_cnt_extra;
|
|
+ break;
|
|
+ case TABLE_SCROLL_LAST:
|
|
+ t->row_nr_select = t->row_cnt;
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Select row one page up
|
|
+ */
|
|
+void table_row_select_up(struct table *t, enum table_scroll_unit scroll_unit)
|
|
+{
|
|
+ switch (scroll_unit) {
|
|
+ case TABLE_SCROLL_LINE:
|
|
+ t->row_nr_select = MAX(t->row_nr_select - 1, 0);
|
|
+ break;
|
|
+ case TABLE_SCROLL_PAGE:
|
|
+ t->row_nr_select = MAX(t->row_nr_begin -
|
|
+ (g.c.row_cnt - t->row_cnt_extra), 0);
|
|
+ break;
|
|
+ case TABLE_SCROLL_LAST:
|
|
+ t->row_nr_select = 0;
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Toggle "hide unmarked" mode
|
|
+ */
|
|
+static int l_mode_hide_unmarked_toggle(struct table *t)
|
|
+{
|
|
+ if (t->row_cnt_marked == 0)
|
|
+ return -ENODEV;
|
|
+ t->mode_hide_unmarked = t->mode_hide_unmarked ? 0 : 1;
|
|
+ t->row_nr_select = 0;
|
|
+ l_row_last_calc(t);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Is it possible to scroll down the table?
|
|
+ */
|
|
+static int l_can_scroll_down(struct table *t)
|
|
+{
|
|
+ int row_cnt = t->mode_hide_unmarked ? t->row_cnt_marked : t->row_cnt;
|
|
+ int row_cnt_real = g.c.row_cnt - t->row_cnt_extra;
|
|
+
|
|
+ return (row_cnt - t->row_nr_begin > row_cnt_real);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Is it possible to scroll up the table?
|
|
+ */
|
|
+static int l_can_scroll_up(struct table *t)
|
|
+{
|
|
+ return (t->row_nr_begin > 0);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Update the status field
|
|
+ */
|
|
+static void l_status_update(struct table *t)
|
|
+{
|
|
+ struct table_entry *e_status = &t->row_last->entries[0];
|
|
+
|
|
+ if (g.o.batch_mode_specified)
|
|
+ return;
|
|
+
|
|
+ if (l_can_scroll_down(t) && l_can_scroll_up(t))
|
|
+ strcpy(e_status->str, "|");
|
|
+ else if (l_can_scroll_up(t))
|
|
+ strcpy(e_status->str, "^");
|
|
+ else if (l_can_scroll_down(t))
|
|
+ strcpy(e_status->str, "V");
|
|
+ else
|
|
+ strcpy(e_status->str, "=");
|
|
+
|
|
+ if (t->attr_sorted_table) {
|
|
+ strcat(e_status->str, ":");
|
|
+ if (t->mode_sort_inverse)
|
|
+ strcat(e_status->str, "^");
|
|
+ else
|
|
+ strcat(e_status->str, "V");
|
|
+ }
|
|
+ strcat(e_status->str, ":");
|
|
+ if (t->mode_select)
|
|
+ strcat(e_status->str, "S");
|
|
+ else
|
|
+ strcat(e_status->str, "N");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print string with alignment
|
|
+ */
|
|
+static void l_str_print(struct table_col *col, const char *str)
|
|
+{
|
|
+ char unit[10];
|
|
+
|
|
+ if (col->align == TABLE_COL_ALIGN_LEFT)
|
|
+ sprintf(unit, "%%-%ds", col->p->max_width);
|
|
+ else
|
|
+ sprintf(unit, "%%%ds", col->p->max_width);
|
|
+ hyptop_printf(unit, str);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print string for "col"
|
|
+ */
|
|
+static void l_col_print(struct table *t, struct table_col *col, const char *str)
|
|
+{
|
|
+ if (l_col_selected(t, col))
|
|
+ ht_underline_on();
|
|
+ if (col->p->col_nr == 0 && t->attr_first_bold)
|
|
+ ht_bold_on();
|
|
+
|
|
+ l_str_print(col, str);
|
|
+
|
|
+ if (l_col_selected(t, col))
|
|
+ ht_underline_off();
|
|
+ if (col->p->col_nr == 0 && t->attr_first_bold)
|
|
+ ht_bold_off();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print status field
|
|
+ */
|
|
+static void l_status_print(struct table *t, const char *str)
|
|
+{
|
|
+ ht_bold_on();
|
|
+ l_str_print(t->col_vec[0], str);
|
|
+ ht_bold_off();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print headline of column
|
|
+ */
|
|
+static void l_col_headline_print(struct table *t, struct table_col *col)
|
|
+{
|
|
+ unsigned int len = strlen(col->head);
|
|
+ char blank_str[TABLE_STR_MAX];
|
|
+ (void) t;
|
|
+
|
|
+ memset(blank_str, ' ', col->p->max_width - len);
|
|
+ blank_str[col->p->max_width - len] = 0;
|
|
+
|
|
+ if (l_col_selected(t, col))
|
|
+ ht_bold_on();
|
|
+ if (col->align == TABLE_COL_ALIGN_RIGHT)
|
|
+ hyptop_printf("%s", blank_str);
|
|
+ hyptop_printf("%s", col->p->head_first);
|
|
+ if (t->attr_sorted_table)
|
|
+ ht_underline_on();
|
|
+ hyptop_printf("%s", col->p->head_char);
|
|
+ if (t->attr_sorted_table)
|
|
+ ht_underline_off();
|
|
+ hyptop_printf("%s", col->p->head_last);
|
|
+ if (col->align == TABLE_COL_ALIGN_LEFT)
|
|
+ hyptop_printf("%s", blank_str);
|
|
+ if (l_col_selected(t, col))
|
|
+ ht_bold_off();
|
|
+
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print headline for table
|
|
+ */
|
|
+static void l_headline_print(struct table *t)
|
|
+{
|
|
+ struct table_col *col;
|
|
+ int col_nr, first = 1;
|
|
+
|
|
+ ht_reverse_on();
|
|
+ /* Print all column headlines */
|
|
+ table_col_iterate(t, col, col_nr) {
|
|
+ if (!col->p->enabled)
|
|
+ continue;
|
|
+ if (first)
|
|
+ first = 0;
|
|
+ else
|
|
+ hyptop_printf(" ");
|
|
+ l_col_headline_print(t, col);
|
|
+ }
|
|
+ /* This creates a black bar to the end of the line */
|
|
+ hyptop_print_seek_back(0);
|
|
+ ht_reverse_off();
|
|
+ hyptop_print_nl();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print unit line for table
|
|
+ */
|
|
+static void l_unitline_print(struct table *t)
|
|
+{
|
|
+ struct table_col *col;
|
|
+ int col_nr, first = 1;
|
|
+ char unit_str[20];
|
|
+
|
|
+ if (!t->attr_with_units)
|
|
+ return;
|
|
+ ht_reverse_on();
|
|
+ /* Print all column units */
|
|
+ table_col_iterate(t, col, col_nr) {
|
|
+ if (!col->p->enabled)
|
|
+ continue;
|
|
+ if (first)
|
|
+ first = 0;
|
|
+ else
|
|
+ hyptop_printf(" ");
|
|
+ if (l_col_selected(t, col))
|
|
+ ht_bold_on();
|
|
+ snprintf(unit_str, sizeof(unit_str), "(%s)", col->unit->str);
|
|
+ l_str_print(col, unit_str);
|
|
+ if (l_col_selected(t, col))
|
|
+ ht_bold_off();
|
|
+ }
|
|
+ /* This creates a black bar to the end of the line */
|
|
+ hyptop_print_seek_back(0);
|
|
+ ht_reverse_off();
|
|
+ hyptop_print_nl();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print one table row
|
|
+ */
|
|
+static void l_row_print(struct table *t, struct table_row *row)
|
|
+{
|
|
+ struct table_col *col;
|
|
+ int first = 1, col_nr;
|
|
+
|
|
+ table_col_iterate(t, col, col_nr) {
|
|
+ struct table_entry *e = &row->entries[col_nr];
|
|
+ if (!col->p->enabled)
|
|
+ continue;
|
|
+ if (!first)
|
|
+ hyptop_printf(" ");
|
|
+ else
|
|
+ first = 0;
|
|
+ if (row == t->row_last && col_nr == 0)
|
|
+ l_status_print(t, e->str);
|
|
+ else
|
|
+ l_col_print(t, col, e->str);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print table under curses
|
|
+ */
|
|
+static void l_table_print_curses(struct table *t)
|
|
+{
|
|
+ struct table_row *row;
|
|
+ int row_nr = 0;
|
|
+
|
|
+ if (!t->ready)
|
|
+ return;
|
|
+ l_adjust_values(t);
|
|
+ l_status_update(t);
|
|
+ l_headline_print(t);
|
|
+ l_unitline_print(t);
|
|
+ list_iterate(row, &t->row_list, list) {
|
|
+ if (t->mode_hide_unmarked && !row->marked)
|
|
+ continue;
|
|
+ if (row_nr < t->row_nr_begin) {
|
|
+ row_nr++;
|
|
+ continue;
|
|
+ }
|
|
+ if (row_nr - t->row_nr_begin >= g.c.row_cnt - t->row_cnt_extra)
|
|
+ break;
|
|
+ if (t->mode_select && row_nr == t->row_nr_select)
|
|
+ ht_reverse_on();
|
|
+ if (row->marked)
|
|
+ ht_bold_on();
|
|
+ l_row_print(t, row);
|
|
+ if (t->mode_select && row_nr == t->row_nr_select) {
|
|
+#ifdef WITH_SCROLL_BAR
|
|
+ hyptop_print_seek_back(1);
|
|
+#else
|
|
+ hyptop_print_seek_back(0);
|
|
+#endif
|
|
+ ht_reverse_off();
|
|
+ }
|
|
+ if (row->marked)
|
|
+ ht_bold_off();
|
|
+ hyptop_print_nl();
|
|
+ row_nr++;
|
|
+ }
|
|
+ ht_reverse_on();
|
|
+ l_row_print(t, t->row_last);
|
|
+ hyptop_print_seek_back(0);
|
|
+ ht_reverse_off();
|
|
+#ifdef WITH_SCROLL_BAR
|
|
+ if (t->mode_hide_unmarked)
|
|
+ ht_print_scroll_bar(t->row_cnt_marked, t->row_nr_begin,
|
|
+ t->row_cnt_extra - 1, 1,
|
|
+ l_can_scroll_up(t),
|
|
+ l_can_scroll_down(t), 1);
|
|
+ else
|
|
+ ht_print_scroll_bar(t->row_cnt, t->row_nr_begin,
|
|
+ t->row_cnt_extra - 1, 1,
|
|
+ l_can_scroll_up(t),
|
|
+ l_can_scroll_down(t), 1);
|
|
+#endif
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print table under batch mode
|
|
+ */
|
|
+static void l_table_print_all(struct table *t)
|
|
+{
|
|
+ struct table_row *row;
|
|
+
|
|
+ l_headline_print(t);
|
|
+ l_unitline_print(t);
|
|
+ list_iterate(row, &t->row_list, list) {
|
|
+ l_row_print(t, row);
|
|
+ hyptop_print_nl();
|
|
+ }
|
|
+ l_row_print(t, t->row_last);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print table to screen
|
|
+ */
|
|
+void table_print(struct table *t)
|
|
+{
|
|
+ if (g.o.batch_mode_specified)
|
|
+ l_table_print_all(t);
|
|
+ else
|
|
+ l_table_print_curses(t);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return column by hotkey
|
|
+ */
|
|
+static struct table_col *l_col_by_hotkey(struct table *t, char hotkey)
|
|
+{
|
|
+ struct table_col *col;
|
|
+ int col_nr;
|
|
+
|
|
+ table_col_iterate(t, col, col_nr) {
|
|
+ if (col->hotkey == hotkey)
|
|
+ return col;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Select next unit for column with "hotkey"
|
|
+ */
|
|
+void table_col_unit_next(struct table *t, char hotkey)
|
|
+{
|
|
+ struct table_col *col;
|
|
+ int i;
|
|
+
|
|
+ col = l_col_by_hotkey(t, hotkey);
|
|
+ if (!col || !col->unit_fam)
|
|
+ assert(0);
|
|
+
|
|
+ for (i = 0; col->unit_fam[i] != NULL; i++) {
|
|
+ if (col->unit != col->unit_fam[i])
|
|
+ continue;
|
|
+
|
|
+ if (col->unit_fam[i + 1] == NULL)
|
|
+ col->unit = col->unit_fam[0];
|
|
+ else
|
|
+ col->unit = col->unit_fam[i + 1];
|
|
+ return;
|
|
+ }
|
|
+ assert(0);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Select previous unit for column with "hotkey"
|
|
+ */
|
|
+void table_col_unit_prev(struct table *t, char hotkey)
|
|
+{
|
|
+ struct table_col *col;
|
|
+ int i;
|
|
+
|
|
+ col = l_col_by_hotkey(t, hotkey);
|
|
+ if (!col || !col->unit_fam)
|
|
+ assert(0);
|
|
+
|
|
+ for (i = 0; col->unit_fam[i] != NULL; i++) {
|
|
+ if (col->unit != col->unit_fam[i])
|
|
+ continue;
|
|
+
|
|
+ if (i == 0) {
|
|
+ int j;
|
|
+
|
|
+ for (j = 0; col->unit_fam[j] != NULL; j++) {}
|
|
+ col->unit = col->unit_fam[j - 1];
|
|
+ } else {
|
|
+ col->unit = col->unit_fam[i - 1];
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ assert(0);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set unit for column
|
|
+ */
|
|
+int table_col_unit_set(struct table *t, char hotkey, const char *str)
|
|
+{
|
|
+ struct table_col *col;
|
|
+ int i;
|
|
+
|
|
+ col = l_col_by_hotkey(t, hotkey);
|
|
+ if (!col)
|
|
+ return -ENODEV;
|
|
+
|
|
+ for (i = 0; col->unit_fam[i] != NULL; i++) {
|
|
+ if (strcasecmp(col->unit_fam[i]->str, str) == 0) {
|
|
+ col->unit = col->unit_fam[i];
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Select column by hotkey
|
|
+ */
|
|
+int table_col_select(struct table *t, char hotkey)
|
|
+{
|
|
+ struct table_col *col;
|
|
+
|
|
+ if (!t->attr_sorted_table)
|
|
+ assert(0);
|
|
+ col = l_col_by_hotkey(t, hotkey);
|
|
+ if (!col || !col->p->enabled)
|
|
+ return -ENODEV;
|
|
+ if (t->col_selected == col) {
|
|
+ t->mode_sort_inverse = t->mode_sort_inverse ? 0 : 1;
|
|
+ } else {
|
|
+ t->mode_sort_inverse = 0;
|
|
+ t->col_selected = col;
|
|
+ }
|
|
+ table_rebuild(t);
|
|
+ l_table_sort(t);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Select next column
|
|
+ */
|
|
+void table_col_select_next(struct table *t)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = t->col_selected->p->col_nr + 1; i < t->col_cnt; i++) {
|
|
+ if (t->col_vec[i]->p->enabled)
|
|
+ goto found;
|
|
+ }
|
|
+ return;
|
|
+found:
|
|
+ t->col_selected = t->col_vec[i];
|
|
+ l_table_sort(t);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Select previous column
|
|
+ */
|
|
+void table_col_select_prev(struct table *t)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = t->col_selected->p->col_nr - 1; i >= 0; i--) {
|
|
+ if (t->col_vec[i]->p->enabled)
|
|
+ goto found;
|
|
+ }
|
|
+ return;
|
|
+found:
|
|
+ t->col_selected = t->col_vec[i];
|
|
+ l_table_sort(t);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Toggle enabled status for column
|
|
+ */
|
|
+void table_col_enable_toggle(struct table *t, char hotkey)
|
|
+{
|
|
+ struct table_col *col;
|
|
+
|
|
+ col = l_col_by_hotkey(t, hotkey);
|
|
+ if (!col || col->p->col_nr == 0)
|
|
+ return;
|
|
+ col->p->enabled = col->p->enabled ? 0 : 1;
|
|
+ if (col == t->col_selected)
|
|
+ t->col_selected = t->col_vec[0];
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Process input for table
|
|
+ */
|
|
+void table_process_input(struct table *t, int c)
|
|
+{
|
|
+ switch (c) {
|
|
+ case '<':
|
|
+ if (t->attr_sorted_table)
|
|
+ table_col_select_prev(t);
|
|
+ break;
|
|
+ case '>':
|
|
+ if (t->attr_sorted_table)
|
|
+ table_col_select_next(t);
|
|
+ break;
|
|
+ case '.':
|
|
+ if (l_mode_hide_unmarked_toggle(t) == 0)
|
|
+ l_select_mode_off(t);
|
|
+ break;
|
|
+ case '+':
|
|
+ if (!t->attr_with_units)
|
|
+ break;
|
|
+ table_col_unit_next(t, t->col_selected->hotkey);
|
|
+ table_rebuild(t);
|
|
+ break;
|
|
+ case '-':
|
|
+ if (!t->attr_with_units)
|
|
+ break;
|
|
+ table_col_unit_prev(t, t->col_selected->hotkey);
|
|
+ table_rebuild(t);
|
|
+ break;
|
|
+ case 'G':
|
|
+ if (t->mode_select)
|
|
+ table_row_select_down(t, TABLE_SCROLL_LAST);
|
|
+ else
|
|
+ table_scroll_down(t, TABLE_SCROLL_LAST);
|
|
+ break;
|
|
+ case 'g':
|
|
+ if (t->mode_select)
|
|
+ table_row_select_up(t, TABLE_SCROLL_LAST);
|
|
+ else
|
|
+ table_scroll_up(t, TABLE_SCROLL_LAST);
|
|
+ break;
|
|
+ case KEY_NPAGE:
|
|
+ if (t->mode_select)
|
|
+ table_row_select_down(t, TABLE_SCROLL_PAGE);
|
|
+ else
|
|
+ table_scroll_down(t, TABLE_SCROLL_PAGE);
|
|
+ break;
|
|
+ case KEY_PPAGE:
|
|
+ if (t->mode_select)
|
|
+ table_row_select_up(t, TABLE_SCROLL_PAGE);
|
|
+ else
|
|
+ table_scroll_up(t, TABLE_SCROLL_PAGE);
|
|
+ break;
|
|
+ case 'j':
|
|
+ case KEY_DOWN:
|
|
+ if (t->mode_select)
|
|
+ table_row_select_down(t, TABLE_SCROLL_LINE);
|
|
+ else
|
|
+ table_scroll_down(t, TABLE_SCROLL_LINE);
|
|
+ break;
|
|
+ case 'k':
|
|
+ case KEY_UP:
|
|
+ if (t->mode_select)
|
|
+ table_row_select_up(t, TABLE_SCROLL_LINE);
|
|
+ else
|
|
+ table_scroll_up(t, TABLE_SCROLL_LINE);
|
|
+ break;
|
|
+ case ' ':
|
|
+ if (t->mode_select) {
|
|
+ l_row_select_mark_toggle(t);
|
|
+ } else {
|
|
+ table_row_mark_del_all(t);
|
|
+ t->mode_hide_unmarked = 0;
|
|
+ }
|
|
+ break;
|
|
+ case 'l':
|
|
+ case KEY_RIGHT:
|
|
+ if (!t->mode_select)
|
|
+ l_select_mode_on(t);
|
|
+ break;
|
|
+ case 'h':
|
|
+ case KEY_LEFT:
|
|
+ if (t->mode_select)
|
|
+ l_select_mode_off(t);
|
|
+ break;
|
|
+ default:
|
|
+ if (t->attr_sorted_table)
|
|
+ table_col_select(t, c);
|
|
+ }
|
|
+}
|
|
diff --git a/hyptop/table.h b/hyptop/table.h
|
|
new file mode 100644
|
|
index 0000000..c441245
|
|
--- /dev/null
|
|
+++ b/hyptop/table.h
|
|
@@ -0,0 +1,424 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Table module: Provide line mode and curses base table
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef TABLE_H
|
|
+#define TABLE_H
|
|
+
|
|
+#include <string.h>
|
|
+#include <assert.h>
|
|
+#include "list.h"
|
|
+#include "helper.h"
|
|
+
|
|
+#define TABLE_STR_MAX 64
|
|
+#define TABLE_HEADING_SIZE 20
|
|
+
|
|
+struct table_col;
|
|
+struct table_entry;
|
|
+
|
|
+/*
|
|
+ * Table Column Unit
|
|
+ */
|
|
+struct table_col_unit {
|
|
+ int (*fn)(struct table_col*, struct table_entry *);
|
|
+ const char *str;
|
|
+ char *desc;
|
|
+ char hotkey;
|
|
+};
|
|
+
|
|
+/* Predefined units */
|
|
+extern struct table_col_unit table_col_unit_str;
|
|
+extern struct table_col_unit table_col_unit_cnt;
|
|
+extern struct table_col_unit table_col_unit_kib;
|
|
+extern struct table_col_unit table_col_unit_mib;
|
|
+extern struct table_col_unit table_col_unit_gib;
|
|
+extern struct table_col_unit table_col_unit_us;
|
|
+extern struct table_col_unit table_col_unit_ms;
|
|
+extern struct table_col_unit table_col_unit_s;
|
|
+extern struct table_col_unit table_col_unit_hm;
|
|
+extern struct table_col_unit table_col_unit_dhm;
|
|
+extern struct table_col_unit table_col_unit_perc;
|
|
+extern struct table_col_unit table_col_unit_vis;
|
|
+
|
|
+/* Predefined families */
|
|
+extern struct table_col_unit *table_col_unit_fam_str[];
|
|
+extern struct table_col_unit *table_col_unit_fam_cnt[];
|
|
+extern struct table_col_unit *table_col_unit_fam_mem[];
|
|
+extern struct table_col_unit *table_col_unit_fam_time[];
|
|
+extern struct table_col_unit *table_col_unit_fam_time_diff[];
|
|
+extern struct table_col_unit *table_col_unit_fam_vis[];
|
|
+
|
|
+/*
|
|
+ * Table Column Type
|
|
+ */
|
|
+enum table_col_type {
|
|
+ TABLE_COL_TYPE_U64,
|
|
+ TABLE_COL_TYPE_S64,
|
|
+ TABLE_COL_TYPE_STR,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Table Column Alignment
|
|
+ */
|
|
+enum table_col_align {
|
|
+ TABLE_COL_ALIGN_LEFT,
|
|
+ TABLE_COL_ALIGN_RIGHT,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Table Column Aggregation
|
|
+ */
|
|
+enum table_col_agg {
|
|
+ TABLE_COL_AGG_SUM,
|
|
+ TABLE_COL_AGG_MAX,
|
|
+ TABLE_COL_AGG_NONE,
|
|
+};
|
|
+
|
|
+static inline const char *table_col_agg_str(enum table_col_agg agg)
|
|
+{
|
|
+ switch (agg) {
|
|
+ case TABLE_COL_AGG_SUM:
|
|
+ return "sum";
|
|
+ case TABLE_COL_AGG_MAX:
|
|
+ return "max";
|
|
+ case TABLE_COL_AGG_NONE:
|
|
+ return "none";
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Table Column
|
|
+ */
|
|
+struct table_col_priv {
|
|
+ unsigned int max_width;
|
|
+ int col_nr;
|
|
+ int enabled;
|
|
+ char head_first[TABLE_HEADING_SIZE];
|
|
+ char head_char[2];
|
|
+ char head_last[TABLE_HEADING_SIZE];
|
|
+ int rsort;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Table Column Specification
|
|
+ */
|
|
+struct table_col_spec {
|
|
+ char hotkey;
|
|
+ char *unit_str;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Table Column
|
|
+ */
|
|
+struct table_col {
|
|
+ enum table_col_type type;
|
|
+ struct table_col_unit *unit;
|
|
+ struct table_col_unit **unit_fam;
|
|
+ enum table_col_align align;
|
|
+ enum table_col_agg agg;
|
|
+ char hotkey;
|
|
+ char head[TABLE_HEADING_SIZE];
|
|
+ struct table_col_priv *p;
|
|
+};
|
|
+
|
|
+static inline int table_col_enabled(struct table_col *col)
|
|
+{
|
|
+ return col->p->enabled;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Table Column Constructor Macros
|
|
+ */
|
|
+#define TABLE_COL_STR(l, h) \
|
|
+{ \
|
|
+ .type = TABLE_COL_TYPE_STR, \
|
|
+ .unit = &table_col_unit_str, \
|
|
+ .unit_fam = table_col_unit_fam_str, \
|
|
+ .align = TABLE_COL_ALIGN_RIGHT, \
|
|
+ .agg = TABLE_COL_AGG_NONE, \
|
|
+ .hotkey = l, \
|
|
+ .head = h, \
|
|
+}
|
|
+
|
|
+#define TABLE_COL_STR_LEFT(l, h) \
|
|
+{ \
|
|
+ .type = TABLE_COL_TYPE_STR, \
|
|
+ .unit = &table_col_unit_str, \
|
|
+ .unit_fam = table_col_unit_fam_str, \
|
|
+ .align = TABLE_COL_ALIGN_LEFT, \
|
|
+ .agg = TABLE_COL_AGG_NONE, \
|
|
+ .hotkey = l, \
|
|
+ .head = h, \
|
|
+}
|
|
+
|
|
+#define TABLE_COL_CNT_SUM(l, h) \
|
|
+{ \
|
|
+ .type = TABLE_COL_TYPE_U64, \
|
|
+ .unit = &table_col_unit_cnt, \
|
|
+ .unit_fam = table_col_unit_fam_cnt, \
|
|
+ .align = TABLE_COL_ALIGN_RIGHT, \
|
|
+ .agg = TABLE_COL_AGG_SUM, \
|
|
+ .hotkey = l, \
|
|
+ .head = h, \
|
|
+}
|
|
+
|
|
+#define TABLE_COL_CNT_NONE(l, h) \
|
|
+{ \
|
|
+ .type = TABLE_COL_TYPE_U64, \
|
|
+ .unit = &table_col_unit_cnt, \
|
|
+ .unit_fam = table_col_unit_fam_cnt, \
|
|
+ .align = TABLE_COL_ALIGN_RIGHT, \
|
|
+ .agg = TABLE_COL_AGG_NONE, \
|
|
+ .hotkey = l, \
|
|
+ .head = h, \
|
|
+}
|
|
+
|
|
+#define TABLE_COL_CNT_MAX(l, h) \
|
|
+{ \
|
|
+ .type = TABLE_COL_TYPE_U64, \
|
|
+ .unit = &table_col_unit_cnt, \
|
|
+ .unit_fam = table_col_unit_fam_cnt, \
|
|
+ .align = TABLE_COL_ALIGN_RIGHT, \
|
|
+ .agg = TABLE_COL_AGG_MAX, \
|
|
+ .hotkey = l, \
|
|
+ .head = h, \
|
|
+}
|
|
+
|
|
+#define TABLE_COL_MEM_SUM(f, l, h) \
|
|
+{ \
|
|
+ .type = TABLE_COL_TYPE_U64, \
|
|
+ .unit = &f, \
|
|
+ .unit_fam = table_col_unit_fam_mem, \
|
|
+ .align = TABLE_COL_ALIGN_RIGHT, \
|
|
+ .agg = TABLE_COL_AGG_SUM, \
|
|
+ .hotkey = l, \
|
|
+ .head = h, \
|
|
+}
|
|
+
|
|
+#define TABLE_COL_TIME_SUM(f, l, h) \
|
|
+{ \
|
|
+ .type = TABLE_COL_TYPE_U64, \
|
|
+ .unit = &f, \
|
|
+ .unit_fam = table_col_unit_fam_time, \
|
|
+ .align = TABLE_COL_ALIGN_RIGHT, \
|
|
+ .agg = TABLE_COL_AGG_SUM, \
|
|
+ .hotkey = l, \
|
|
+ .head = h, \
|
|
+}
|
|
+
|
|
+#define TABLE_COL_TIME_DIFF_SUM(f, l, h) \
|
|
+{ \
|
|
+ .type = TABLE_COL_TYPE_U64, \
|
|
+ .unit = &f, \
|
|
+ .unit_fam = table_col_unit_fam_time_diff, \
|
|
+ .align = TABLE_COL_ALIGN_RIGHT, \
|
|
+ .agg = TABLE_COL_AGG_SUM, \
|
|
+ .hotkey = l, \
|
|
+ .head = h, \
|
|
+}
|
|
+
|
|
+#define TABLE_COL_STIME_SUM(f, l, h) \
|
|
+{ \
|
|
+ .type = TABLE_COL_TYPE_S64, \
|
|
+ .unit = &f, \
|
|
+ .unit_fam = table_col_unit_fam_time, \
|
|
+ .align = TABLE_COL_ALIGN_RIGHT, \
|
|
+ .agg = TABLE_COL_AGG_SUM, \
|
|
+ .hotkey = l, \
|
|
+ .head = h, \
|
|
+}
|
|
+
|
|
+#define TABLE_COL_STIME_DIFF_SUM(f, l, h) \
|
|
+{ \
|
|
+ .type = TABLE_COL_TYPE_S64, \
|
|
+ .unit = &f, \
|
|
+ .unit_fam = table_col_unit_fam_time_diff, \
|
|
+ .align = TABLE_COL_ALIGN_RIGHT, \
|
|
+ .agg = TABLE_COL_AGG_SUM, \
|
|
+ .hotkey = l, \
|
|
+ .head = h, \
|
|
+}
|
|
+
|
|
+#define TABLE_COL_TIME_MAX(f, l, h) \
|
|
+{ \
|
|
+ .type = TABLE_COL_TYPE_U64, \
|
|
+ .unit = &f, \
|
|
+ .unit_fam = table_col_unit_fam_time, \
|
|
+ .align = TABLE_COL_ALIGN_RIGHT, \
|
|
+ .agg = TABLE_COL_AGG_MAX, \
|
|
+ .hotkey = l, \
|
|
+ .head = h, \
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set reverse sort property for column
|
|
+ */
|
|
+static inline void table_col_rsort(struct table_col *col)
|
|
+{
|
|
+ col->p->rsort = 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Column member access macros
|
|
+ */
|
|
+#define table_col_hotkey(col) ((col)->hotkey)
|
|
+#define table_col_head(col) ((col)->head)
|
|
+#define table_col_unit_str(col) ((col)->unit->str)
|
|
+
|
|
+/*
|
|
+ * Table Entry
|
|
+ */
|
|
+struct table_entry {
|
|
+ union {
|
|
+ struct {
|
|
+ u64 v1;
|
|
+ u64 v2;
|
|
+ } u64;
|
|
+ struct {
|
|
+ s64 v1;
|
|
+ s64 v2;
|
|
+ } s64;
|
|
+ } d;
|
|
+ char str[TABLE_STR_MAX];
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Table Row
|
|
+ */
|
|
+struct table_row {
|
|
+ struct list list;
|
|
+ int entry_count;
|
|
+ struct table_entry *entries;
|
|
+ int marked;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Table Mark Key
|
|
+ */
|
|
+struct table_mark_key {
|
|
+ struct list list;
|
|
+ char str[TABLE_STR_MAX];
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Table
|
|
+ */
|
|
+struct table {
|
|
+ struct list row_list;
|
|
+ int col_cnt;
|
|
+ struct table_col **col_vec;
|
|
+ struct table_col *col_selected;
|
|
+ struct table_row *row_last;
|
|
+ int row_cnt;
|
|
+ int row_cnt_marked;
|
|
+ int row_cnt_extra;
|
|
+ int row_nr_begin;
|
|
+ int row_nr_select;
|
|
+ int ready;
|
|
+ struct list mark_key_list;
|
|
+ unsigned int mark_keys_cnt;
|
|
+ int attr_sorted_table;
|
|
+ int attr_first_bold;
|
|
+ int attr_with_units;
|
|
+ int mode_sort_inverse;
|
|
+ int mode_select;
|
|
+ int mode_hide_unmarked;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Return if we are in select mode
|
|
+ */
|
|
+static inline int table_mode_select(struct table *t)
|
|
+{
|
|
+ return t->mode_select;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Table croll units
|
|
+ */
|
|
+enum table_scroll_unit {
|
|
+ TABLE_SCROLL_LINE,
|
|
+ TABLE_SCROLL_PAGE,
|
|
+ TABLE_SCROLL_LAST,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Prototypes
|
|
+ */
|
|
+extern struct table *table_new(int extra_rows, int sorted, int first_bold,
|
|
+ int with_units);
|
|
+extern void table_reset(struct table *t);
|
|
+extern void table_rebuild(struct table *t);
|
|
+extern void table_finish(struct table *t);
|
|
+extern void table_print(struct table *t);
|
|
+extern void table_process_input(struct table *t, int c);
|
|
+
|
|
+extern void table_col_unit_next(struct table *t, char hotkey);
|
|
+extern void table_col_unit_prev(struct table *t, char hotkey);
|
|
+extern int table_col_unit_set(struct table *t, char hotkey, const char *unit);
|
|
+extern void table_col_add(struct table *t, struct table_col *col);
|
|
+extern int table_col_select(struct table *t, char hotkey);
|
|
+extern void table_col_select_next(struct table *t);
|
|
+extern void table_col_select_prev(struct table *t);
|
|
+extern void table_col_enable_toggle(struct table *t, char hotkey);
|
|
+
|
|
+extern void table_row_del_all(struct table *t);
|
|
+extern void table_row_add(struct table *t, struct table_row *row);
|
|
+extern void table_row_mark(struct table *t, struct table_row *row);
|
|
+extern void table_row_mark_del_all(struct table *t);
|
|
+extern void table_row_mark_toggle(struct table *t, struct table_row *row);
|
|
+extern void table_row_mark_toggle_by_key(struct table *t, const char *mark_key);
|
|
+extern void table_row_select_down(struct table *t, enum table_scroll_unit unit);
|
|
+extern void table_row_select_up(struct table *t, enum table_scroll_unit unit);
|
|
+extern void table_row_select_key_get(struct table *t, char str[TABLE_STR_MAX]);
|
|
+extern struct table_row *table_row_alloc(struct table *t);
|
|
+
|
|
+extern void table_scroll_down(struct table *t, enum table_scroll_unit unit);
|
|
+extern void table_scroll_up(struct table *t, enum table_scroll_unit unit);
|
|
+
|
|
+/*
|
|
+ * Entry add functions
|
|
+ */
|
|
+static inline void table_row_entry_u64_add(struct table_row *table_row,
|
|
+ struct table_col *table_col,
|
|
+ u64 value)
|
|
+{
|
|
+ table_row->entries[table_col->p->col_nr].d.u64.v1 = value;
|
|
+}
|
|
+
|
|
+static inline void table_row_entry_s64_add(struct table_row *table_row,
|
|
+ struct table_col *table_col,
|
|
+ s64 value)
|
|
+{
|
|
+ table_row->entries[table_col->p->col_nr].d.s64.v1 = value;
|
|
+}
|
|
+
|
|
+static inline void table_row_entry_u64_add_pair(struct table_row *table_row,
|
|
+ struct table_col *table_col,
|
|
+ u64 value1, u64 value2)
|
|
+{
|
|
+ table_row->entries[table_col->p->col_nr].d.u64.v1 = value1;
|
|
+ table_row->entries[table_col->p->col_nr].d.u64.v2 = value2;
|
|
+}
|
|
+
|
|
+static inline void table_row_entry_str_add(struct table_row *table_row,
|
|
+ struct table_col *table_col,
|
|
+ const char *str)
|
|
+{
|
|
+ assert(strlen(str) < TABLE_STR_MAX);
|
|
+ strcpy(table_row->entries[table_col->p->col_nr].str, str);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Interate over all mark keys
|
|
+ */
|
|
+#define table_iterate_mark_keys(t, key) \
|
|
+ list_iterate(key, &t->mark_key_list, list)
|
|
+
|
|
+#endif /* TABLE_H */
|
|
diff --git a/hyptop/table_col_unit.c b/hyptop/table_col_unit.c
|
|
new file mode 100644
|
|
index 0000000..a8f5851
|
|
--- /dev/null
|
|
+++ b/hyptop/table_col_unit.c
|
|
@@ -0,0 +1,369 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Table unit module: Provide different units for data
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <stdio.h>
|
|
+#include "table.h"
|
|
+
|
|
+#define L_VISUAL_ROW_CNT 45
|
|
+#define L_COL_FMT_STR_0 "%.0lf"
|
|
+#define L_COL_FMT_STR_2 "%.2lf"
|
|
+
|
|
+/*
|
|
+ * Helper: Divide value and format it
|
|
+ */
|
|
+static int l_unit_raw_div(struct table_col *col, struct table_entry *e,
|
|
+ unsigned int divisor, const char *fmt_str)
|
|
+{
|
|
+ double v1;
|
|
+
|
|
+ switch (col->type) {
|
|
+ case TABLE_COL_TYPE_U64:
|
|
+ v1 = ((double) e->d.u64.v1) / divisor;
|
|
+ break;
|
|
+ case TABLE_COL_TYPE_S64:
|
|
+ v1 = ((double) e->d.s64.v1) / divisor;
|
|
+ break;
|
|
+ default:
|
|
+ assert(0);
|
|
+ }
|
|
+ return snprintf(e->str, sizeof(e->str), fmt_str, v1);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Helper: Format value as is
|
|
+ */
|
|
+static int l_unit_raw(struct table_col *col, struct table_entry *e)
|
|
+{
|
|
+ switch (col->type) {
|
|
+ case TABLE_COL_TYPE_U64:
|
|
+ return snprintf(e->str, sizeof(e->str), "%llu", e->d.u64.v1);
|
|
+ case TABLE_COL_TYPE_S64:
|
|
+ return snprintf(e->str, sizeof(e->str), "%lld", e->d.s64.v1);
|
|
+ default:
|
|
+ assert(0);
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Format: String
|
|
+ */
|
|
+static int l_str(struct table_col *col, struct table_entry *e)
|
|
+{
|
|
+ (void) col;
|
|
+ return strlen(e->str);
|
|
+}
|
|
+
|
|
+struct table_col_unit table_col_unit_str = {
|
|
+ .fn = l_str,
|
|
+ .hotkey = 'S',
|
|
+ .str = "str",
|
|
+ .desc = "String",
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Format: Count
|
|
+ */
|
|
+static int l_unit_cnt(struct table_col *col, struct table_entry *e)
|
|
+{
|
|
+ return l_unit_raw(col, e);
|
|
+}
|
|
+
|
|
+struct table_col_unit table_col_unit_cnt = {
|
|
+ .fn = l_unit_cnt,
|
|
+ .hotkey = '#',
|
|
+ .str = "#",
|
|
+ .desc = "Count",
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Format: Kibibytes
|
|
+ */
|
|
+static int l_unit_kib(struct table_col *col, struct table_entry *e)
|
|
+{
|
|
+ return l_unit_raw(col, e);
|
|
+}
|
|
+
|
|
+struct table_col_unit table_col_unit_kib = {
|
|
+ .fn = l_unit_kib,
|
|
+ .hotkey = 'k',
|
|
+ .str = "KiB",
|
|
+ .desc = "Kibibyte (1.024 bytes)",
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Format: Mebibytes
|
|
+ */
|
|
+static int l_unit_mib(struct table_col *col, struct table_entry *e)
|
|
+{
|
|
+ return l_unit_raw_div(col, e, 1024, L_COL_FMT_STR_2);
|
|
+}
|
|
+
|
|
+struct table_col_unit table_col_unit_mib = {
|
|
+ .fn = l_unit_mib,
|
|
+ .hotkey = 'M',
|
|
+ .str = "MiB",
|
|
+ .desc = "Mebibyte (1.048.576 bytes)",
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Format: Gibibytes
|
|
+ */
|
|
+static int l_unit_gib(struct table_col *col, struct table_entry *e)
|
|
+{
|
|
+ return l_unit_raw_div(col, e, 1024 * 1024, L_COL_FMT_STR_2);
|
|
+}
|
|
+
|
|
+struct table_col_unit table_col_unit_gib = {
|
|
+ .fn = l_unit_gib,
|
|
+ .hotkey = 'g',
|
|
+ .str = "GiB",
|
|
+ .desc = "Gibibyte (1.073.741.824 bytes)",
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Format: Microseconds
|
|
+ */
|
|
+static int l_unit_us(struct table_col *col, struct table_entry *e)
|
|
+{
|
|
+ return l_unit_raw(col, e);
|
|
+}
|
|
+
|
|
+struct table_col_unit table_col_unit_us = {
|
|
+ .fn = l_unit_us,
|
|
+ .hotkey = 'u',
|
|
+ .str = "us",
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Format: Milliseconds
|
|
+ */
|
|
+static int l_unit_ms(struct table_col *col, struct table_entry *e)
|
|
+{
|
|
+ return l_unit_raw_div(col, e, 1000, L_COL_FMT_STR_2);
|
|
+}
|
|
+
|
|
+struct table_col_unit table_col_unit_ms = {
|
|
+ .fn = l_unit_ms,
|
|
+ .hotkey = 'm',
|
|
+ .str = "ms",
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Format: Percent (Hundreds)
|
|
+ */
|
|
+static int l_unit_perc(struct table_col *col, struct table_entry *e)
|
|
+{
|
|
+ return l_unit_raw_div(col, e, 10000, L_COL_FMT_STR_2);
|
|
+}
|
|
+
|
|
+struct table_col_unit table_col_unit_perc = {
|
|
+ .fn = l_unit_perc,
|
|
+ .hotkey = '%',
|
|
+ .str = "%",
|
|
+ .desc = "Percent",
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Format: Seconds
|
|
+ */
|
|
+static int l_unit_s(struct table_col *col, struct table_entry *e)
|
|
+{
|
|
+ return l_unit_raw_div(col, e, 1000000, L_COL_FMT_STR_2);
|
|
+}
|
|
+
|
|
+struct table_col_unit table_col_unit_s = {
|
|
+ .fn = l_unit_s,
|
|
+ .hotkey = 's',
|
|
+ .str = "s",
|
|
+ .desc = "Seconds",
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Format: Minutes
|
|
+ */
|
|
+static int l_unit_m(struct table_col *col, struct table_entry *e)
|
|
+{
|
|
+ return l_unit_raw_div(col, e, 1000000 * 60, L_COL_FMT_STR_0);
|
|
+}
|
|
+
|
|
+struct table_col_unit table_col_unit_m = {
|
|
+ .fn = l_unit_m,
|
|
+ .hotkey = 'm',
|
|
+ .str = "m",
|
|
+ .desc = "Minutes",
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Format: Hours:Minutes
|
|
+ */
|
|
+static int l_unit_hm_u64(char *str, u64 v1, int negative)
|
|
+{
|
|
+ u64 time_tmp, time_h, time_m;
|
|
+
|
|
+ time_tmp = v1 / (1000000 * 60);
|
|
+ time_h = time_tmp / 60;
|
|
+ time_m = time_tmp - time_h * 60;
|
|
+
|
|
+ if (negative)
|
|
+ return sprintf(str, "-%llu:%02llu", time_h, time_m);
|
|
+ else
|
|
+ return sprintf(str, "%llu:%02llu", time_h, time_m);
|
|
+}
|
|
+
|
|
+static int l_unit_hm(struct table_col *col, struct table_entry *e)
|
|
+{
|
|
+ switch (col->type) {
|
|
+ case TABLE_COL_TYPE_U64:
|
|
+ return l_unit_hm_u64(e->str, e->d.u64.v1, 0);
|
|
+ case TABLE_COL_TYPE_S64:
|
|
+ if (e->d.s64.v1 < 0)
|
|
+ return l_unit_hm_u64(e->str, -e->d.s64.v1, 1);
|
|
+ else
|
|
+ return l_unit_hm_u64(e->str, e->d.s64.v1, 0);
|
|
+ default:
|
|
+ assert(0);
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+struct table_col_unit table_col_unit_hm = {
|
|
+ .fn = l_unit_hm,
|
|
+ .hotkey = 'H',
|
|
+ .str = "hm",
|
|
+ .desc = "Hours:Minutes",
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Format: Days:Hours:Minutes
|
|
+ */
|
|
+static int l_unit_dhm_u64(char *str, u64 v1, int negative)
|
|
+{
|
|
+ u64 time_tmp, time_d, time_h, time_m;
|
|
+
|
|
+ time_tmp = v1 / (1000000 * 60);
|
|
+ time_d = time_tmp / (60 * 24);
|
|
+ time_h = time_tmp / 60 - time_d * 24;
|
|
+ time_m = time_tmp - time_h * 60 - time_d * 60 * 24;
|
|
+
|
|
+ if (negative)
|
|
+ return sprintf(str, "-%llu:%02llu:%02llu", time_d, time_h,
|
|
+ time_m);
|
|
+ else
|
|
+ return sprintf(str, "%llu:%02llu:%02llu", time_d, time_h,
|
|
+ time_m);
|
|
+}
|
|
+
|
|
+static int l_unit_dhm(struct table_col *col, struct table_entry *e)
|
|
+{
|
|
+ switch (col->type) {
|
|
+ case TABLE_COL_TYPE_U64:
|
|
+ return l_unit_dhm_u64(e->str, e->d.u64.v1, 0);
|
|
+ case TABLE_COL_TYPE_S64:
|
|
+ if (e->d.s64.v1 < 0)
|
|
+ return l_unit_dhm_u64(e->str, -e->d.s64.v1, 1);
|
|
+ else
|
|
+ return l_unit_dhm_u64(e->str, e->d.s64.v1, 0);
|
|
+ default:
|
|
+ assert(0);
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+struct table_col_unit table_col_unit_dhm = {
|
|
+ .fn = l_unit_dhm,
|
|
+ .hotkey = 'D',
|
|
+ .str = "dhm",
|
|
+ .desc = "Days:Hours:Minutes",
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Format: Visualization with bar chart
|
|
+ */
|
|
+static int l_unit_vis(struct table_col *col, struct table_entry *e)
|
|
+{
|
|
+ double val1_perc, val2_perc;
|
|
+ int val1_nr, val2_nr;
|
|
+ int i;
|
|
+
|
|
+ assert(col->type == TABLE_COL_TYPE_U64);
|
|
+
|
|
+ sprintf(e->str, "|");
|
|
+ val1_perc = e->d.u64.v1;
|
|
+ val1_perc /= 1000000;
|
|
+ val2_perc = e->d.u64.v2;
|
|
+ val2_perc /= 1000000;
|
|
+ val1_nr = (val1_perc * L_VISUAL_ROW_CNT) + 0.5;
|
|
+ val2_nr = (val2_perc * L_VISUAL_ROW_CNT) + 0.5;
|
|
+
|
|
+ if (val1_nr > L_VISUAL_ROW_CNT)
|
|
+ val1_nr = L_VISUAL_ROW_CNT;
|
|
+ if (val1_nr + val2_nr > L_VISUAL_ROW_CNT)
|
|
+ val2_nr = L_VISUAL_ROW_CNT - val1_nr;
|
|
+
|
|
+ for (i = 0; i < val1_nr; i++)
|
|
+ strcat(e->str, "#");
|
|
+ for (i = 0; i < val2_nr; i++)
|
|
+ strcat(e->str, "-");
|
|
+ for (i = 0; i < L_VISUAL_ROW_CNT - val1_nr - val2_nr; i++)
|
|
+ strcat(e->str, " ");
|
|
+ strcat(e->str, "|");
|
|
+
|
|
+ return strlen(e->str);
|
|
+}
|
|
+
|
|
+struct table_col_unit table_col_unit_vis = {
|
|
+ .fn = l_unit_vis,
|
|
+ .hotkey = 'v',
|
|
+ .str = "vis",
|
|
+ .desc = "Visualization with bar chart",
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Families
|
|
+ */
|
|
+struct table_col_unit *table_col_unit_fam_str[] = {
|
|
+ &table_col_unit_str,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+struct table_col_unit *table_col_unit_fam_cnt[] = {
|
|
+ &table_col_unit_cnt,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+struct table_col_unit *table_col_unit_fam_mem[] = {
|
|
+ &table_col_unit_kib,
|
|
+ &table_col_unit_mib,
|
|
+ &table_col_unit_gib,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+struct table_col_unit *table_col_unit_fam_time_diff[] = {
|
|
+ &table_col_unit_us,
|
|
+ &table_col_unit_ms,
|
|
+ &table_col_unit_perc,
|
|
+ &table_col_unit_s,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+struct table_col_unit *table_col_unit_fam_time[] = {
|
|
+ &table_col_unit_us,
|
|
+ &table_col_unit_ms,
|
|
+ &table_col_unit_s,
|
|
+ &table_col_unit_m,
|
|
+ &table_col_unit_hm,
|
|
+ &table_col_unit_dhm,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+struct table_col_unit *table_col_unit_fam_vis[] = {
|
|
+ &table_col_unit_vis,
|
|
+ NULL,
|
|
+};
|
|
diff --git a/hyptop/tbox.c b/hyptop/tbox.c
|
|
new file mode 100644
|
|
index 0000000..82eda0c
|
|
--- /dev/null
|
|
+++ b/hyptop/tbox.c
|
|
@@ -0,0 +1,240 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Text box: Provide scrollable text window under curses.
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <assert.h>
|
|
+#include <ncurses.h>
|
|
+#include "hyptop.h"
|
|
+#include "tbox.h"
|
|
+#include "helper.h"
|
|
+
|
|
+/*
|
|
+ * Delete one line
|
|
+ */
|
|
+static void l_line_free(struct tbox_line *line)
|
|
+{
|
|
+ ht_free(line->str);
|
|
+ ht_free(line);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Delete all lines
|
|
+ */
|
|
+void tbox_line_del_all(struct tbox *tb)
|
|
+{
|
|
+ struct tbox_line *line, *tmp;
|
|
+
|
|
+ list_iterate_safe(line, &tb->line_list, list, tmp) {
|
|
+ list_del(&line->list);
|
|
+ l_line_free(line);
|
|
+ }
|
|
+ tb->tbox_ready = 0;
|
|
+ tb->line_cnt = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Finish text box after all lines have been added
|
|
+ */
|
|
+void tbox_finish(struct tbox *tb)
|
|
+{
|
|
+ tb->tbox_ready = 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add one line to text box
|
|
+ */
|
|
+void tbox_line_add(struct tbox *tb, const char *str)
|
|
+{
|
|
+ struct tbox_line *line;
|
|
+
|
|
+ if (strlen(str) > TBOX_MAX_STR)
|
|
+ assert(0);
|
|
+ line = ht_zalloc(sizeof(*line));
|
|
+ line->str = ht_strdup(str);
|
|
+ if (list_is_empty(&tb->line_list))
|
|
+ list_add(&line->list, &tb->line_list);
|
|
+ else
|
|
+ list_add(&line->list, &tb->last_line->list);
|
|
+ tb->last_line = line;
|
|
+ tb->line_cnt++;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Adjust values, if we scrolled out of range
|
|
+ */
|
|
+static void l_adjust_values(struct tbox *tb)
|
|
+{
|
|
+ if (tb->line_cnt - tb->line_start < g.c.row_cnt)
|
|
+ tb->line_start = MAX(tb->line_cnt - g.c.row_cnt, 0);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Scroll text box down
|
|
+ */
|
|
+void tbox_scroll_down(struct tbox *tb, enum tbox_scroll_unit unit)
|
|
+{
|
|
+ switch (unit) {
|
|
+ case TBOX_SCROLL_LINE:
|
|
+ tb->line_start++;
|
|
+ break;
|
|
+ case TBOX_SCROLL_PAGE:
|
|
+ tb->line_start += (g.c.row_cnt - 2);
|
|
+ break;
|
|
+ case TBOX_SCROLL_LAST:
|
|
+ tb->line_start = tb->line_cnt;
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Scroll text box up
|
|
+ */
|
|
+void tbox_scroll_up(struct tbox *tb, enum tbox_scroll_unit unit)
|
|
+{
|
|
+ switch (unit) {
|
|
+ case TBOX_SCROLL_LINE:
|
|
+ tb->line_start = MAX(tb->line_start - 1, 0);
|
|
+ break;
|
|
+ case TBOX_SCROLL_PAGE:
|
|
+ tb->line_start = MAX(tb->line_start - (g.c.row_cnt - 2), 0);
|
|
+ break;
|
|
+ case TBOX_SCROLL_LAST:
|
|
+ tb->line_start = 0;
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Resize text box
|
|
+ */
|
|
+void tbox_term_resize(struct tbox *tb)
|
|
+{
|
|
+ l_adjust_values(tb);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Toggle bold curses format attribute
|
|
+ */
|
|
+static void l_bold_toggle(void)
|
|
+{
|
|
+ static int bold_on;
|
|
+
|
|
+ if (bold_on) {
|
|
+ ht_bold_off();
|
|
+ bold_on = 0;
|
|
+ } else {
|
|
+ ht_bold_on();
|
|
+ bold_on = 1;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Toggle underline curses format attribute
|
|
+ */
|
|
+static void l_underline_toggle(void)
|
|
+{
|
|
+ static int underline_on;
|
|
+
|
|
+ if (underline_on) {
|
|
+ ht_underline_off();
|
|
+ underline_on = 0;
|
|
+ } else {
|
|
+ ht_underline_on();
|
|
+ underline_on = 1;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print one line with attributes (bold and underline)
|
|
+ */
|
|
+void l_print_line(const char *line)
|
|
+{
|
|
+ char line_cpy[TBOX_MAX_STR + 1];
|
|
+ char *ptr_old, *ptr;
|
|
+
|
|
+ strncpy(line_cpy, line, sizeof(line_cpy));
|
|
+ ptr_old = ptr = line_cpy;
|
|
+ do {
|
|
+ ptr = strchr(ptr, '\\');
|
|
+ if (ptr) {
|
|
+ *ptr = 0;
|
|
+ hyptop_printf("%s", ptr_old);
|
|
+ switch (ptr[1]) {
|
|
+ case 'B':
|
|
+ l_bold_toggle();
|
|
+ break;
|
|
+ case 'U':
|
|
+ l_underline_toggle();
|
|
+ break;
|
|
+ }
|
|
+ ptr += 2;
|
|
+ ptr_old = ptr;
|
|
+ } else {
|
|
+ hyptop_printf("%s", ptr_old);
|
|
+ return;
|
|
+ }
|
|
+ } while (*ptr);
|
|
+}
|
|
+
|
|
+#ifdef WITH_SCROLL_BAR
|
|
+static int l_can_scroll_down(struct tbox *tb)
|
|
+{
|
|
+ return (tb->line_cnt - tb->line_start > g.c.row_cnt);
|
|
+}
|
|
+
|
|
+static int l_can_scroll_up(struct tbox *tb)
|
|
+{
|
|
+ return (tb->line_start > 0);
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * Print text box to screen
|
|
+ */
|
|
+void tbox_print(struct tbox *tb)
|
|
+{
|
|
+ int line_nr = 0, first = 1;
|
|
+ struct tbox_line *line;
|
|
+
|
|
+ if (!tb->tbox_ready)
|
|
+ return;
|
|
+
|
|
+ l_adjust_values(tb);
|
|
+ list_iterate(line, &tb->line_list, list) {
|
|
+ if (line_nr < tb->line_start) {
|
|
+ line_nr++;
|
|
+ continue;
|
|
+ }
|
|
+ /* Have we printed the whole visible screen ? */
|
|
+ if (line_nr - tb->line_start >= g.c.row_cnt)
|
|
+ break;
|
|
+ if (first)
|
|
+ first = 0;
|
|
+ else
|
|
+ hyptop_print_nl();
|
|
+ l_print_line(line->str);
|
|
+ line_nr++;
|
|
+ }
|
|
+#ifdef WITH_SCROLL_BAR
|
|
+ ht_print_scroll_bar(tb->line_cnt, tb->line_start, 0,
|
|
+ 0, l_can_scroll_up(tb), l_can_scroll_down(tb),
|
|
+ 0);
|
|
+#endif
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Create new text box
|
|
+ */
|
|
+struct tbox *tbox_new(void)
|
|
+{
|
|
+ struct tbox *tb;
|
|
+
|
|
+ tb = ht_zalloc(sizeof(*tb));
|
|
+ list_init(&tb->line_list);
|
|
+ return tb;
|
|
+}
|
|
diff --git a/hyptop/tbox.h b/hyptop/tbox.h
|
|
new file mode 100644
|
|
index 0000000..60397f3
|
|
--- /dev/null
|
|
+++ b/hyptop/tbox.h
|
|
@@ -0,0 +1,52 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Text box: Provide scrollable text window under curses.
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef TBOX_H
|
|
+#define TBOX_H
|
|
+
|
|
+#include "list.h"
|
|
+
|
|
+#define TBOX_MAX_STR 120
|
|
+
|
|
+struct tbox_line {
|
|
+ struct list list;
|
|
+ char *str;
|
|
+};
|
|
+
|
|
+struct tbox {
|
|
+ struct list line_list;
|
|
+ int line_cnt;
|
|
+ int line_start;
|
|
+ int tbox_ready;
|
|
+ struct tbox_line *last_line;
|
|
+};
|
|
+
|
|
+enum tbox_scroll_unit {
|
|
+ TBOX_SCROLL_LINE,
|
|
+ TBOX_SCROLL_PAGE,
|
|
+ TBOX_SCROLL_LAST,
|
|
+};
|
|
+
|
|
+struct tbox *tbox_new(void);
|
|
+void tbox_line_del_all(struct tbox *tb);
|
|
+void tbox_line_add(struct tbox *tb, const char *str);
|
|
+void tbox_finish(struct tbox *tb);
|
|
+void tbox_scroll_down(struct tbox *tb, enum tbox_scroll_unit);
|
|
+void tbox_scroll_up(struct tbox *tb, enum tbox_scroll_unit);
|
|
+void tbox_term_resize(struct tbox *tb);
|
|
+void tbox_print(struct tbox *tb);
|
|
+
|
|
+#define tbox_printf(tb, x...) \
|
|
+{ \
|
|
+ char line[TBOX_MAX_STR + 1]; \
|
|
+ sprintf(line, x); \
|
|
+ tbox_line_add(tb, line); \
|
|
+}
|
|
+
|
|
+#endif /* TBOX_H */
|
|
diff --git a/hyptop/win_cpu_types.c b/hyptop/win_cpu_types.c
|
|
new file mode 100644
|
|
index 0000000..7d3dff2
|
|
--- /dev/null
|
|
+++ b/hyptop/win_cpu_types.c
|
|
@@ -0,0 +1,248 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Window "cpu_types": Select CPU types used for CPU data calculation.
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include "helper.h"
|
|
+#include "hyptop.h"
|
|
+#include "table.h"
|
|
+#include "win_cpu_types.h"
|
|
+#include "sd.h"
|
|
+#include "nav_desc.h"
|
|
+
|
|
+/*
|
|
+ * Globals for cpu_types window
|
|
+ */
|
|
+static struct table_col l_col_key = TABLE_COL_STR_LEFT('k', "k");
|
|
+static struct table_col l_col_select = TABLE_COL_STR_LEFT('s', "s");
|
|
+static struct table_col l_col_id = TABLE_COL_STR_LEFT('i', "id");
|
|
+static struct table_col l_col_desc = TABLE_COL_STR_LEFT('d', "description");
|
|
+
|
|
+/*
|
|
+ * Online help text for cpu_types window
|
|
+ */
|
|
+static const char l_help_str[] =
|
|
+"In the \"cpu_types\" window you can select the CPU types that are used for\n"
|
|
+"calculating CPU data. Toggle the selection of types either by pressing the\n"
|
|
+"corresponding hotkey or by selecting them in select mode using the SPACE bar.\n"
|
|
+"\n"
|
|
+"The table of the \"cpu_types\" window has the following columns:\n"
|
|
+" - K : Hotkey of CPU type\n"
|
|
+" - S : Shows if CPU type is selected\n"
|
|
+" - ID : Name of CPU type\n"
|
|
+" - DESC: Description of CPU type\n";
|
|
+
|
|
+/*
|
|
+ * Description of Navigation Keys (used for help window)
|
|
+ */
|
|
+static struct nav_desc *l_nav_desc_normal_vec[] = {
|
|
+ &nav_desc_select_mode_enter,
|
|
+ &nav_desc_marks_clear,
|
|
+ &nav_desc_win_leave_cpu_types,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+static struct nav_desc *l_nav_desc_select_vec[] = {
|
|
+ &nav_desc_select_mode_leave,
|
|
+ &nav_desc_mark_toggle,
|
|
+ &nav_desc_win_leave_cpu_types_fast,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+static struct nav_desc *l_nav_desc_general_vec[] = {
|
|
+ &nav_desc_toggle_mark_hotkey,
|
|
+ &nav_desc_scroll_up_line,
|
|
+ &nav_desc_scroll_down_line,
|
|
+ &nav_desc_scroll_up_page,
|
|
+ &nav_desc_scroll_down_page,
|
|
+ &nav_desc_scroll_up_head,
|
|
+ &nav_desc_scroll_down_tail,
|
|
+ &nav_desc_mark_toggle_view,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Add a CPU type to the table
|
|
+ */
|
|
+static void l_add_cpu_type(struct win_cpu_types *win_cpu_types,
|
|
+ struct sd_cpu_type *cpu_type)
|
|
+{
|
|
+ char char_str[2], select_str[2];
|
|
+ struct table_row *table_row;
|
|
+
|
|
+ if (sd_cpu_type_selected(cpu_type))
|
|
+ sprintf(select_str, "*");
|
|
+ else
|
|
+ sprintf(select_str, " ");
|
|
+ sprintf(char_str, "%c", cpu_type->hotkey);
|
|
+
|
|
+ table_row = table_row_alloc(win_cpu_types->t);
|
|
+ table_row_entry_str_add(table_row, &l_col_select, select_str);
|
|
+ table_row_entry_str_add(table_row, &l_col_key, char_str);
|
|
+ table_row_entry_str_add(table_row, &l_col_id, cpu_type->id);
|
|
+ table_row_entry_str_add(table_row, &l_col_desc, cpu_type->desc);
|
|
+ table_row_add(win_cpu_types->t, table_row);
|
|
+ if (sd_cpu_type_selected(cpu_type))
|
|
+ table_row_mark_toggle(win_cpu_types->t, table_row);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Fill all available CPU types into table
|
|
+ */
|
|
+static void l_table_create(struct win_cpu_types *win_cpu_types)
|
|
+{
|
|
+ struct sd_cpu_type *cpu_type;
|
|
+ unsigned int i;
|
|
+
|
|
+ table_row_del_all(win_cpu_types->t);
|
|
+ table_row_mark_del_all(win_cpu_types->t);
|
|
+ sd_cpu_type_iterate(cpu_type, i)
|
|
+ l_add_cpu_type(win_cpu_types, cpu_type);
|
|
+ table_finish(win_cpu_types->t);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Toggle the cpu type specified by "key" in the system data module
|
|
+ */
|
|
+static void l_toggle_cpu_type(char key)
|
|
+{
|
|
+ struct sd_cpu_type *cpu_type;
|
|
+ unsigned int i;
|
|
+
|
|
+ sd_cpu_type_iterate(cpu_type, i) {
|
|
+ if (key == cpu_type->hotkey) {
|
|
+ sd_cpu_type_select_toggle(cpu_type);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Process input for selection with SPACE key
|
|
+ */
|
|
+static void l_process_input_select_space(struct win_cpu_types *win_cpu_types)
|
|
+{
|
|
+ char cpu_type_key[TABLE_STR_MAX];
|
|
+
|
|
+ if (table_mode_select(win_cpu_types->t)) {
|
|
+ table_row_select_key_get(win_cpu_types->t, cpu_type_key);
|
|
+ l_toggle_cpu_type(cpu_type_key[0]);
|
|
+ } else {
|
|
+ struct table_mark_key *key;
|
|
+
|
|
+ table_iterate_mark_keys(win_cpu_types->t, key)
|
|
+ l_toggle_cpu_type(key->str[0]);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Process input for selection with hotkey
|
|
+ */
|
|
+static void l_process_input_select_key(struct win_cpu_types *win_cpu_types,
|
|
+ int c)
|
|
+{
|
|
+ char cpu_type_key[TABLE_STR_MAX];
|
|
+
|
|
+ sprintf(cpu_type_key, "%c", c);
|
|
+ table_row_mark_toggle_by_key(win_cpu_types->t, cpu_type_key);
|
|
+ l_toggle_cpu_type(cpu_type_key[0]);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Process input and switch window if necessary
|
|
+ */
|
|
+static enum hyptop_win_action l_process_input(struct hyptop_win *win, int c)
|
|
+{
|
|
+ struct win_cpu_types *win_cpu_types = (struct win_cpu_types *) win;
|
|
+
|
|
+ switch (c) {
|
|
+ case 't':
|
|
+ case 'q':
|
|
+ return win_back();
|
|
+ case KEY_RETURN:
|
|
+ case KEY_ENTER:
|
|
+ case 'h':
|
|
+ case KEY_LEFT:
|
|
+ if (!table_mode_select(win_cpu_types->t))
|
|
+ return win_back();
|
|
+ break;
|
|
+ case '?':
|
|
+ return win_switch(win_cpu_types->win_help);
|
|
+ case ' ':
|
|
+ l_process_input_select_space(win_cpu_types);
|
|
+ break;
|
|
+ case ERR:
|
|
+ return WIN_KEEP;
|
|
+ default:
|
|
+ l_process_input_select_key(win_cpu_types, c);
|
|
+ break;
|
|
+ }
|
|
+ table_process_input(win_cpu_types->t, c);
|
|
+ hyptop_update_term();
|
|
+ return WIN_KEEP;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Event loop: We stay in hyptop_process_input() until fields menu
|
|
+ * is left.
|
|
+ */
|
|
+static void l_run(struct hyptop_win *win)
|
|
+{
|
|
+ struct win_cpu_types *win_cpu_types = (struct win_cpu_types *) win;
|
|
+
|
|
+ table_reset(win_cpu_types->t);
|
|
+ while (1) {
|
|
+ hyptop_update_term();
|
|
+ if (hyptop_process_input() == WIN_SWITCH)
|
|
+ return;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Create table and print it to screen
|
|
+ */
|
|
+static void l_update_term(struct hyptop_win *win)
|
|
+{
|
|
+ struct win_cpu_types *win_cpu_types = (struct win_cpu_types *) win;
|
|
+
|
|
+ l_table_create(win_cpu_types);
|
|
+ hyptop_printf("Select Processor Types");
|
|
+ ht_print_help_icon();
|
|
+ hyptop_print_nl();
|
|
+
|
|
+ table_print(win_cpu_types->t);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Create new cpu_types window
|
|
+ */
|
|
+struct hyptop_win *win_cpu_types_new(void)
|
|
+{
|
|
+ struct win_cpu_types *win_cpu_types;
|
|
+
|
|
+ win_cpu_types = ht_zalloc(sizeof(*win_cpu_types));
|
|
+
|
|
+ win_cpu_types->win.process_input = l_process_input;
|
|
+ win_cpu_types->win.update_term = l_update_term;
|
|
+ win_cpu_types->win.run = l_run;
|
|
+ win_cpu_types->win.desc = l_help_str;
|
|
+ win_cpu_types->win.desc_normal_vec = l_nav_desc_normal_vec;
|
|
+ win_cpu_types->win.desc_select_vec = l_nav_desc_select_vec;
|
|
+ win_cpu_types->win.desc_general_vec = l_nav_desc_general_vec;
|
|
+ win_cpu_types->win.id = "cpu_types";
|
|
+
|
|
+ win_cpu_types->t = table_new(1, 0, 0, 0);
|
|
+ table_col_add(win_cpu_types->t, &l_col_key);
|
|
+ table_col_add(win_cpu_types->t, &l_col_select);
|
|
+ table_col_add(win_cpu_types->t, &l_col_id);
|
|
+ table_col_add(win_cpu_types->t, &l_col_desc);
|
|
+
|
|
+ win_cpu_types->win_help =
|
|
+ win_help_new((struct hyptop_win *) win_cpu_types);
|
|
+ l_table_create(win_cpu_types);
|
|
+ return (struct hyptop_win *) win_cpu_types;
|
|
+}
|
|
diff --git a/hyptop/win_cpu_types.h b/hyptop/win_cpu_types.h
|
|
new file mode 100644
|
|
index 0000000..b0f29a7
|
|
--- /dev/null
|
|
+++ b/hyptop/win_cpu_types.h
|
|
@@ -0,0 +1,26 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Window "cpu_types": Select CPU types used for CPU data calculation.
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef WIN_CPU_TYPES_H
|
|
+#define WIN_CPU_TYPES_H
|
|
+
|
|
+#include "hyptop.h"
|
|
+#include "table.h"
|
|
+#include "win_help.h"
|
|
+
|
|
+struct win_cpu_types {
|
|
+ struct hyptop_win win;
|
|
+ struct table *t;
|
|
+ int in_select;
|
|
+ struct hyptop_win *win_help;
|
|
+};
|
|
+
|
|
+extern struct hyptop_win *win_cpu_types_new(void);
|
|
+
|
|
+#endif /* WIN_CPU_TYPES_H */
|
|
diff --git a/hyptop/win_fields.c b/hyptop/win_fields.c
|
|
new file mode 100644
|
|
index 0000000..8d5e233
|
|
--- /dev/null
|
|
+++ b/hyptop/win_fields.c
|
|
@@ -0,0 +1,272 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Window "fields": Select fields dialog.
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include "helper.h"
|
|
+#include "hyptop.h"
|
|
+#include "table.h"
|
|
+#include "win_fields.h"
|
|
+
|
|
+
|
|
+/*
|
|
+ * Globals for fields window
|
|
+ */
|
|
+static struct table_col l_col_select = TABLE_COL_STR_LEFT('s', "s");
|
|
+static struct table_col l_col_id = TABLE_COL_STR_LEFT('i', "id");
|
|
+static struct table_col l_col_key = TABLE_COL_STR_LEFT('k', "k");
|
|
+static struct table_col l_col_unit = TABLE_COL_STR_LEFT('u', "unit");
|
|
+static struct table_col l_col_agg = TABLE_COL_STR_LEFT('a', "agg");
|
|
+static struct table_col l_col_desc = TABLE_COL_STR_LEFT('d', "description");
|
|
+
|
|
+/*
|
|
+ * Online help text for fields window
|
|
+ */
|
|
+static const char l_help_str[] =
|
|
+"In the \"fields\" window you can select fields and units. Toggle the selection\n"
|
|
+"of fields either by pressing the corresponding hotkey or by selecting them\n"
|
|
+"in select mode using the SPACE bar. The units can be changed by selecting a\n"
|
|
+"field in select mode and by pressing '+' or '-'.\n"
|
|
+"\n"
|
|
+"The table of the \"fields\" window has the following columns:\n"
|
|
+" - K : Hotkey of field\n"
|
|
+" - S : Shows if field is selected\n"
|
|
+" - ID : Name of field\n"
|
|
+" - UNIT: Current unit used for field\n"
|
|
+" - AGG : Aggregation used for last line of table\n"
|
|
+" - DESC: Description of field\n";
|
|
+
|
|
+/*
|
|
+ * Description of Navigation Keys (used for help window)
|
|
+ */
|
|
+static struct nav_desc *l_nav_desc_normal_vec[] = {
|
|
+ &nav_desc_select_mode_enter,
|
|
+ &nav_desc_marks_clear,
|
|
+ &nav_desc_win_leave_fields,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+static struct nav_desc *l_nav_desc_select_vec[] = {
|
|
+ &nav_desc_select_mode_leave,
|
|
+ &nav_desc_mark_toggle,
|
|
+ &nav_desc_row_unit_increase,
|
|
+ &nav_desc_row_unit_decrease,
|
|
+ &nav_desc_win_leave_fields_fast,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+static struct nav_desc *l_nav_desc_general_vec[] = {
|
|
+ &nav_desc_toggle_mark_hotkey,
|
|
+ &nav_desc_scroll_up_line,
|
|
+ &nav_desc_scroll_down_line,
|
|
+ &nav_desc_scroll_up_page,
|
|
+ &nav_desc_scroll_down_page,
|
|
+ &nav_desc_scroll_up_head,
|
|
+ &nav_desc_scroll_down_tail,
|
|
+ &nav_desc_mark_toggle_view,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Add a field that is the column of the reference table to the table
|
|
+ */
|
|
+static void l_add_field(struct win_fields *win_fields, struct table_col *col,
|
|
+ const char *desc)
|
|
+{
|
|
+ char char_str[2], select_str[2];
|
|
+ struct table_row *table_row;
|
|
+
|
|
+ if (table_col_enabled(col))
|
|
+ sprintf(select_str, "*");
|
|
+ else
|
|
+ sprintf(select_str, " ");
|
|
+ sprintf(char_str, "%c", table_col_hotkey(col));
|
|
+
|
|
+ table_row = table_row_alloc(win_fields->t);
|
|
+ table_row_entry_str_add(table_row, &l_col_select, select_str);
|
|
+ table_row_entry_str_add(table_row, &l_col_key, char_str);
|
|
+ table_row_entry_str_add(table_row, &l_col_id, table_col_head(col));
|
|
+ table_row_entry_str_add(table_row, &l_col_unit,
|
|
+ table_col_unit_str(col));
|
|
+ table_row_entry_str_add(table_row, &l_col_agg,
|
|
+ table_col_agg_str(col->agg));
|
|
+ table_row_entry_str_add(table_row, &l_col_desc, desc);
|
|
+ table_row_add(win_fields->t, table_row);
|
|
+
|
|
+ if (table_col_enabled(col))
|
|
+ table_row_mark_toggle(win_fields->t, table_row);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Fill all field information into table
|
|
+ */
|
|
+static void l_table_create(struct win_fields *win_fields)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ table_row_del_all(win_fields->t);
|
|
+ table_row_mark_del_all(win_fields->t);
|
|
+ for (i = 0; win_fields->col_vec[i]; i++) {
|
|
+ l_add_field(win_fields, win_fields->col_vec[i],
|
|
+ win_fields->col_desc_vec[i]);
|
|
+ }
|
|
+ table_finish(win_fields->t);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Process input for selection with SPACE key
|
|
+ */
|
|
+static void l_process_input_select_space(struct win_fields *win_fields)
|
|
+{
|
|
+ char field_key[TABLE_STR_MAX];
|
|
+
|
|
+ if (table_mode_select(win_fields->t)) {
|
|
+ table_row_select_key_get(win_fields->t, field_key);
|
|
+ table_col_enable_toggle(win_fields->table, field_key[0]);
|
|
+ } else {
|
|
+ struct table_mark_key *key;
|
|
+ /* switch off all fields in reference table */
|
|
+ table_iterate_mark_keys(win_fields->t, key)
|
|
+ table_col_enable_toggle(win_fields->table,
|
|
+ key->str[0]);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Process input for selection with hotkey
|
|
+ */
|
|
+static void l_process_input_select_key(struct win_fields *win_fields, int c)
|
|
+{
|
|
+ char field_key[TABLE_STR_MAX];
|
|
+
|
|
+ sprintf(field_key, "%c", c);
|
|
+ table_row_mark_toggle_by_key(win_fields->t, field_key);
|
|
+ table_col_enable_toggle(win_fields->table, field_key[0]);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Process input for unit selection
|
|
+ */
|
|
+static void l_process_input_units(struct win_fields *win_fields, int c)
|
|
+{
|
|
+ char field_key[TABLE_STR_MAX];
|
|
+
|
|
+ if (!table_mode_select(win_fields->t))
|
|
+ return;
|
|
+ table_row_select_key_get(win_fields->t, field_key);
|
|
+ if (c == '+')
|
|
+ table_col_unit_next(win_fields->table, field_key[0]);
|
|
+ else
|
|
+ table_col_unit_prev(win_fields->table, field_key[0]);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Process input and switch window if necessary
|
|
+ */
|
|
+static enum hyptop_win_action l_process_input(struct hyptop_win *win, int c)
|
|
+{
|
|
+ struct win_fields *win_fields = (struct win_fields *) win;
|
|
+
|
|
+ switch (c) {
|
|
+ case 'f':
|
|
+ case 'q':
|
|
+ return win_back();
|
|
+ case KEY_RETURN:
|
|
+ case KEY_ENTER:
|
|
+ case 'h':
|
|
+ case KEY_LEFT:
|
|
+ if (!table_mode_select(win_fields->t))
|
|
+ return win_back();
|
|
+ break;
|
|
+ case '?':
|
|
+ return win_switch(win_fields->win_help);
|
|
+ case ' ':
|
|
+ l_process_input_select_space(win_fields);
|
|
+ break;
|
|
+ case '+':
|
|
+ case '-':
|
|
+ l_process_input_units(win_fields, c);
|
|
+ break;
|
|
+ case ERR:
|
|
+ return WIN_KEEP;
|
|
+ default:
|
|
+ l_process_input_select_key(win_fields, c);
|
|
+ break;
|
|
+ }
|
|
+ table_process_input(win_fields->t, c);
|
|
+ hyptop_update_term();
|
|
+ return WIN_KEEP;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Event loop: We stay in hyptop_process_input() until fields menu
|
|
+ * is left.
|
|
+ */
|
|
+static void l_run(struct hyptop_win *win)
|
|
+{
|
|
+ struct win_fields *win_fields = (struct win_fields *) win;
|
|
+
|
|
+ table_reset(win_fields->t);
|
|
+ while (1) {
|
|
+ hyptop_update_term();
|
|
+ if (hyptop_process_input() == WIN_SWITCH)
|
|
+ return;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Create table and print it to screen
|
|
+ */
|
|
+static void l_update_term(struct hyptop_win *win)
|
|
+{
|
|
+ struct win_fields *win_fields = (struct win_fields *) win;
|
|
+
|
|
+ l_table_create(win_fields);
|
|
+ hyptop_printf("Select Fields and Units");
|
|
+ ht_print_help_icon();
|
|
+ hyptop_print_nl();
|
|
+ table_print(win_fields->t);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Create new fields window
|
|
+ *
|
|
+ * - t...........: Reference table
|
|
+ * - col_vec.....: Table column vector for fields
|
|
+ * - col_desc_vec: Vector with descriptions for fields
|
|
+ */
|
|
+struct hyptop_win *win_fields_new(struct table *t, struct table_col **col_vec,
|
|
+ char **col_desc_vec)
|
|
+{
|
|
+ struct win_fields *win_fields;
|
|
+
|
|
+ win_fields = ht_zalloc(sizeof(*win_fields));
|
|
+
|
|
+ win_fields->win.process_input = l_process_input;
|
|
+ win_fields->win.update_term = l_update_term;
|
|
+ win_fields->win.run = l_run;
|
|
+ win_fields->win.desc = l_help_str;
|
|
+ win_fields->win.desc_normal_vec = l_nav_desc_normal_vec;
|
|
+ win_fields->win.desc_select_vec = l_nav_desc_select_vec;
|
|
+ win_fields->win.desc_general_vec = l_nav_desc_general_vec;
|
|
+ win_fields->win.id = "fields";
|
|
+
|
|
+ win_fields->t = table_new(1, 0, 0, 0);
|
|
+ table_col_add(win_fields->t, &l_col_key);
|
|
+ table_col_add(win_fields->t, &l_col_select);
|
|
+ table_col_add(win_fields->t, &l_col_id);
|
|
+ table_col_add(win_fields->t, &l_col_unit);
|
|
+ table_col_add(win_fields->t, &l_col_agg);
|
|
+ table_col_add(win_fields->t, &l_col_desc);
|
|
+ win_fields->col_desc_vec = col_desc_vec;
|
|
+ win_fields->col_vec = col_vec;
|
|
+ win_fields->table = t;
|
|
+ win_fields->win_help = win_help_new((struct hyptop_win *) win_fields);
|
|
+
|
|
+ l_table_create(win_fields);
|
|
+ return (struct hyptop_win *) win_fields;
|
|
+}
|
|
diff --git a/hyptop/win_fields.h b/hyptop/win_fields.h
|
|
new file mode 100644
|
|
index 0000000..b399203
|
|
--- /dev/null
|
|
+++ b/hyptop/win_fields.h
|
|
@@ -0,0 +1,31 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Window "fields": Select fields dialog.
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef WIN_FIELDS_H
|
|
+#define WIN_FIELDS_H
|
|
+
|
|
+#include "table.h"
|
|
+#include "hyptop.h"
|
|
+#include "win_help.h"
|
|
+
|
|
+struct win_fields {
|
|
+ struct hyptop_win win;
|
|
+ struct table *t;
|
|
+ struct table *table;
|
|
+ struct table_col **col_vec;
|
|
+ char **col_desc_vec;
|
|
+ int mode_unit_change;
|
|
+ int in_select;
|
|
+ struct hyptop_win *win_help;
|
|
+};
|
|
+
|
|
+struct hyptop_win *win_fields_new(struct table *t, struct table_col **col_vec,
|
|
+ char **col_desc_vec);
|
|
+
|
|
+#endif /* WIN_FIELDS_H */
|
|
diff --git a/hyptop/win_help.c b/hyptop/win_help.c
|
|
new file mode 100644
|
|
index 0000000..f18d0bb
|
|
--- /dev/null
|
|
+++ b/hyptop/win_help.c
|
|
@@ -0,0 +1,122 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Window "help": Show online help text.
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include "helper.h"
|
|
+#include "table.h"
|
|
+#include "hyptop.h"
|
|
+#include "win_help.h"
|
|
+#include "sd.h"
|
|
+
|
|
+/*
|
|
+ * Print help text to screen
|
|
+ */
|
|
+static void l_update_term(struct hyptop_win *win)
|
|
+{
|
|
+ struct win_help *win_help = (struct win_help *) win;
|
|
+ tbox_print(win_help->tb);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Process input and switch window if necessary
|
|
+ */
|
|
+static enum hyptop_win_action l_process_input(struct hyptop_win *win, int c)
|
|
+{
|
|
+ struct win_help *win_help = (struct win_help *) win;
|
|
+
|
|
+ switch (c) {
|
|
+ case 'h':
|
|
+ case KEY_RETURN:
|
|
+ case KEY_ENTER:
|
|
+ case KEY_LEFT:
|
|
+ case '?':
|
|
+ case 'q':
|
|
+ return win_back();
|
|
+ case 'G':
|
|
+ tbox_scroll_down(win_help->tb, TBOX_SCROLL_LAST);
|
|
+ break;
|
|
+ case 'g':
|
|
+ tbox_scroll_up(win_help->tb, TBOX_SCROLL_LAST);
|
|
+ break;
|
|
+ case KEY_NPAGE:
|
|
+ tbox_scroll_down(win_help->tb, TBOX_SCROLL_PAGE);
|
|
+ break;
|
|
+ case KEY_PPAGE:
|
|
+ tbox_scroll_up(win_help->tb, TBOX_SCROLL_PAGE);
|
|
+ break;
|
|
+ case 'k':
|
|
+ case KEY_UP:
|
|
+ tbox_scroll_up(win_help->tb, TBOX_SCROLL_LINE);
|
|
+ break;
|
|
+ case 'j':
|
|
+ case KEY_DOWN:
|
|
+ tbox_scroll_down(win_help->tb, TBOX_SCROLL_LINE);
|
|
+ break;
|
|
+ case ERR:
|
|
+ return WIN_KEEP;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ hyptop_update_term();
|
|
+ return WIN_KEEP;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Event loop: wait for input and print help text
|
|
+ */
|
|
+static void l_run(struct hyptop_win *win)
|
|
+{
|
|
+ (void) win;
|
|
+
|
|
+ while (1) {
|
|
+ hyptop_update_term();
|
|
+ if (hyptop_process_input() == WIN_SWITCH)
|
|
+ return;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add text to text box
|
|
+ */
|
|
+static void l_add_text(struct tbox *tb, const char *str)
|
|
+{
|
|
+ char *line, *line_end, *str_cpy;
|
|
+
|
|
+ str_cpy = line_end = ht_strdup(str);
|
|
+ for (line = str_cpy; line_end != NULL; line = line_end + 1) {
|
|
+ line_end = strchr(line, '\n');
|
|
+ if (line_end)
|
|
+ *line_end = 0;
|
|
+ tbox_line_add(tb, line);
|
|
+ }
|
|
+ ht_free(str_cpy);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Create new help window for "win" and init window description
|
|
+ */
|
|
+struct hyptop_win *win_help_new(struct hyptop_win *win)
|
|
+{
|
|
+ struct win_help *win_help;
|
|
+
|
|
+ win_help = ht_zalloc(sizeof(*win_help));
|
|
+
|
|
+ win_help->tb = tbox_new();
|
|
+ tbox_printf(win_help->tb, "\\BWindow: %s\\B", win->id);
|
|
+ tbox_printf(win_help->tb, " ");
|
|
+ l_add_text(win_help->tb, win->desc);
|
|
+ nav_desc_add(win_help->tb, win->desc_normal_vec, win->desc_select_vec,
|
|
+ win->desc_general_vec);
|
|
+ tbox_finish(win_help->tb);
|
|
+
|
|
+ win_help->win.process_input = l_process_input;
|
|
+ win_help->win.update_term = l_update_term;
|
|
+ win_help->win.run = l_run;
|
|
+
|
|
+ return (struct hyptop_win *) win_help;
|
|
+}
|
|
diff --git a/hyptop/win_help.h b/hyptop/win_help.h
|
|
new file mode 100644
|
|
index 0000000..5f4f45d
|
|
--- /dev/null
|
|
+++ b/hyptop/win_help.h
|
|
@@ -0,0 +1,23 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Window "help": Show online help text.
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef WIN_HELP_H
|
|
+#define WIN_HELP_H
|
|
+
|
|
+#include "tbox.h"
|
|
+#include "hyptop.h"
|
|
+
|
|
+struct win_help {
|
|
+ struct hyptop_win win;
|
|
+ struct tbox *tb;
|
|
+};
|
|
+
|
|
+struct hyptop_win *win_help_new(struct hyptop_win *win);
|
|
+
|
|
+#endif /* WIN_HELP_H */
|
|
diff --git a/hyptop/win_sys.c b/hyptop/win_sys.c
|
|
new file mode 100644
|
|
index 0000000..2039c72
|
|
--- /dev/null
|
|
+++ b/hyptop/win_sys.c
|
|
@@ -0,0 +1,387 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Window "sys": Shows one system in more detail.
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <errno.h>
|
|
+#include "helper.h"
|
|
+#include "table.h"
|
|
+#include "hyptop.h"
|
|
+#include "sd.h"
|
|
+#include "win_fields.h"
|
|
+#include "win_help.h"
|
|
+#include "opts.h"
|
|
+
|
|
+/*
|
|
+ * Globals for sys_list window
|
|
+ */
|
|
+static char l_sys_id[SD_SYS_ID_SIZE]; /* System to show */
|
|
+static struct table_col l_cpu_col; /* CPU column */
|
|
+static struct table_col l_vis_col; /* Visual column */
|
|
+static struct table *l_t; /* Table */
|
|
+static int l_initialized; /* Win initialized ? */
|
|
+static struct hyptop_win *l_win_fields; /* Fields window */
|
|
+static struct hyptop_win *l_win_help; /* Help window */
|
|
+
|
|
+/* CPU column */
|
|
+static struct table_col l_cpu_col = {
|
|
+ .type = TABLE_COL_TYPE_U64,
|
|
+ .unit = &table_col_unit_cnt,
|
|
+ .unit_fam = table_col_unit_fam_cnt,
|
|
+ .align = TABLE_COL_ALIGN_LEFT,
|
|
+ .agg = TABLE_COL_AGG_NONE,
|
|
+ .hotkey = 'i',
|
|
+ .head = "cpuid",
|
|
+};
|
|
+
|
|
+/* Visual column */
|
|
+static struct table_col l_vis_col = {
|
|
+ .type = TABLE_COL_TYPE_U64,
|
|
+ .unit = &table_col_unit_vis,
|
|
+ .unit_fam = table_col_unit_fam_vis,
|
|
+ .align = TABLE_COL_ALIGN_LEFT,
|
|
+ .agg = TABLE_COL_AGG_NONE,
|
|
+ .hotkey = 'v',
|
|
+ .head = "visual",
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Online help text for sys window
|
|
+ */
|
|
+static const char l_help_str[] =
|
|
+"The \"sys\" window displays CPU information about one selected system.\n"
|
|
+"Under z/VM you can only see aggregated CPU information and not information\n"
|
|
+"about single CPUs.\n"
|
|
+"\n"
|
|
+"Select a column by pressing the hotkey of the column. This key is underlined\n"
|
|
+"in the heading. The table is sorted according to the values in the selected\n"
|
|
+"column. If you press the hotkey again, the sort order is reversed.\n"
|
|
+"Alternatively you can select columns with the '<' and '>' keys.\n";
|
|
+
|
|
+/*
|
|
+ * Description of Navigation Keys (used for help window)
|
|
+ */
|
|
+static struct nav_desc *l_nav_desc_normal_vec[] = {
|
|
+ &nav_desc_select_mode_enter,
|
|
+ &nav_desc_marks_clear,
|
|
+ &nav_desc_win_leave_sys,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+static struct nav_desc *l_nav_desc_select_vec[] = {
|
|
+ &nav_desc_select_mode_leave,
|
|
+ &nav_desc_mark_toggle,
|
|
+ &nav_desc_win_leave_sys_fast,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+static struct nav_desc *l_nav_desc_general_vec[] = {
|
|
+ &nav_desc_win_enter_fields,
|
|
+ &nav_desc_win_enter_cpu_types,
|
|
+ &nav_desc_col_unit_increase,
|
|
+ &nav_desc_col_unit_decrease,
|
|
+ &nav_desc_select_col_next,
|
|
+ &nav_desc_select_col_prev,
|
|
+ &nav_desc_select_col_hotkey,
|
|
+ &nav_desc_scroll_up_line,
|
|
+ &nav_desc_scroll_down_line,
|
|
+ &nav_desc_scroll_up_page,
|
|
+ &nav_desc_scroll_down_page,
|
|
+ &nav_desc_scroll_up_head,
|
|
+ &nav_desc_scroll_down_tail,
|
|
+ &nav_desc_mark_toggle_view,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Add CPU item to table row
|
|
+ */
|
|
+static void l_cpu_item_add(struct table_row *table_row, struct sd_cpu *cpu,
|
|
+ struct sd_cpu_item *item)
|
|
+{
|
|
+ switch (sd_cpu_item_type(item)) {
|
|
+ case SD_TYPE_U16:
|
|
+ case SD_TYPE_U32:
|
|
+ assert(0);
|
|
+ break;
|
|
+ case SD_TYPE_U64:
|
|
+ table_row_entry_u64_add(table_row,
|
|
+ sd_cpu_item_table_col(item),
|
|
+ sd_cpu_item_u64(item, cpu));
|
|
+ break;
|
|
+ case SD_TYPE_S64:
|
|
+ table_row_entry_s64_add(table_row,
|
|
+ sd_cpu_item_table_col(item),
|
|
+ sd_cpu_item_s64(item, cpu));
|
|
+ break;
|
|
+ case SD_TYPE_STR:
|
|
+ table_row_entry_str_add(table_row,
|
|
+ sd_cpu_item_table_col(item),
|
|
+ sd_cpu_item_str(item, cpu));
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add visualization of CPU time to table row
|
|
+ */
|
|
+static void l_cpu_add_visual(struct table_row *table_row, struct sd_cpu *cpu)
|
|
+{
|
|
+ s64 steal_us;
|
|
+ u64 cpu_us;
|
|
+
|
|
+ cpu_us = sd_cpu_item_u64(&sd_cpu_item_cpu_diff, cpu) / sd_cpu_cnt(cpu);
|
|
+ steal_us = sd_cpu_item_s64(&sd_cpu_item_steal_diff, cpu) /
|
|
+ sd_cpu_cnt(cpu);
|
|
+ steal_us = MAX(steal_us, 0);
|
|
+ table_row_entry_u64_add_pair(table_row, &l_vis_col, cpu_us, steal_us);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add CPU to table
|
|
+ */
|
|
+static void l_cpu_add(struct sd_cpu *cpu)
|
|
+{
|
|
+ struct table_row *table_row;
|
|
+ struct sd_cpu_item *item;
|
|
+ unsigned int cpu_id;
|
|
+ unsigned int i;
|
|
+
|
|
+ table_row = table_row_alloc(l_t);
|
|
+ cpu_id = atoi(sd_cpu_id(cpu));
|
|
+ table_row_entry_u64_add(table_row, &l_cpu_col, cpu_id);
|
|
+
|
|
+ sd_cpu_item_iterate(item, i)
|
|
+ l_cpu_item_add(table_row, cpu, item);
|
|
+ l_cpu_add_visual(table_row, cpu);
|
|
+ table_row_add(l_t, table_row);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Fill system CPU data into table
|
|
+ */
|
|
+static int l_table_create(void)
|
|
+{
|
|
+ struct sd_sys *parent;
|
|
+ struct sd_cpu *cpu;
|
|
+
|
|
+ parent = sd_sys_get(sd_sys_root_get(), l_sys_id);
|
|
+ if (!parent)
|
|
+ return -ENODEV;
|
|
+ table_row_del_all(l_t);
|
|
+ sd_cpu_iterate(parent, cpu)
|
|
+ l_cpu_add(cpu);
|
|
+ table_finish(l_t);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print table to screen
|
|
+ */
|
|
+static void l_table_update_term(struct hyptop_win *win)
|
|
+{
|
|
+ (void) win;
|
|
+
|
|
+ ht_print_head(l_sys_id);
|
|
+ table_print(l_t);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Process input and switch window if necessary
|
|
+ */
|
|
+static enum hyptop_win_action l_process_input(struct hyptop_win *win, int c)
|
|
+{
|
|
+ (void) win;
|
|
+
|
|
+ switch (c) {
|
|
+ case 't':
|
|
+ return win_switch(g.win_cpu_types);
|
|
+ case '?':
|
|
+ return win_switch(l_win_help);
|
|
+ case 'f':
|
|
+ return win_switch(l_win_fields);
|
|
+ case 'q':
|
|
+ return win_back();
|
|
+ case 'h':
|
|
+ case KEY_LEFT:
|
|
+ if (!(table_mode_select(l_t)))
|
|
+ return win_back();
|
|
+ break;
|
|
+ case ERR:
|
|
+ return WIN_KEEP;
|
|
+ }
|
|
+ table_process_input(l_t, c);
|
|
+ hyptop_update_term();
|
|
+ return WIN_KEEP;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Enable field and set unit
|
|
+ */
|
|
+static void l_field_set(struct table_col_spec *col_spec)
|
|
+{
|
|
+ table_col_enable_toggle(l_t, col_spec->hotkey);
|
|
+ if (!col_spec->unit_str)
|
|
+ return;
|
|
+ if (table_col_unit_set(l_t, col_spec->hotkey, col_spec->unit_str))
|
|
+ ERR_EXIT("Invalid unit \"%s\" for field \"%c\"\n",
|
|
+ col_spec->unit_str, col_spec->hotkey);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Enable field defined in "col_spec"
|
|
+ */
|
|
+static void l_field_enable(struct table_col_spec *col_spec)
|
|
+{
|
|
+ struct sd_cpu_item *item;
|
|
+ struct table_col *col;
|
|
+ unsigned int i;
|
|
+
|
|
+ if (table_col_hotkey(&l_vis_col) == col_spec->hotkey) {
|
|
+ l_field_set(col_spec);
|
|
+ return;
|
|
+ }
|
|
+ sd_cpu_item_iterate(item, i) {
|
|
+ col = sd_cpu_item_table_col(item);
|
|
+ if (table_col_hotkey(col) != col_spec->hotkey)
|
|
+ continue;
|
|
+ l_field_set(col_spec);
|
|
+ return;
|
|
+ }
|
|
+ ERR_EXIT("Unknown field \"%c\"\n", col_spec->hotkey);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Enable fields defined on command line
|
|
+ */
|
|
+static void l_fields_enable_cmdline(void)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ table_col_enable_toggle(l_t, table_col_hotkey(&l_vis_col));
|
|
+ for (i = 0; i < win_sys.opts.fields.cnt; i++)
|
|
+ l_field_enable(win_sys.opts.fields.vec[i]);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Enable fields like defined in data gatherer
|
|
+ */
|
|
+static void l_fields_enable_default(void)
|
|
+{
|
|
+ struct sd_cpu_item *item;
|
|
+ struct table_col *col;
|
|
+ unsigned int i;
|
|
+
|
|
+ sd_cpu_item_enable_iterate(item, i) {
|
|
+ col = sd_cpu_item_table_col(item);
|
|
+ table_col_enable_toggle(l_t, table_col_hotkey(col));
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Event loop: Make regular updates of table
|
|
+ */
|
|
+static void l_run(struct hyptop_win *win)
|
|
+{
|
|
+ enum hyptop_win_action action;
|
|
+ (void) win;
|
|
+
|
|
+ /* Reformat table when entering window */
|
|
+ table_rebuild(l_t);
|
|
+ while (1) {
|
|
+ if (l_table_create()) {
|
|
+ if (g.o.batch_mode_specified)
|
|
+ ERR_EXIT("System \"%s\" not available.\n",
|
|
+ l_sys_id);
|
|
+ win_back();
|
|
+ return;
|
|
+ }
|
|
+ hyptop_update_term();
|
|
+ action = hyptop_process_input_timeout();
|
|
+ if (action == WIN_SWITCH)
|
|
+ return;
|
|
+
|
|
+ /* No updates in select mode */
|
|
+ if (!table_mode_select(l_t))
|
|
+ sd_update();
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Define system for window
|
|
+ */
|
|
+void win_sys_set(const char *sys_id)
|
|
+{
|
|
+ if (l_initialized)
|
|
+ table_reset(l_t);
|
|
+ strncpy(l_sys_id, sys_id, sizeof(l_sys_id));
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize window
|
|
+ */
|
|
+void win_sys_init(void)
|
|
+{
|
|
+ struct table_col **col_vec;
|
|
+ struct sd_cpu_item *item;
|
|
+ struct table_col *col;
|
|
+ char **col_desc_vec;
|
|
+ unsigned int i, item_cnt;
|
|
+
|
|
+ /* Alloc table and add columns */
|
|
+ l_t = table_new(1, 1, 1, 1);
|
|
+ table_col_add(l_t, &l_cpu_col);
|
|
+ table_col_rsort(&l_cpu_col);
|
|
+
|
|
+ item_cnt = sd_cpu_item_cnt() + 2;
|
|
+ col_vec = ht_zalloc(sizeof(void *) * item_cnt);
|
|
+ col_desc_vec = ht_zalloc(sizeof(void *) * item_cnt);
|
|
+
|
|
+ sd_cpu_item_iterate(item, i) {
|
|
+ col = sd_cpu_item_table_col(item);
|
|
+ table_col_add(l_t, col);
|
|
+ table_col_enable_toggle(l_t, table_col_hotkey(col));
|
|
+ col_vec[i] = col;
|
|
+ col_desc_vec[i] = item->desc;
|
|
+ }
|
|
+ col_vec[i] = &l_vis_col;
|
|
+ col_desc_vec[i] = "Visualization of CPU time per second";
|
|
+ table_col_add(l_t, &l_vis_col);
|
|
+
|
|
+ /* Enable fields */
|
|
+ if (win_sys.opts.fields.specified)
|
|
+ l_fields_enable_cmdline();
|
|
+ else
|
|
+ l_fields_enable_default();
|
|
+
|
|
+ /* Select sort field */
|
|
+ if (win_sys.opts.sort_field_specified) {
|
|
+ for (i = 0; i < win_sys.opts.sort_field_specified; i++) {
|
|
+ if (table_col_select(l_t, win_sys.opts.sort_field))
|
|
+ ERR_EXIT("Sort field \"%c\" is not available\n",
|
|
+ win_sys.opts.sort_field);
|
|
+ }
|
|
+ }
|
|
+ /* Initialize help and fields window */
|
|
+ l_win_fields = win_fields_new(l_t, col_vec, col_desc_vec);
|
|
+ l_win_help = win_help_new(&win_sys);
|
|
+ l_initialized = 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * hyptop window structure definition
|
|
+ */
|
|
+struct hyptop_win win_sys = {
|
|
+ .process_input = l_process_input,
|
|
+ .update_term = l_table_update_term,
|
|
+ .run = l_run,
|
|
+ .id = "sys",
|
|
+ .desc = l_help_str,
|
|
+ .desc_normal_vec = l_nav_desc_normal_vec,
|
|
+ .desc_select_vec = l_nav_desc_select_vec,
|
|
+ .desc_general_vec = l_nav_desc_general_vec,
|
|
+};
|
|
diff --git a/hyptop/win_sys_list.c b/hyptop/win_sys_list.c
|
|
new file mode 100644
|
|
index 0000000..79eb092
|
|
--- /dev/null
|
|
+++ b/hyptop/win_sys_list.c
|
|
@@ -0,0 +1,380 @@
|
|
+/*
|
|
+ * hyptop - Show hypervisor performance data on System z
|
|
+ *
|
|
+ * Window "sys_list":
|
|
+ * Shows a list of systems that the hypervisor is currently running.
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include "helper.h"
|
|
+#include "table.h"
|
|
+#include "hyptop.h"
|
|
+#include "win_fields.h"
|
|
+#include "win_help.h"
|
|
+#include "sd.h"
|
|
+#include "nav_desc.h"
|
|
+#include "opts.h"
|
|
+
|
|
+/*
|
|
+ * Globals for sys_list window
|
|
+ */
|
|
+static struct table *l_t; /* Table */
|
|
+static struct hyptop_win *l_win_fields; /* Fields Window */
|
|
+static struct hyptop_win *l_win_help; /* Herp Window */
|
|
+
|
|
+/* System column */
|
|
+static struct table_col l_col_sys = TABLE_COL_STR_LEFT('y', "system");
|
|
+
|
|
+/*
|
|
+ * Online help text for sys_list window
|
|
+ */
|
|
+static const char l_help_str[] =
|
|
+"The following windows can be accessed:\n"
|
|
+"\n"
|
|
+" +-----------+ RIGHT +----------+\n"
|
|
+" | | <----------------------> | |\n"
|
|
+" | | LEFT | |\n"
|
|
+" | | | |\n"
|
|
+" | sys_list | 't' +-----------+ | | 't' +-----------+\n"
|
|
+" | | <-------> | cpu_types | | sys | <-------> | cpu_types |\n"
|
|
+" | (start) | 't',LEFT +-----------+ | | 't',LEFT +-----------+\n"
|
|
+" | | | |\n"
|
|
+" | | 'f' +--------+ | | 'f' +--------+\n"
|
|
+" | | <-------> | fields | | | <-------> | fields |\n"
|
|
+" | | 'f',LEFT +--------+ | | 'f',LEFT +--------+\n"
|
|
+" +-----------+ +----------+\n"
|
|
+"\n"
|
|
+" * sys_list: Start window that shows a list of systems that the hypervisor\n"
|
|
+" is currently running.\n"
|
|
+" * sys: Shows one system in more detail.\n"
|
|
+" * cpu_types: Select CPU types that are used for calculating CPU data.\n"
|
|
+" * fields: Select fields and units for windows sys_list or sys.\n"
|
|
+"\n"
|
|
+"\\BNavigation\\B\n"
|
|
+"\n"
|
|
+"To navigate between the windows, use the arrow keys or 'hjkl'. The windows\n"
|
|
+"have two modes, \"normal mode\" and \"select mode\". When you start the "
|
|
+"program,\n"
|
|
+"the window is in normal mode where data is updated at regular intervals. Use\n"
|
|
+"the RIGHT arrow key to enter the select mode. In select mode you can select\n"
|
|
+"rows with with UP and DOWN arrow keys and mark them with the SPACE bar. From\n"
|
|
+"the \"sys_list\" window you can access the \"sys\" window in select mode\n"
|
|
+"with the arrow key RIGHT. Leave the select mode with the arrow key LEFT.\n"
|
|
+"If you are in normal mode, the arrow key LEFT goes to the previous window.\n"
|
|
+"You can scroll all windows using the arrow keys UP, DOWN, PAGEUP and\n"
|
|
+"PAGEDOWN. You can jump to the end of a window with 'G' and to the beginning\n"
|
|
+"with 'g'.\n"
|
|
+"\n"
|
|
+"Select a column by pressing the hotkey of the column. This key is underlined\n"
|
|
+"in the heading. The table is sorted according to the values in the selected\n"
|
|
+"column. If you press the hotkey again, the sort order is reversed.\n"
|
|
+"Alternatively you can select columns with the '<' and '>' keys.\n"
|
|
+"\n"
|
|
+"\\BTable layout\\B\n"
|
|
+"\n"
|
|
+"At the top left of the table the current time is shown. Then the CPU types\n"
|
|
+"with the physical CPU numbers that are used for CPU time calculation are\n"
|
|
+"displayed. The second row shows the units that are currently used for\n"
|
|
+"formatting the data. The last row shows the status display (see description\n"
|
|
+"below) and the aggregation of the the data columns. The last row aggregates\n"
|
|
+"all rows, not only the visible ones. If only the marked rows are shown\n"
|
|
+"(with '.') then only these rows are aggregated.\n"
|
|
+"\n"
|
|
+"\\BStatus display\\B\n"
|
|
+"\n"
|
|
+"At the left bottom of the screen a status display is shown.\n"
|
|
+"Example: \"V:V:N\"\n\n"
|
|
+"The first character shows, if the window can be scrolled:\n"
|
|
+" 'V': Window can be scrolled down\n"
|
|
+" '|': Window can be scrolled up/down\n"
|
|
+" '^': Window can be scrolled up\n"
|
|
+" '=': Window cannot be scrolled\n"
|
|
+"The second character shows the sort order for sorted tables:\n"
|
|
+" 'V': Higher values first\n"
|
|
+" '^': Lower values first\n"
|
|
+"The third character shows the current mode:\n"
|
|
+" 'N': Normal mode\n"
|
|
+" 'S': Select mode\n";
|
|
+
|
|
+/*
|
|
+ * Description of Navigation Keys (used for help window)
|
|
+ */
|
|
+static struct nav_desc *l_nav_desc_normal_vec[] = {
|
|
+ &nav_desc_select_mode_enter,
|
|
+ &nav_desc_marks_clear,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+static struct nav_desc *l_nav_desc_select_vec[] = {
|
|
+ &nav_desc_select_mode_leave,
|
|
+ &nav_desc_win_enter_sys,
|
|
+ &nav_desc_mark_toggle,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+static struct nav_desc *l_nav_desc_general_vec[] = {
|
|
+ &nav_desc_win_enter_fields,
|
|
+ &nav_desc_win_enter_cpu_types,
|
|
+ &nav_desc_col_unit_increase,
|
|
+ &nav_desc_col_unit_decrease,
|
|
+ &nav_desc_select_col_next,
|
|
+ &nav_desc_select_col_prev,
|
|
+ &nav_desc_select_col_hotkey,
|
|
+ &nav_desc_scroll_up_line,
|
|
+ &nav_desc_scroll_down_line,
|
|
+ &nav_desc_scroll_up_page,
|
|
+ &nav_desc_scroll_down_page,
|
|
+ &nav_desc_scroll_up_head,
|
|
+ &nav_desc_scroll_down_tail,
|
|
+ &nav_desc_mark_toggle_view,
|
|
+ &nav_desc_quit,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Add system item to table row
|
|
+ */
|
|
+static void l_sys_item_add(struct table_row *table_row, struct sd_sys *sys,
|
|
+ struct sd_sys_item *item)
|
|
+{
|
|
+ switch (sd_sys_item_type(item)) {
|
|
+ case SD_TYPE_U64:
|
|
+ case SD_TYPE_U32:
|
|
+ case SD_TYPE_U16:
|
|
+ table_row_entry_u64_add(table_row,
|
|
+ sd_sys_item_table_col(item),
|
|
+ sd_sys_item_u64(sys, item));
|
|
+ break;
|
|
+ case SD_TYPE_S64:
|
|
+ table_row_entry_s64_add(table_row,
|
|
+ sd_sys_item_table_col(item),
|
|
+ sd_sys_item_s64(sys, item));
|
|
+ break;
|
|
+ case SD_TYPE_STR:
|
|
+ table_row_entry_str_add(table_row,
|
|
+ sd_sys_item_table_col(item),
|
|
+ sd_sys_item_str(sys, item));
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add system to table
|
|
+ */
|
|
+static void l_sys_add(struct sd_sys *sys)
|
|
+{
|
|
+ struct table_row *table_row;
|
|
+ struct sd_sys_item *item;
|
|
+ unsigned int i;
|
|
+
|
|
+ table_row = table_row_alloc(l_t);
|
|
+ table_row_entry_str_add(table_row, &l_col_sys, sd_sys_id(sys));
|
|
+
|
|
+ sd_sys_item_iterate(item, i)
|
|
+ l_sys_item_add(table_row, sys, item);
|
|
+ table_row_add(l_t, table_row);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Fill system data into table
|
|
+ */
|
|
+static void l_table_create(void)
|
|
+{
|
|
+ struct sd_sys *parent, *guest;
|
|
+
|
|
+ table_row_del_all(l_t);
|
|
+ parent = sd_sys_root_get();
|
|
+ sd_sys_iterate(parent, guest) {
|
|
+ if (!opts_sys_specified(&win_sys_list, sd_sys_id(guest)))
|
|
+ continue;
|
|
+ l_sys_add(guest);
|
|
+ }
|
|
+ table_finish(l_t);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print table to screen
|
|
+ */
|
|
+static void l_table_update_term(struct hyptop_win *win)
|
|
+{
|
|
+ (void) win;
|
|
+
|
|
+ ht_print_head(NULL);
|
|
+ table_print(l_t);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Process input and switch window if necessary
|
|
+ */
|
|
+static enum hyptop_win_action l_process_input(struct hyptop_win *win, int c)
|
|
+{
|
|
+ char selected_sys[TABLE_STR_MAX];
|
|
+ (void) win;
|
|
+
|
|
+ switch (c) {
|
|
+ case 'f':
|
|
+ return win_switch(l_win_fields);
|
|
+ case 't':
|
|
+ return win_switch(g.win_cpu_types);
|
|
+ case '?':
|
|
+ return win_switch(l_win_help);
|
|
+ case 'q':
|
|
+ hyptop_exit(0);
|
|
+ case 'l':
|
|
+ case KEY_RIGHT:
|
|
+ if (!table_mode_select(l_t))
|
|
+ break;
|
|
+ table_row_select_key_get(l_t, selected_sys);
|
|
+ win_sys_set(selected_sys);
|
|
+ return win_switch(&win_sys);
|
|
+ case ERR:
|
|
+ break;
|
|
+ }
|
|
+ table_process_input(l_t, c);
|
|
+ hyptop_update_term();
|
|
+ return WIN_KEEP;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Enable field and set unit
|
|
+ */
|
|
+static void l_field_set(struct table_col_spec *col_spec)
|
|
+{
|
|
+ table_col_enable_toggle(l_t, col_spec->hotkey);
|
|
+ if (!col_spec->unit_str)
|
|
+ return;
|
|
+ if (table_col_unit_set(l_t, col_spec->hotkey, col_spec->unit_str))
|
|
+ ERR_EXIT("Invalid unit \"%s\" for field \"%c\"\n",
|
|
+ col_spec->unit_str, col_spec->hotkey);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Enable field defined in "col_spec"
|
|
+ */
|
|
+static void l_field_enable(struct table_col_spec *col_spec)
|
|
+{
|
|
+ struct sd_sys_item *item;
|
|
+ struct table_col *col;
|
|
+ unsigned int i;
|
|
+
|
|
+ sd_sys_item_iterate(item, i) {
|
|
+ col = sd_sys_item_table_col(item);
|
|
+ if (table_col_hotkey(col) != col_spec->hotkey)
|
|
+ continue;
|
|
+ l_field_set(col_spec);
|
|
+ return;
|
|
+ }
|
|
+ ERR_EXIT("Unknown field \"%c\"\n", col_spec->hotkey);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Enable fields defined on command line
|
|
+ */
|
|
+static void l_fields_enable_cmdline(void)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ for (i = 0; i < win_sys_list.opts.fields.cnt; i++)
|
|
+ l_field_enable(win_sys_list.opts.fields.vec[i]);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Enable fields like defined in data gatherer
|
|
+ */
|
|
+static void l_fields_enable_default(void)
|
|
+{
|
|
+ struct sd_sys_item *item;
|
|
+ struct table_col *col;
|
|
+ unsigned int i;
|
|
+
|
|
+ sd_sys_item_enable_iterate(item, i) {
|
|
+ col = sd_sys_item_table_col(item);
|
|
+ table_col_enable_toggle(l_t, table_col_hotkey(col));
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Event loop: Make regular updates of table
|
|
+ */
|
|
+static void l_run(struct hyptop_win *win)
|
|
+{
|
|
+ enum hyptop_win_action action;
|
|
+ (void) win;
|
|
+
|
|
+ /* Reformat table when entering window */
|
|
+ table_rebuild(l_t);
|
|
+ while (1) {
|
|
+ l_table_create();
|
|
+ hyptop_update_term();
|
|
+ action = hyptop_process_input_timeout();
|
|
+ if (action == WIN_SWITCH)
|
|
+ return;
|
|
+ /* No updates in select mode */
|
|
+ if (!table_mode_select(l_t))
|
|
+ sd_update();
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize window
|
|
+ */
|
|
+void win_sys_list_init(void)
|
|
+{
|
|
+ struct table_col **col_vec;
|
|
+ struct sd_sys_item *item;
|
|
+ struct table_col *col;
|
|
+ char **col_desc_vec;
|
|
+ unsigned int i;
|
|
+ int item_cnt;
|
|
+
|
|
+ /* Alloc table and add columns */
|
|
+ l_t = table_new(1, 1, 1, 1);
|
|
+ table_col_add(l_t, &l_col_sys);
|
|
+
|
|
+ item_cnt = sd_sys_item_cnt() + 1;
|
|
+ col_vec = ht_zalloc(sizeof(void *) * item_cnt);
|
|
+ col_desc_vec = ht_zalloc(sizeof(void *) * item_cnt);
|
|
+
|
|
+ sd_sys_item_iterate(item, i) {
|
|
+ col = sd_sys_item_table_col(item);
|
|
+ table_col_add(l_t, col);
|
|
+ table_col_enable_toggle(l_t, table_col_hotkey(col));
|
|
+ col_vec[i] = col;
|
|
+ col_desc_vec[i] = item->desc;
|
|
+ }
|
|
+ /* Enable fields */
|
|
+ if (win_sys_list.opts.fields.specified)
|
|
+ l_fields_enable_cmdline();
|
|
+ else
|
|
+ l_fields_enable_default();
|
|
+
|
|
+ /* Select sort field */
|
|
+ if (win_sys_list.opts.sort_field_specified) {
|
|
+ for (i = 0; i < win_sys_list.opts.sort_field_specified; i++) {
|
|
+ if (table_col_select(l_t, win_sys_list.opts.sort_field))
|
|
+ ERR_EXIT("Sort field \"%c\" is not available\n",
|
|
+ win_sys_list.opts.sort_field);
|
|
+ }
|
|
+ } else {
|
|
+ table_col_select(l_t, sd_sys_item_cpu_diff.table_col.hotkey);
|
|
+ }
|
|
+ /* Initialize help and fields window */
|
|
+ l_win_help = win_help_new(&win_sys_list);
|
|
+ l_win_fields = win_fields_new(l_t, col_vec, col_desc_vec);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * hyptop window structure definition
|
|
+ */
|
|
+struct hyptop_win win_sys_list = {
|
|
+ .process_input = l_process_input,
|
|
+ .update_term = l_table_update_term,
|
|
+ .run = l_run,
|
|
+ .id = "sys_list",
|
|
+ .desc = l_help_str,
|
|
+ .desc_normal_vec = l_nav_desc_normal_vec,
|
|
+ .desc_select_vec = l_nav_desc_select_vec,
|
|
+ .desc_general_vec = l_nav_desc_general_vec,
|
|
+};
|
|
--
|
|
1.7.3.5
|
|
|