s390utils/0049-hyptop-Show-hypervisor...

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