From 096c2b87270417456ce9d92046838795ccd32ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= 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 + */ + +#include +#include +#include +#include +#include +#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 + */ + +#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 + */ + +#include +#include +#include +#include +#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 + */ + +#include +#include +#include +#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 + * Christian Borntraeger + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 + * Christian Borntraeger + */ + +#ifndef HELPER_H +#define HELPER_H + +#include +#include +#include +#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 " " or " "\-\-window=" +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 ,..." " or " "\-\-sys=,..." +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 [:],..." " or " "\-\-fields=[:],..." +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 " " or " "\-\-sort=" +Select sort field for current window. To reverse the sort order, specify the +option twice. See FIELDS below for definitions. +.TP +.BR "\-t ,..." " or " "\-\-cpu_types=,..." +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 " " or " "\-\-delay=" +Specifies the delay between screen updates. +.TP +.BR "\-n " " or " "\-\-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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#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 + */ + +#ifndef HYPTOP_H +#define HYPTOP_H + +#include +#include +#include +#include +#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 + */ + +#include +#include +#include +#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 = {"", 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 = {"", 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 + */ + +#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 + */ + +#include +#include +#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 + */ + +#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 + */ + +#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 + */ + +#include +#include +#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 + */ + +#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 + */ + +#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 + */ + +#include +#include +#include +#include +#include +#include +#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 + */ + +#ifndef TABLE_H +#define TABLE_H + +#include +#include +#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 + */ + +#include +#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 + */ + +#include +#include +#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 + */ + +#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 + */ + +#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 + */ + +#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 + */ + +#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 + */ + +#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 + */ + +#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 + */ + +#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 + */ + +#include +#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 + */ + +#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