diff --git a/hyptop/helper.c b/hyptop/helper.c index 162caa1b..b1d54e88 100644 --- a/hyptop/helper.c +++ b/hyptop/helper.c @@ -25,6 +25,9 @@ #include #include +#include "lib/util_fmt.h" +#include "lib/util_libc.h" + #include "helper.h" #include "hyptop.h" #include "sd.h" @@ -401,3 +404,40 @@ s64 ht_calculate_smt_util(u64 core_us, u64 thr_us, u64 mgm_us, int thread_per_co return smt_us; } + +/* + * Add two new key value pairs containing the current time as UNIX epoch and formatted string to a + * structured output object. + */ +void ht_fmt_time(void) +{ + struct timeval tv; + struct tm *tm; + char str[30]; + + gettimeofday(&tv, NULL); + tm = localtime(&tv.tv_sec); + if (!tm) + return; + util_fmt_pair(FMT_PERSIST, "time_epoch", "%lld", mktime(tm)); + strftime(str, sizeof(str), "%F %T%z", tm); + util_fmt_pair(FMT_PERSIST | FMT_QUOTE, "time", "%s", str); +} + +/* + * Add a new object for available CPU types to a structured output object. + */ +void ht_fmt_cpu_types(void) +{ + struct sd_cpu_type *cpu_type; + int i; + + util_fmt_obj_start(FMT_DEFAULT, "cputypes"); + sd_cpu_type_iterate(cpu_type, i) { + char *cpu_type_str = sd_cpu_type_id(cpu_type); + util_str_tolower(cpu_type_str); + util_fmt_pair(FMT_PERSIST, cpu_type_str, "%i", + sd_cpu_type_cpu_cnt(cpu_type)); + } + util_fmt_obj_end(); /* cpus{} */ +} diff --git a/hyptop/helper.h b/hyptop/helper.h index 9679c2aa..34672907 100644 --- a/hyptop/helper.h +++ b/hyptop/helper.h @@ -66,6 +66,12 @@ 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); +/* + * util_fmt helper functions + */ +void ht_fmt_time(void); +void ht_fmt_cpu_types(void); + /* * Error Macros */ diff --git a/hyptop/hyptop.c b/hyptop/hyptop.c index db059a11..dc02a7d8 100644 --- a/hyptop/hyptop.c +++ b/hyptop/hyptop.c @@ -18,6 +18,8 @@ #include #include +#include "lib/util_fmt.h" + #include "dg_debugfs.h" #include "helper.h" #include "hyptop.h" @@ -218,6 +220,22 @@ static void l_term_check(void) " try \"--batch_mode\"\n"); } +/* + * Init util_fmt if --format is specified on the command line. + */ +static void l_fmt_init(void) +{ + unsigned int flags = FMT_WARN; + + if (!g.o.format_specified) + return; + if (g.o.format == FMT_CSV) + flags |= FMT_QUOTEALL; + if (g.o.format == FMT_JSON || g.o.format == FMT_JSONSEQ) + flags |= FMT_HANDLEINT; + util_fmt_init(stdout, g.o.format, flags, 1); +} + /* * Init curses */ @@ -359,6 +377,7 @@ void __noreturn hyptop_exit(int rc) int main(int argc, char *argv[]) { opts_parse(argc, argv); + l_fmt_init(); hyptop_helper_init(); sd_init(); l_dg_init(); diff --git a/hyptop/hyptop.h b/hyptop/hyptop.h index 0ce68873..7eb84241 100644 --- a/hyptop/hyptop.h +++ b/hyptop/hyptop.h @@ -17,6 +17,8 @@ #include #include +#include "lib/util_fmt.h" + #include "helper.h" #include "nav_desc.h" #include "table.h" @@ -52,6 +54,8 @@ struct hyptop_win_opts { struct hyptop_opts { unsigned int win_specified; unsigned int batch_mode_specified; + unsigned int format_specified; + enum util_fmt_t format; unsigned int iterations_specified; unsigned int iterations; unsigned int iterations_act; diff --git a/hyptop/opts.c b/hyptop/opts.c index 9f315aee..a8c1f2b6 100644 --- a/hyptop/opts.c +++ b/hyptop/opts.c @@ -12,6 +12,7 @@ #include #include +#include "lib/util_fmt.h" #include "lib/util_libc.h" #include "lib/zt_common.h" @@ -20,6 +21,7 @@ #include "hyptop.h" #include "opts.h" #include "sd.h" +#include "table.h" static const char l_copyright_str[] = "Copyright IBM Corp. 2010, 2017"; @@ -39,10 +41,16 @@ static char HELP_TEXT[] = "-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" +" --format FORMAT Output format (" FMT_TYPE_NAMES "), implies -b\n" "-d, --delay SECONDS Delay time between screen updates\n" "-m, --smt_factor FACTOR Machine generation dependent SMT speedup factor.\n" "-n, --iterations NUMBER Number of iterations before ending\n"; +/* + * Options with long-name only + */ +#define OPT_FORMAT 256 /* --format */ + /* * Initialize default settings */ @@ -288,6 +296,23 @@ static void l_batch_mode_set(void) g.o.batch_mode_specified = 1; } +/* + * Set the "--format" option + */ +static void l_format_set(const char *str) +{ + enum util_fmt_t fmt; + + if (!util_fmt_name_to_type(str, &fmt)) { + ERR_EXIT("Unknown format '%s', supported formats: " + FMT_TYPE_NAMES "\n", str); + } + + l_batch_mode_set(); + g.o.format_specified = 1; + g.o.format = fmt; +} + /* * Make option consisteny checks at end of command line parsing */ @@ -323,6 +348,7 @@ void opts_parse(int argc, char *argv[]) { "fields", required_argument, NULL, 'f'}, { "sort_field", required_argument, NULL, 'S'}, { "cpu_types", required_argument, NULL, 't'}, + { "format", required_argument, NULL, OPT_FORMAT }, { NULL, 0, NULL, 0 } }; static const char option_string[] = "vhbd:m:w:s:n:f:t:S:"; @@ -367,6 +393,9 @@ void opts_parse(int argc, char *argv[]) case 'S': l_sort_field_set(optarg); break; + case OPT_FORMAT: + l_format_set(optarg); + break; default: l_std_usage_exit(); } @@ -432,8 +461,11 @@ void opts_iterations_next(void) { if (g.o.iterations_specified) { g.o.iterations_act++; - if (g.o.iterations_act >= g.o.iterations) + if (g.o.iterations_act >= g.o.iterations) { + if (g.o.format_specified) + table_fmt_end(); hyptop_exit(0); + } } } diff --git a/hyptop/table.c b/hyptop/table.c index 62ea0c34..46cfe9f3 100644 --- a/hyptop/table.c +++ b/hyptop/table.c @@ -16,6 +16,7 @@ #include #include +#include "lib/util_fmt.h" #include "lib/util_libc.h" #include "helper.h" @@ -955,15 +956,86 @@ static void l_table_print_all(struct table *t) "-------------------------\n"); } +/* + * Print one table row as structured output + * + * Note: column filtering and sorting is explicitly ignored because the + * assumption is that these operations can be trivially performed by the + * consumer. + */ +static void l_row_print_formatted(struct table *t, struct table_row *row) +{ + struct table_col *col; + int col_nr; + + table_col_iterate(t, col, col_nr) { + unsigned int flags = 0; + struct table_entry *e = &row->entries[col_nr]; + + if (row == t->row_last && col_nr == 0) + continue; + if (table_col_needs_quotes(col)) + flags = FMT_QUOTE; + util_fmt_pair(flags, col->head, "%s", e->str); + } +} + +/* + * Print table as structured output + */ +static void l_table_print_all_formatted(struct table *t) +{ + struct table_row *row; + + util_fmt_obj_start(FMT_ROW, "iteration"); + util_fmt_pair(FMT_PERSIST, "iteration", "%u", g.o.iterations_act); + ht_fmt_time(); + ht_fmt_cpu_types(); + if (strcmp(g.o.cur_win->id, "sys_list") == 0) + util_fmt_obj_start(FMT_LIST, "systems"); + else + util_fmt_obj_start(FMT_LIST, "cpus"); + util_list_iterate(&t->row_list, row) { + util_fmt_obj_start(FMT_ROW, "entry"); + l_row_print_formatted(t, row); + util_fmt_obj_end(); /* entry */ + } + util_fmt_obj_end(); /* systems[] */ + util_fmt_obj_start(FMT_DEFAULT, "summary"); + l_row_print_formatted(t, t->row_last); + util_fmt_obj_end(); /* summary{} */ + util_fmt_obj_end(); /* iteration */ +} + +void table_fmt_start(void) +{ + if (!g.o.format_specified) + return; + if (g.o.format != FMT_JSONSEQ) + util_fmt_obj_start(FMT_LIST, "hyptop"); +} + +void table_fmt_end(void) +{ + if (!g.o.format_specified) + return; + if (g.o.format != FMT_JSONSEQ) + util_fmt_obj_end(); /* hyptop[] */ +} + /* * Print table to screen */ void table_print(struct table *t) { - if (g.o.batch_mode_specified) - l_table_print_all(t); - else + if (g.o.batch_mode_specified) { + if (!g.o.format_specified) + l_table_print_all(t); + else + l_table_print_all_formatted(t); + } else { l_table_print_curses(t); + } } /* diff --git a/hyptop/table.h b/hyptop/table.h index 87d9f791..b30b79c7 100644 --- a/hyptop/table.h +++ b/hyptop/table.h @@ -390,6 +390,9 @@ 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); +extern void table_fmt_start(void); +extern void table_fmt_end(void); + /* * Entry add functions */ diff --git a/hyptop/win_sys.c b/hyptop/win_sys.c index 15311309..e46e8b46 100644 --- a/hyptop/win_sys.c +++ b/hyptop/win_sys.c @@ -194,7 +194,8 @@ static void l_table_update_term(struct hyptop_win *win) { (void) win; - ht_print_head(l_sys_id); + if (!g.o.format_specified) + ht_print_head(l_sys_id); table_print(l_t); } @@ -300,6 +301,7 @@ static void l_run(struct hyptop_win *win) /* Reformat table when entering window */ table_rebuild(l_t); + table_fmt_start(); while (1) { if (l_table_create()) { if (g.o.batch_mode_specified) diff --git a/hyptop/win_sys_list.c b/hyptop/win_sys_list.c index 20b4b8c0..43056bc4 100644 --- a/hyptop/win_sys_list.c +++ b/hyptop/win_sys_list.c @@ -206,7 +206,8 @@ static void l_table_update_term(struct hyptop_win *win) { (void) win; - ht_print_head(NULL); + if (!g.o.format_specified) + ht_print_head(NULL); table_print(l_t); } @@ -310,6 +311,7 @@ static void l_run(struct hyptop_win *win) /* Reformat table when entering window */ table_rebuild(l_t); + table_fmt_start(); while (1) { l_table_create(); hyptop_update_term();