From 735f08f55121026662e57b2ec748e029f52f20af Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Mon, 7 Nov 2016 22:18:26 -0500 Subject: [PATCH] more improvements to make statistic subsystem extensible --- include/villas/stats.h | 38 +++++---- lib/hooks/hooks-internal.c | 2 +- lib/stats.c | 162 ++++++++++++++++++++++--------------- 3 files changed, 124 insertions(+), 78 deletions(-) diff --git a/include/villas/stats.h b/include/villas/stats.h index e6260833b..49d6e11ef 100644 --- a/include/villas/stats.h +++ b/include/villas/stats.h @@ -23,20 +23,24 @@ struct sample; struct path; struct node; -struct stats { - struct { - uintmax_t invalid; /**< Counter for invalid messages */ - uintmax_t skipped; /**< Counter for skipped messages due to hooks */ - uintmax_t dropped; /**< Counter for dropped messages due to reordering */ - } counter; +enum stats_id { + STATS_INVALID, /**< Counter for invalid messages */ + STATS_SKIPPED, /**< Counter for skipped messages due to hooks */ + STATS_DROPPED, /**< Counter for dropped messages due to reordering */ + STATS_GAP_SEQUENCE, /**< Histogram of sequence number displacement of received messages */ + STATS_GAP_SAMPLE, /**< Histogram for inter message timestamps (as sent by remote) */ + STATS_GAP_RECEIVED, /**< Histogram for inter message arrival time (as seen by this instance) */ + STATS_OWD, /**< Histogram for one-way-delay (OWD) of received messages */ + STATS_COUNT /**< Just here to have an updated number of statistics */ +}; + +struct stats_delta { + double vals[STATS_COUNT]; +}; + +struct stats { + struct hist histograms[STATS_COUNT]; - struct { - struct hist owd; /**< Histogram for one-way-delay (OWD) of received messages */ - struct hist gap_msg; /**< Histogram for inter message timestamps (as sent by remote) */ - struct hist gap_recv; /**< Histogram for inter message arrival time (as seen by this instance) */ - struct hist gap_seq; /**< Histogram of sequence number displacement of received messages */ - } histogram; - struct sample *last; }; @@ -44,8 +48,14 @@ int stats_init(struct stats *s); void stats_destroy(struct stats *s); +void stats_update(struct stats *s, enum stats_id id, double val); + +int stats_commit(struct stats *s, struct stats_delta *d); + void stats_collect(struct stats *s, struct sample *smps[], size_t cnt); +void stats_decollect(struct stats *s, struct sample *smps[], size_t cnt); + #ifdef WITH_JANSSON json_t * stats_json(struct stats *s); #endif @@ -56,7 +66,7 @@ void stats_print_header(); void stats_print_periodic(struct stats *s, struct path *p); -void stats_print(struct stats *s); +void stats_print(struct stats *s, int details); void stats_send(struct stats *s, struct node *n); diff --git a/lib/hooks/hooks-internal.c b/lib/hooks/hooks-internal.c index d10efb92e..cb91505fd 100644 --- a/lib/hooks/hooks-internal.c +++ b/lib/hooks/hooks-internal.c @@ -85,7 +85,7 @@ int hook_drop(struct hook *h, int when, struct hook_info *j) if (h->prev) { dist = h->last->sequence - (int32_t) h->prev->sequence; if (dist <= 0) { - j->path->stats->counter.dropped++; + stats_update(j->path->stats, STATS_DROPPED, dist); warn("Dropped sample: dist = %d, i = %d", dist, i); } else { diff --git a/lib/stats.c b/lib/stats.c index f25675d1d..5bcceee21 100644 --- a/lib/stats.c +++ b/lib/stats.c @@ -13,38 +13,81 @@ #include "sample.h" #include "log.h" +static struct stats_desc { + const char *name; + const char *unit; + const char *desc; + struct { + double min; + double max; + double resolution; + } hist; +} stats_table[] = { + { "invalid", "", "invalid messages", {0, 0, -1, }}, + { "skipped", "", "skipped messages by hooks", {0, 0, -1, }}, + { "dropped", "", "dropped messages because of reordering", {0, 0, -1, }}, + { "gap_sequence", "", "sequence number displacement of received messages", {-10, 10, 20, }}, + { "gap_sample", "", "inter message timestamps (as sent by remote)", {90e-3, 110e-3, 1e-3, }}, + { "gap_received", "", "Histogram for inter message arrival time (as seen by this instance)", {90e-3, 110e-3, 1e-3, }}, + { "owd", "s", "Histogram for one-way-delay (OWD) of received messages", {0, 1, 100e-3, }} +}; + int stats_init(struct stats *s) { - /** @todo Allow configurable bounds for histograms */ - hist_create(&s->histogram.owd, 0, 1, 100e-3); - hist_create(&s->histogram.gap_msg, 90e-3, 110e-3, 1e-3); - hist_create(&s->histogram.gap_recv, 90e-3, 110e-3, 1e-3); - hist_create(&s->histogram.gap_seq, -HIST_SEQ, +HIST_SEQ, 1); - + for (int i = 0; i < STATS_COUNT; i++) { + struct stats_desc *desc = &stats_table[i]; + hist_create(&s->histograms[i], desc->hist.min, desc->hist.max, desc->hist.resolution); + } + return 0; } void stats_destroy(struct stats *s) { - hist_destroy(&s->histogram.owd); - hist_destroy(&s->histogram.gap_msg); - hist_destroy(&s->histogram.gap_recv); - hist_destroy(&s->histogram.gap_seq); + for (int i = 0; i < STATS_COUNT; i++) { + hist_destroy(&s->histograms[i]); + } +} + +void stats_update(struct stats *s, enum stats_id id, double val) +{ + if (!s) + return; + + hist_put(&s->histograms[id], val); +} + +#if 0 +int stats_delta(struct stats_delta *d, struct sample *s, struct sample *p) +{ + d->histogram.owd = time_delta(&smps[i]->ts.origin, &smps[i]->ts.received); + d->histogram.gap = time_delta(&s->last->ts.origin, &smps[i]->ts.origin); + d->histogram.gap_seq = s->sequence - (int32_t) p->sequence; + d->histogram.gap_recv = time_delta(&s->last->ts.received, &smps[i]->ts.received); + + d->counter.dropped = d->histogram.gap_seq <= 0 ? 1 : 0; + d->counter.invalid = 0; + + return 0; +} +#endif + +int stats_commit(struct stats *s, struct stats_delta *d) +{ + for (int i = 0; i < STATS_COUNT; i++) { + hist_put(&s->histograms[i], d->vals[i]); + } + + return 0; } void stats_collect(struct stats *s, struct sample *smps[], size_t cnt) { for (int i = 0; i < cnt; i++) { if (s->last) { - int gap_seq = smps[i]->sequence - (int32_t) s->last->sequence; - double owd = time_delta(&smps[i]->ts.origin, &smps[i]->ts.received); - double gap = time_delta(&s->last->ts.origin, &smps[i]->ts.origin); - double gap_recv = time_delta(&s->last->ts.received, &smps[i]->ts.received); - - hist_put(&s->histogram.owd, owd); - hist_put(&s->histogram.gap_msg, gap); - hist_put(&s->histogram.gap_seq, gap_seq); - hist_put(&s->histogram.gap_recv, gap_recv); +// struct stats_delta d; +// stats_get_delta(&d, smps[i], s->last); +// stats_commit(s, &d); } if (i == 0 && s->last) @@ -59,30 +102,25 @@ void stats_collect(struct stats *s, struct sample *smps[], size_t cnt) #ifdef WITH_JANSSON json_t * stats_json(struct stats *s) { - return json_pack("{ s: { s: i, s: i, s: i }, s: { s: o, s: o, s: o } }", - "counter", - "dropped", s->counter.dropped, - "invalid", s->counter.invalid, - "skipped", s->counter.skipped, - "histogram", - "owd", hist_json(&s->histogram.owd), - "gap_msg", hist_json(&s->histogram.gap_msg), - "gap_recv",hist_json(&s->histogram.gap_recv), - "gap_seq", hist_json(&s->histogram.gap_seq) - ); + json_t *obj = json_object(); + + for (int i = 0; i < STATS_COUNT; i++) { + struct stats_desc *desc = &stats_table[i]; + + json_t *stats = hist_json(&s->histograms[i]); + + json_object_set(obj, desc->name, stats); + } + + return obj; } #endif void stats_reset(struct stats *s) { - s->counter.invalid = - s->counter.skipped = - s->counter.dropped = 0; - - hist_reset(&s->histogram.owd); - hist_reset(&s->histogram.gap_seq); - hist_reset(&s->histogram.gap_msg); - hist_reset(&s->histogram.gap_recv); + for (int i = 0; i < STATS_COUNT; i++) { + hist_reset(&s->histograms[i]); + } } void stats_print_header() @@ -101,41 +139,39 @@ void stats_print_header() void stats_print_periodic(struct stats *s, struct path *p) { - stats("%-40.40s|%10s|%10s|%10ju|%10ju|%10ju|", path_name(p), "", "", - s->counter.dropped, s->counter.skipped, s->counter.invalid); + stats("%-40.40s|%10f|%10f|%10ju|%10ju|%10ju|", path_name(p), + s->histograms[STATS_OWD].last, + 1.0 / s->histograms[STATS_GAP_SAMPLE].last, + s->histograms[STATS_DROPPED].total, + s->histograms[STATS_SKIPPED].total, + s->histograms[STATS_INVALID].total + ); } -void stats_print(struct stats *s) +void stats_print(struct stats *s, int details) { - stats("Dropped samples: %ju", s->counter.dropped); - stats("Skipped samples: %ju", s->counter.skipped); - stats("Invalid samples: %ju", s->counter.invalid); - - stats("One-way delay:"); - hist_print(&s->histogram.owd); - - stats("Inter-message arrival time:"); - hist_print(&s->histogram.gap_recv); - - stats("Inter-message ts gap:"); - hist_print(&s->histogram.gap_msg); - - stats("Inter-message sequence number gaps:"); - hist_print(&s->histogram.gap_seq); + for (int i = 0; i < STATS_COUNT; i++) { + struct stats_desc *desc = &stats_table[i]; + + stats("%s: %s", desc->name, desc->desc); + hist_print(&s->histograms[i], details); + } } void stats_send(struct stats *s, struct node *n) { - char buf[SAMPLE_LEN(16)]; + char buf[SAMPLE_LEN(STATS_COUNT * 5)]; struct sample *smp = (struct sample *) buf; int i = 0; - smp->data[i++].f = s->counter.invalid; /**< Use integer here? */ - smp->data[i++].f = s->counter.skipped; - smp->data[i++].f = s->counter.dropped; - smp->data[i++].f = s->histogram.owd.last, - smp->data[i++].f = 1.0 / s->histogram.gap_msg.last; - smp->data[i++].f = 1.0 / s->histogram.gap_recv.last; + + for (int j = 0; j < STATS_COUNT; j++) { + smp->data[i++].f = s->histograms[j].last; + smp->data[i++].f = s->histograms[j].highest; + smp->data[i++].f = s->histograms[j].lowest; + smp->data[i++].f = hist_mean(&s->histograms[j]); + smp->data[i++].f = hist_var(&s->histograms[j]); + } smp->length = i; node_write(n, &smp, 1); /* Send single message with statistics to destination node */