mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
log: added new module to print fancy tables and use it for histograms as well as periodc stats
This commit is contained in:
parent
2af83114bf
commit
cc6f6a6132
13 changed files with 471 additions and 157 deletions
3
config.h
3
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
|
||||
|
|
|
@ -29,14 +29,17 @@ extern "C" {
|
|||
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
29
lib/hist.c
29
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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
174
lib/log.c
174
lib/log.c
|
@ -22,9 +22,10 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
}
|
105
lib/log_helper.c
Normal file
105
lib/log_helper.c
Normal file
|
@ -0,0 +1,105 @@
|
|||
/** Logging and debugging routines
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#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);
|
||||
}
|
170
lib/log_table.c
Normal file
170
lib/log_table.c
Normal file
|
@ -0,0 +1,170 @@
|
|||
/** Print fancy tables.
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
47
lib/stats.c
47
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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue