diff --git a/config.h b/config.h index 4a30d2ef1..55ebdded4 100644 --- a/config.h +++ b/config.h @@ -41,7 +41,8 @@ #define DEFAULT_NR_HUGEPAGES 100 /** Width of log output in characters */ -#define LOG_WIDTH 132 +#define LOG_WIDTH 80 +#define LOG_HEIGHT 25 /** Socket priority */ #define SOCKET_PRIO 7 diff --git a/include/villas/log.h b/include/villas/log.h index 22919e074..36cabee20 100644 --- a/include/villas/log.h +++ b/include/villas/log.h @@ -29,14 +29,17 @@ extern "C" { #include #include +#include #include "common.h" #include "log_config.h" #ifdef __GNUC__ #define INDENT int __attribute__ ((__cleanup__(log_outdent), unused)) _old_indent = log_indent(1); + #define NOINDENT int __attribute__ ((__cleanup__(log_outdent), unused)) _old_indent = log_noindent(); #else #define INDENT ; + #define NOINDENT ; #endif /* The log level which is passed as first argument to print() */ @@ -86,20 +89,16 @@ struct log { enum state state; struct timespec epoch; /**< A global clock used to prefix the log messages. */ + + struct winsize window; /**< Size of the terminal window. */ /** Debug level used by the debug() macro. * It defaults to V (defined by the Makefile) and can be * overwritten by the 'debug' setting in the configuration file. */ int level; - - /** Debug facilities used by the debug() macro. */ - long facilities; - - /** Path of the log file */ - const char *path; - - /** Send all log output to this file / stdout / stderr */ - FILE *file; + long facilities; /**< Debug facilities used by the debug() macro. */ + const char *path; /**< Path of the log file. */ + FILE *file; /**< Send all log output to this file / stdout / stderr. */ }; /** The global log instance. */ @@ -122,6 +121,9 @@ int log_destroy(struct log *l); */ int log_indent(int levels); +/** Disable log indention of current thread. */ +int log_noindent(); + /** A helper function the restore the previous log indention level. * * This function is usually called by a __cleanup__ handler (GCC C Extension). @@ -163,9 +165,6 @@ void log_vprint(struct log *l, const char *lvl, const char *fmt, va_list va); void debug(long lvl, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); -/** Print a horizontal line. */ -void line(); - /** Printf alike info message. */ void info(const char *fmt, ...) __attribute__ ((format(printf, 1, 2))); @@ -186,6 +185,41 @@ void error(const char *fmt, ...) void serror(const char *fmt, ...) __attribute__ ((format(printf, 1, 2))); +/** @addtogroup table Print fancy tables + * @{ + */ + +struct table_column { + int width; /**< Width of the column. */ + char *title; /**< The title as shown in the table header. */ + char *format; /**< The format which is used to print the table rows. */ + char *unit; /**< An optional unit which will be shown in the table header. */ + + enum { + TABLE_ALIGN_LEFT, + TABLE_ALIGN_RIGHT + } align; + + int _width; /**< The real width of this column. Calculated by table_header() */ +}; + +struct table { + int ncols; + int width; + struct table_column *cols; +}; + +/** Print a table header consisting of \p n columns. */ +void table_header(struct table *t); + +/** Print table rows. */ +void table_row(struct table *t, ...); + +/** Print the table footer. */ +void table_footer(struct table *t); + +/** @} */ + #ifdef __cplusplus } #endif diff --git a/include/villas/stats.h b/include/villas/stats.h index f72ae9fd8..be4d025a6 100644 --- a/include/villas/stats.h +++ b/include/villas/stats.h @@ -82,6 +82,7 @@ json_t * stats_json(struct stats *s); void stats_reset(struct stats *s); void stats_print_header(); +void stats_print_footer(); void stats_print_periodic(struct stats *s, FILE *f, enum stats_format fmt, int verbose, struct path *p); diff --git a/include/villas/utils.h b/include/villas/utils.h index e1f6032e5..d681fcb79 100644 --- a/include/villas/utils.h +++ b/include/villas/utils.h @@ -41,12 +41,6 @@ #endif /* Some color escape codes for pretty log messages */ - -/* Alternate character set */ -#define ACS(chr) "\e(0" chr "\e(B" -#define ACS_HORIZONTAL ACS("\x71") -#define ACS_VERTICAL ACS("\x78") -#define ACS_VERTRIGHT ACS("\x74") #define CLR(clr, str) "\e[" XSTR(clr) "m" str "\e[0m" #define CLR_GRY(str) CLR(30, str) /**< Print str in gray */ #define CLR_RED(str) CLR(31, str) /**< Print str in red */ @@ -58,6 +52,29 @@ #define CLR_WHT(str) CLR(37, str) /**< Print str in white */ #define CLR_BLD(str) CLR( 1, str) /**< Print str in bold */ +/* Alternate character set + * + * The suffixed of the BOX_ macro a constructed by + * combining the following letters in the written order: + * - U for a line facing upwards + * - D for a line facing downwards + * - L for a line facing leftwards + * - R for a line facing rightwards + * + * E.g. a cross can be constructed by combining all line fragments: + * BOX_UDLR + */ +#define BOX(chr) "\e(0" chr "\e(B" +#define BOX_LR BOX("\x71") /**< Boxdrawing: ─ */ +#define BOX_UD BOX("\x78") /**< Boxdrawing: │ */ +#define BOX_UDR BOX("\x74") /**< Boxdrawing: ├ */ +#define BOX_UDLR BOX("\x6E") /**< Boxdrawing: ┼ */ +#define BOX_UDL BOX("\x75") /**< Boxdrawing: ┤ */ +#define BOX_ULR BOX("\x76") /**< Boxdrawing: ┴ */ +#define BOX_UL BOX("\x6A") /**< Boxdrawing: ┘ */ +#define BOX_DLR BOX("\x77") /**< Boxdrawing: ┘ */ +#define BOX_DL BOX("\x6B") /**< Boxdrawing: ┘ */ + /* CPP stringification */ #define XSTR(x) STR(x) #define STR(x) #x diff --git a/lib/Makefile.villas-ext.inc b/lib/Makefile.villas-ext.inc index 1168f2fe2..9bf373b7c 100644 --- a/lib/Makefile.villas-ext.inc +++ b/lib/Makefile.villas-ext.inc @@ -26,7 +26,7 @@ LIBEXT = $(BUILDDIR)/$(LIBEXT_NAME).so.$(LIBEXT_ABI_VERSION) LIBEXT_SRCS += $(addprefix lib/, sample.c queue.c queue_signalled.c \ memory.c log.c shmem.c utils.c kernel/kernel.c list.c \ - timing.c pool.c \ + timing.c pool.c log_helper.c \ ) LIBEXT_LDFLAGS = -shared diff --git a/lib/Makefile.villas.inc b/lib/Makefile.villas.inc index 59849fcf1..8061ef2d0 100644 --- a/lib/Makefile.villas.inc +++ b/lib/Makefile.villas.inc @@ -31,7 +31,7 @@ LIB_SRCS += $(addprefix lib/nodes/, file.c cbuilder.c shmem.c signal.c) \ log.c log_config.c utils.c super_node.c hist.c timing.c pool.c \ list.c queue.c queue_signalled.c memory.c advio.c web.c api.c \ plugin.c node_type.c stats.c mapping.c sample_io.c shmem.c \ - json.c crypt.c compat.c \ + json.c crypt.c compat.c log_table.c log_helper.c \ ) LIB_LDFLAGS = -shared diff --git a/lib/hist.c b/lib/hist.c index 46885ec69..fc094a400 100644 --- a/lib/hist.c +++ b/lib/hist.c @@ -166,27 +166,36 @@ void hist_plot(struct hist *h) if (h->data[i] > max) max = h->data[i]; } + + struct table_column cols[] = { + { -9, "Value", "%+9.3g", NULL, TABLE_ALIGN_RIGHT }, + { -6, "Count", "%6ju", NULL, TABLE_ALIGN_RIGHT }, + { 0, "Plot", "%s", "occurences", TABLE_ALIGN_LEFT } + }; + + struct table table = { + .ncols = ARRAY_LEN(cols), + .cols = cols + }; /* Print plot */ - stats("%9s | %5s | %s", "Value", "Count", "Plot"); - line(); + table_header(&table); for (int i = 0; i < h->length; i++) { double value = VAL(h, i); hist_cnt_t cnt = h->data[i]; - int bar = HIST_HEIGHT * ((double) cnt / max); - - char *buf = 0; - buf = strcatf(&buf, "%+9.3g | %5ju | ", value, cnt); - + int bar = cols[2]._width * ((double) cnt / max); + + char *buf = strf("%s", ""); for (int i = 0; i < bar; i++) buf = strcatf(&buf, "\u2588"); - stats("%s", buf); + table_row(&table, value, cnt, buf); + free(buf); } - - line(); + + table_footer(&table); } char * hist_dump(struct hist *h) diff --git a/lib/hooks/stats_collect.c b/lib/hooks/stats_collect.c index 8fc7e7ab2..facd89570 100644 --- a/lib/hooks/stats_collect.c +++ b/lib/hooks/stats_collect.c @@ -110,8 +110,7 @@ static int stats_collect_periodic(struct hook *h) { struct stats_collect *p = h->_vd; - if (h->path->state == STATE_STARTED) - stats_print_periodic(&p->stats, p->output, p->format, p->verbose, h->path); + stats_print_periodic(&p->stats, p->output, p->format, p->verbose, h->path); return 0; } diff --git a/lib/log.c b/lib/log.c index 33b90954d..3f012adb5 100644 --- a/lib/log.c +++ b/lib/log.c @@ -22,9 +22,10 @@ #include #include +#include #include #include -#include +#include #include "config.h" #include "log.h" @@ -45,7 +46,11 @@ struct log default_log = { .facilities = LOG_ALL, .file = NULL, .path = NULL, - .epoch = { -1 , -1 } + .epoch = { -1 , -1 }, + .window = { + .ws_row = LOG_HEIGHT, + .ws_col = LOG_WIDTH + } }; /** List of debug facilities as strings */ @@ -80,8 +85,41 @@ static const char *facilities_strs[] = { /** The current log indention level (per thread!). */ static __thread int indent = 0; +int log_indent(int levels) +{ + int old = indent; + indent += levels; + return old; +} + +int log_noindent() +{ + int old = indent; + indent = 0; + return old; +} + +void log_outdent(int *old) +{ + indent = *old; +} +#endif + +static void log_resize(int signal, siginfo_t *sinfo, void *ctx) +{ + int ret; + + ret = ioctl(STDOUT_FILENO, TIOCGWINSZ, &global_log->window); + if (ret) + return; + + debug(LOG_LOG | 15, "New terminal size: %dx%x", global_log->window.ws_row, global_log->window.ws_col); +} + int log_init(struct log *l, int level, long facilitites) { + int ret; + /* Register this log instance globally */ global_log = l; @@ -89,6 +127,26 @@ int log_init(struct log *l, int level, long facilitites) l->facilities = facilitites; l->file = stderr; l->path = NULL; + l->window.ws_col = LOG_WIDTH; + l->window.ws_row = LOG_HEIGHT; + + /* Register signal handler which is called whenever the + * terminal size changes. */ + if (l->file == stderr) { + struct sigaction sa_resize = { + .sa_flags = SA_SIGINFO, + .sa_sigaction = log_resize + }; + + sigemptyset(&sa_resize.sa_mask); + + ret = sigaction(SIGWINCH, &sa_resize, NULL); + if (ret) + return ret; + + /* Try to get initial window size */ + ioctl(STDERR_FILENO, TIOCGWINSZ, &global_log->window); + } l->state = STATE_INITIALIZED; @@ -135,19 +193,6 @@ int log_destroy(struct log *l) return 0; } -int log_indent(int levels) -{ - int old = indent; - indent += levels; - return old; -} - -void log_outdent(int *old) -{ - indent = *old; -} -#endif - int log_set_facility_expression(struct log *l, const char *expression) { bool negate; @@ -212,18 +257,15 @@ void log_vprint(struct log *l, const char *lvl, const char *fmt, va_list ap) struct timespec ts = time_now(); char *buf = alloc(512); - /* Timestamp */ - strcatf(&buf, "%10.3f ", time_delta(&l->epoch, &ts)); - - /* Severity */ - strcatf(&buf, "%5s ", lvl); + /* Timestamp & Severity */ + strcatf(&buf, "%10.3f %5s ", time_delta(&l->epoch, &ts), lvl); /* Indention */ #ifdef __GNUC__ for (int i = 0; i < indent; i++) - strcatf(&buf, ACS_VERTICAL " "); + strcatf(&buf, "%s ", BOX_UD); - strcatf(&buf, ACS_VERTRIGHT " "); + strcatf(&buf, "%s ", BOX_UDR); #endif /* Format String */ @@ -234,92 +276,6 @@ void log_vprint(struct log *l, const char *lvl, const char *fmt, va_list ap) OpalPrint("VILLASnode: %s\n", buf); #endif fprintf(l->file ? l->file : stderr, "\33[2K\r%s\n", buf); - free(buf); -} - -void line() -{ - char buf[LOG_WIDTH]; - memset(buf, 0x71, sizeof(buf)); - - log_print(global_log, "", "\b" ACS("%.*s"), LOG_WIDTH, buf); -} - -void debug(long class, const char *fmt, ...) -{ - va_list ap; - - struct log *l = global_log ? global_log : &default_log; - - int lvl = class & 0xFF; - int fac = class & ~0xFF; - - if (((fac == 0) || (fac & l->facilities)) && (lvl <= l->level)) { - va_start(ap, fmt); - log_vprint(l, LOG_LVL_DEBUG, fmt, ap); - va_end(ap); - } -} - -void info(const char *fmt, ...) -{ - va_list ap; - - struct log *l = global_log ? global_log : &default_log; - - va_start(ap, fmt); - log_vprint(l, LOG_LVL_INFO, fmt, ap); - va_end(ap); -} - -void warn(const char *fmt, ...) -{ - va_list ap; - - struct log *l = global_log ? global_log : &default_log; - - va_start(ap, fmt); - log_vprint(l, LOG_LVL_WARN, fmt, ap); - va_end(ap); -} - -void stats(const char *fmt, ...) -{ - va_list ap; - - struct log *l = global_log ? global_log : &default_log; - - va_start(ap, fmt); - log_vprint(l, LOG_LVL_STATS, fmt, ap); - va_end(ap); -} - -void error(const char *fmt, ...) -{ - va_list ap; - - struct log *l = global_log ? global_log : &default_log; - - va_start(ap, fmt); - log_vprint(l, LOG_LVL_ERROR, fmt, ap); - va_end(ap); - - die(); -} - -void serror(const char *fmt, ...) -{ - va_list ap; - char *buf = NULL; - - struct log *l = global_log ? global_log : &default_log; - - va_start(ap, fmt); - vstrcatf(&buf, fmt, ap); - va_end(ap); - - log_print(l, LOG_LVL_ERROR, "%s: %m (%u)", buf, errno); free(buf); - die(); -} +} \ No newline at end of file diff --git a/lib/log_helper.c b/lib/log_helper.c new file mode 100644 index 000000000..1467605df --- /dev/null +++ b/lib/log_helper.c @@ -0,0 +1,105 @@ +/** Logging and debugging routines + * + * @author Steffen Vogel + * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC + * @license GNU General Public License (version 3) + * + * VILLASnode + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *********************************************************************************/ + +#include + +#include "utils.h" +#include "log.h" + +void debug(long class, const char *fmt, ...) +{ + va_list ap; + + struct log *l = global_log ? global_log : &default_log; + + int lvl = class & 0xFF; + int fac = class & ~0xFF; + + if (((fac == 0) || (fac & l->facilities)) && (lvl <= l->level)) { + va_start(ap, fmt); + log_vprint(l, LOG_LVL_DEBUG, fmt, ap); + va_end(ap); + } +} + +void info(const char *fmt, ...) +{ + va_list ap; + + struct log *l = global_log ? global_log : &default_log; + + va_start(ap, fmt); + log_vprint(l, LOG_LVL_INFO, fmt, ap); + va_end(ap); +} + +void warn(const char *fmt, ...) +{ + va_list ap; + + struct log *l = global_log ? global_log : &default_log; + + va_start(ap, fmt); + log_vprint(l, LOG_LVL_WARN, fmt, ap); + va_end(ap); +} + +void stats(const char *fmt, ...) +{ + va_list ap; + + struct log *l = global_log ? global_log : &default_log; + + va_start(ap, fmt); + log_vprint(l, LOG_LVL_STATS, fmt, ap); + va_end(ap); +} + +void error(const char *fmt, ...) +{ + va_list ap; + + struct log *l = global_log ? global_log : &default_log; + + va_start(ap, fmt); + log_vprint(l, LOG_LVL_ERROR, fmt, ap); + va_end(ap); + + killme(SIGALRM); +} + +void serror(const char *fmt, ...) +{ + va_list ap; + char *buf = NULL; + + struct log *l = global_log ? global_log : &default_log; + + va_start(ap, fmt); + vstrcatf(&buf, fmt, ap); + va_end(ap); + + log_print(l, LOG_LVL_ERROR, "%s: %m (%u)", buf, errno); + + free(buf); + killme(SIGALRM); +} \ No newline at end of file diff --git a/lib/log_table.c b/lib/log_table.c new file mode 100644 index 000000000..509368bbb --- /dev/null +++ b/lib/log_table.c @@ -0,0 +1,170 @@ +/** Print fancy tables. + * + * @author Steffen Vogel + * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC + * @license GNU General Public License (version 3) + * + * VILLASnode + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *********************************************************************************/ + +#include +#include + +#include "utils.h" +#include "log.h" + +static int table_resize(struct table *t, int width) +{ + int norm, flex, fixed, total; + + t->width = width; + + norm = 0; + flex = 0; + fixed = 0; + total = t->width - t->ncols * 2; + + /* Normalize width */ + for (int i = 0; i < t->ncols; i++) { + if (t->cols[i].width > 0) + norm += t->cols[i].width; + if (t->cols[i].width == 0) + flex++; + if (t->cols[i].width < 0) + fixed += -1 * t->cols[i].width; + } + + for (int i = 0; i < t->ncols; i++) { + if (t->cols[i].width > 0) + t->cols[i]._width = t->cols[i].width * (float) (total - fixed) / norm; + if (t->cols[i].width == 0) + t->cols[i]._width = (float) (total - fixed) / flex; + if (t->cols[i].width < 0) + t->cols[i]._width = -1 * t->cols[i].width; + } + + return 0; +} + +void table_header(struct table *t) +{ NOINDENT + struct log *l = global_log ? global_log : &default_log; + + if (t->width != l->window.ws_col - 24) + table_resize(t, l->window.ws_col - 24); + + char *line1 = strf("\b\b" BOX_UD); + char *line0 = strf("\b"); + char *line2 = strf("\b"); + + for (int i = 0; i < t->ncols; i++) { + char *col = strf(CLR_BLD("%s"), t->cols[i].title); + + if (t->cols[i].unit) + strcatf(&col, " (" CLR_YEL("%s") ")", t->cols[i].unit); + + int l = strlenp(col); + int r = strlen(col); + int w = t->cols[i]._width + r - l; + + if (t->cols[i].align == TABLE_ALIGN_LEFT) + strcatf(&line1, " %-*.*s " BOX_UD, w, w, col); + else + strcatf(&line1, " %*.*s " BOX_UD, w, w, col); + + for (int j = 0; j < t->cols[i]._width + 2; j++) { + strcatf(&line0, "%s", BOX_LR); + strcatf(&line2, "%s", BOX_LR); + } + + if (i == t->ncols - 1) { + strcatf(&line0, "%s", BOX_DL); + strcatf(&line2, "%s", BOX_UDL); + } + else { + strcatf(&line0, "%s", BOX_DLR); + strcatf(&line2, "%s", BOX_UDLR); + } + + free(col); + } + + stats(line0); + stats(line1); + stats(line2); + + free(line0); + free(line1); + free(line2); +} + +void table_row(struct table *t, ...) +{ NOINDENT + struct log *l = global_log ? global_log : &default_log; + + if (t->width != l->window.ws_col - 24) { + table_resize(t, l->window.ws_col - 24); + table_header(t); + } + + va_list args; + va_start(args, t); + + char *line = strf("\b\b" BOX_UD); + + for (int i = 0; i < t->ncols; ++i) { + char *col = vstrf(t->cols[i].format, args); + + int l = strlenp(col); + int r = strlen(col); + int w = t->cols[i]._width + r - l; + + if (t->cols[i].align == TABLE_ALIGN_LEFT) + strcatf(&line, " %-*.*s " BOX_UD, w, w, col); + else + strcatf(&line, " %*.*s " BOX_UD, w, w, col); + + free(col); + } + + va_end(args); + + stats(line); + free(line); +} + +void table_footer(struct table *t) +{ NOINDENT + struct log *l = global_log ? global_log : &default_log; + + if (t->width != l->window.ws_col - 24) + table_resize(t, l->window.ws_col - 24); + + char *line = strf("\b"); + + for (int i = 0; i < t->ncols; i++) { + for (int j = 0; j < t->cols[i]._width + 2; j++) + strcatf(&line, BOX_LR); + + if (i == t->ncols - 1) + strcatf(&line, "%s", BOX_UL); + else + strcatf(&line, "%s", BOX_ULR); + } + + stats(line); + free(line); +} \ No newline at end of file diff --git a/lib/stats.c b/lib/stats.c index de0b2005a..02cf2bf62 100644 --- a/lib/stats.c +++ b/lib/stats.c @@ -34,7 +34,7 @@ static struct stats_desc { const char *unit; const char *desc; int hist_buckets; -} stats_table[] = { +} stats_metrics[] = { { "skipped", "samples", "skipped samples by hooks", 25 }, { "reorderd", "samples", "reordered samples", 25 }, { "gap_sample", "seconds", "inter message timestamps (as sent by remote)", 25 }, @@ -116,7 +116,7 @@ json_t * stats_json(struct stats *s) json_t *obj = json_object(); for (int i = 0; i < STATS_COUNT; i++) { - struct stats_desc *desc = &stats_table[i]; + struct stats_desc *desc = &stats_metrics[i]; json_t *stats = hist_json(&s->histograms[i]); @@ -145,19 +145,36 @@ void stats_reset(struct stats *s) } } +static struct table_column stats_cols[] = { + { 35, "Path", "%s", NULL, TABLE_ALIGN_LEFT }, + { 10, "Cnt", "%ju", "p", TABLE_ALIGN_RIGHT }, + { 10, "OWD", "%f", "S", TABLE_ALIGN_RIGHT }, + { 10, "Rate", "%f", "p/S", TABLE_ALIGN_RIGHT }, + { 10, "Drop", "%ju", "p", TABLE_ALIGN_RIGHT }, + { 10, "Skip", "%ju", "p", TABLE_ALIGN_RIGHT } +}; + +static struct table stats_table = { + .ncols = ARRAY_LEN(stats_cols), + .cols = stats_cols +}; + void stats_print_header(enum stats_format fmt) { - #define UNIT(u) "(" YEL(u) ")" - switch (fmt) { case STATS_FORMAT_HUMAN: - stats("%-40s|%19s|%19s|%19s|%19s|", "Source " MAG("=>") " Destination", - "OWD" UNIT("S") " ", - "Rate" UNIT("p/S") " ", - "Drop" UNIT("p") " ", - "Skip" UNIT("p") " " - ); - line(); + table_header(&stats_table); + break; + + default: { } + } +} + +void stats_print_footer(enum stats_format fmt) +{ + switch (fmt) { + case STATS_FORMAT_HUMAN: + table_footer(&stats_table); break; default: { } @@ -168,7 +185,9 @@ void stats_print_periodic(struct stats *s, FILE *f, enum stats_format fmt, int v { switch (fmt) { case STATS_FORMAT_HUMAN: - stats("%-40.40s|%10f|%10f|%10ju|%10ju|", path_name(p), + table_row(&stats_table, + path_name(p), + s->histograms[STATS_OWD].total, s->histograms[STATS_OWD].last, 1.0 / s->histograms[STATS_GAP_SAMPLE].last, s->histograms[STATS_REORDERED].total, @@ -191,7 +210,7 @@ void stats_print(struct stats *s, FILE *f, enum stats_format fmt, int verbose) switch (fmt) { case STATS_FORMAT_HUMAN: for (int i = 0; i < STATS_COUNT; i++) { - struct stats_desc *desc = &stats_table[i]; + struct stats_desc *desc = &stats_metrics[i]; stats("%s: %s", desc->name, desc->desc); hist_print(&s->histograms[i], verbose); @@ -231,7 +250,7 @@ void stats_send(struct stats *s, struct node *n) enum stats_id stats_lookup_id(const char *name) { for (int i = 0; i < STATS_COUNT; i++) { - struct stats_desc *desc = &stats_table[i]; + struct stats_desc *desc = &stats_metrics[i]; if (!strcmp(desc->name, name)) return i; diff --git a/src/node.c b/src/node.c index f6e8ed2cc..1ad04378a 100644 --- a/src/node.c +++ b/src/node.c @@ -47,6 +47,9 @@ struct super_node sn; static void quit(int signal, siginfo_t *sinfo, void *ctx) { + if (sn.stats > 0) + stats_print_footer(STATS_FORMAT_HUMAN); + super_node_stop(&sn); super_node_destroy(&sn); @@ -119,7 +122,7 @@ int main(int argc, char *argv[]) super_node_start(&sn); if (sn.stats > 0) - stats_print_header(); + stats_print_header(STATS_FORMAT_HUMAN); struct timespec now, last = time_now();