1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00

stats: port to C++

This commit is contained in:
Steffen Vogel 2019-06-23 13:35:42 +02:00
parent f05b2ae952
commit c842914bc5
16 changed files with 318 additions and 343 deletions

View file

@ -25,7 +25,7 @@
#include <jansson.h>
#include <villas/stats.h>
#include <villas/stats.hpp>
#include <villas/common.h>
/* Forward declarations */
@ -70,8 +70,8 @@ struct mapping_entry {
} data;
struct {
enum stats_metric metric;
enum stats_type type;
enum villas::Stats::Metric metric;
enum villas::Stats::Type type;
} stats;
struct {

View file

@ -37,6 +37,7 @@
#include <villas/sample.h>
#include <villas/list.h>
#include <villas/queue.h>
#include <villas/stats.hpp>
#include <villas/common.h>
#if defined(LIBNL3_ROUTE_FOUND) && defined(__linux__)
@ -67,7 +68,7 @@ struct node {
uint64_t sequence; /**< This is a counter of received samples, in case the node-type does not generate sequence numbers itself. */
struct stats *stats; /**< Statistic counters. This is a pointer to the statistic hooks private data. */
villas::Stats *stats; /**< Statistic counters. This is a pointer to the statistic hooks private data. */
struct node_direction in, out;

View file

@ -32,7 +32,7 @@
#include <jansson.h>
#include <villas/node.h>
#include <villas/stats.h>
#include <villas/stats.hpp>
#include <villas/task.h>
#include <villas/list.h>
@ -40,8 +40,8 @@ struct stats_node_signal {
struct node *node;
char *node_str;
enum stats_metric metric;
enum stats_type type;
enum villas::Stats::Metric metric;
enum villas::Stats::Type type;
};
struct stats_node {

View file

@ -1,120 +0,0 @@
/** Statistic collection.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2019, 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/>.
*********************************************************************************/
#pragma once
#include <stdint.h>
#include <jansson.h>
#include <villas/common.h>
#include <villas/hist.hpp>
#include <villas/signal.h>
/* Forward declarations */
struct sample;
struct node;
enum stats_format {
STATS_FORMAT_HUMAN,
STATS_FORMAT_JSON,
STATS_FORMAT_MATLAB
};
enum stats_metric {
STATS_METRIC_INVALID = -1,
STATS_METRIC_SMPS_SKIPPED, /**< Counter for skipped samples due to hooks. */
STATS_METRIC_SMPS_REORDERED, /**< Counter for reordered samples. */
/* Timings */
STATS_METRIC_GAP_SAMPLE, /**< Histogram for inter sample timestamps (as sent by remote). */
STATS_METRIC_GAP_RECEIVED, /**< Histogram for inter sample arrival time (as seen by this instance). */
STATS_METRIC_OWD, /**< Histogram for one-way-delay (OWD) of received samples. */
STATS_METRIC_AGE, /**< Processing time of packets within VILLASnode. */
/* RTP metrics */
STATS_METRIC_RTP_LOSS_FRACTION, /**< Fraction lost since last RTP SR/RR. */
STATS_METRIC_RTP_PKTS_LOST, /**< Cumul. no. pkts lost. */
STATS_METRIC_RTP_JITTER, /**< Interarrival jitter. */
/* Always last */
STATS_METRIC_COUNT /**< Just here to have an updated number of statistics. */
};
enum stats_type {
STATS_TYPE_INVALID = -1,
STATS_TYPE_LAST,
STATS_TYPE_HIGHEST,
STATS_TYPE_LOWEST,
STATS_TYPE_MEAN,
STATS_TYPE_VAR,
STATS_TYPE_STDDEV,
STATS_TYPE_TOTAL,
STATS_TYPE_COUNT
};
struct stats_metric_description {
const char *name;
enum stats_metric metric;
const char *unit;
const char *desc;
};
struct stats_type_description {
const char *name;
enum stats_type type;
enum signal_type signal_type;
};
struct stats {
enum state state;
villas::Hist histograms[STATS_METRIC_COUNT];
};
extern struct stats_metric_description stats_metrics[];
extern struct stats_type_description stats_types[];
int stats_lookup_format(const char *str);
enum stats_metric stats_lookup_metric(const char *str);
enum stats_type stats_lookup_type(const char *str);
int stats_init(struct stats *s, int buckets, int warmup);
int stats_destroy(struct stats *s);
void stats_update(struct stats *s, enum stats_metric id, double val);
json_t * stats_json(struct stats *s);
void stats_reset(struct stats *s);
void stats_print_header(enum stats_format fmt);
void stats_print_periodic(struct stats *s, FILE *f, enum stats_format fmt, struct node *p);
void stats_print(struct stats *s, FILE *f, enum stats_format fmt, int verbose);
union signal_data stats_get_value(const struct stats *s, enum stats_metric sm, enum stats_type st);

132
include/villas/stats.hpp Normal file
View file

@ -0,0 +1,132 @@
/** Statistic collection.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2019, 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/>.
*********************************************************************************/
#pragma once
#include <cstdint>
#include <jansson.h>
#include <unordered_map>
#include <memory>
#include <string>
#include <villas/common.h>
#include <villas/hist.hpp>
#include <villas/table.hpp>
#include <villas/signal.h>
/* Forward declarations */
struct sample;
struct node;
namespace villas {
class Stats {
public:
enum class Format {
HUMAN,
JSON,
MATLAB
};
enum class Metric {
SMPS_SKIPPED, /**< Counter for skipped samples due to hooks. */
SMPS_REORDERED, /**< Counter for reordered samples. */
/* Timings */
GAP_SAMPLE, /**< Histogram for inter sample timestamps (as sent by remote). */
GAP_RECEIVED, /**< Histogram for inter sample arrival time (as seen by this instance). */
OWD, /**< Histogram for one-way-delay (OWD) of received samples. */
AGE, /**< Processing time of packets within VILLASnode. */
/* RTP metrics */
RTP_LOSS_FRACTION, /**< Fraction lost since last RTP SR/RR. */
RTP_PKTS_LOST, /**< Cumul. no. pkts lost. */
RTP_JITTER /**< Interarrival jitter. */
};
enum class Type {
LAST,
HIGHEST,
LOWEST,
MEAN,
VAR,
STDDEV,
TOTAL
};
protected:
std::unordered_map<Metric, villas::Hist> histograms;
struct MetricDescription {
const char *name;
const char *unit;
const char *desc;
};
struct TypeDescription {
const char *name;
enum signal_type signal_type;
};
static std::shared_ptr<Table> table;
static void setupTable();
public:
Stats(int buckets, int warmup);
static
enum Format lookupFormat(const std::string &str);
static
enum Metric lookupMetric(const std::string &str);
static
enum Type lookupType(const std::string &str);
void update(enum Metric id, double val);
void reset();
json_t * toJson() const;
static
void printHeader(enum Format fmt);
void printPeriodic(FILE *f, enum Format fmt, struct node *p) const;
void print(FILE *f, enum Format fmt, int verbose) const;
union signal_data getValue(enum Metric sm, enum Type st) const;
const villas::Hist & getHistogram(enum Metric sm) const;
static std::unordered_map<Metric, MetricDescription> metrics;
static std::unordered_map<Type, TypeDescription> types;
static std::vector<TableColumn> columns;
};
} /* namespace villas */

View file

@ -25,7 +25,7 @@
#include <villas/super_node.hpp>
#include <villas/node.h>
#include <villas/utils.hpp>
#include <villas/stats.h>
#include <villas/stats.hpp>
#include <villas/api/action.hpp>
#include <villas/api/session.hpp>
@ -58,7 +58,7 @@ public:
);
if (n->stats)
json_object_set_new(json_node, "stats", stats_json(n->stats));
json_object_set_new(json_node, "stats", n->stats->toJson());
/* Add all additional fields of node here.
* This can be used for metadata */

View file

@ -24,7 +24,7 @@
#include <villas/log.h>
#include <villas/node.h>
#include <villas/stats.h>
#include <villas/stats.hpp>
#include <villas/super_node.hpp>
#include <villas/utils.hpp>
#include <villas/api/session.hpp>
@ -58,7 +58,7 @@ public:
if (n->stats) {
if (reset) {
stats_reset(n->stats);
n->stats->reset();
info("Stats resetted for node %s", node_name(n));
}
}

View file

@ -30,7 +30,7 @@
#include <villas/advio.h>
#include <villas/hook.hpp>
#include <villas/node/exceptions.hpp>
#include <villas/stats.h>
#include <villas/stats.hpp>
#include <villas/node.h>
#include <villas/timing.h>
@ -53,11 +53,11 @@ public:
virtual int process(sample *smp)
{
stats *s = node->stats;
Stats *s = node->stats;
timespec now = time_now();
stats_update(s, STATS_METRIC_AGE, time_delta(&smp->ts.received, &now));
s->update(Stats::Metric::AGE, time_delta(&smp->ts.received, &now));
return HOOK_OK;
}
@ -96,22 +96,22 @@ public:
virtual int process(sample *smp)
{
stats *s = node->stats;
Stats *s = node->stats;
if (last) {
if (smp->flags & last->flags & SAMPLE_HAS_TS_RECEIVED)
stats_update(s, STATS_METRIC_GAP_RECEIVED, time_delta(&last->ts.received, &smp->ts.received));
s->update(Stats::Metric::GAP_RECEIVED, time_delta(&last->ts.received, &smp->ts.received));
if (smp->flags & last->flags & SAMPLE_HAS_TS_ORIGIN)
stats_update(s, STATS_METRIC_GAP_SAMPLE, time_delta(&last->ts.origin, &smp->ts.origin));
s->update(Stats::Metric::GAP_SAMPLE, time_delta(&last->ts.origin, &smp->ts.origin));
if ((smp->flags & SAMPLE_HAS_TS_ORIGIN) && (smp->flags & SAMPLE_HAS_TS_RECEIVED))
stats_update(s, STATS_METRIC_OWD, time_delta(&smp->ts.origin, &smp->ts.received));
s->update(Stats::Metric::OWD, time_delta(&smp->ts.origin, &smp->ts.received));
if (smp->flags & last->flags & SAMPLE_HAS_SEQUENCE) {
int dist = smp->sequence - (int32_t) last->sequence;
if (dist != 1)
stats_update(s, STATS_METRIC_SMPS_REORDERED, dist);
s->update(Stats::Metric::SMPS_REORDERED, dist);
}
}
@ -129,12 +129,12 @@ public:
class StatsHook : public Hook {
protected:
struct stats stats;
Stats stats;
StatsReadHook *readHook;
StatsWriteHook *writeHook;
enum stats_format format;
enum Stats::Format format;
int verbose;
int warmup;
int buckets;
@ -146,20 +146,14 @@ public:
StatsHook(struct path *p, struct node *n, int fl, int prio, bool en = true) :
Hook(p, n, fl, prio, en),
format(STATS_FORMAT_HUMAN),
stats(buckets, warmup),
format(Stats::Format::HUMAN),
verbose(0),
warmup(500),
buckets(20),
output(nullptr),
uri(nullptr)
{
int ret;
stats.state = STATE_DESTROYED;
ret = stats_init(&stats, buckets, warmup);
if (ret)
throw RuntimeError("Failed to initialize stats");
/* Register statistic object to path.
*
* This allows the path code to update statistics. */
@ -178,8 +172,7 @@ public:
if (uri)
free(uri);
if (stats.state != STATE_DESTROYED)
stats_destroy(&stats);
stats.~Stats();
}
virtual void start()
@ -199,7 +192,7 @@ public:
{
assert(state == STATE_STARTED);
stats_print(&stats, uri ? output->file : stdout, format, verbose);
stats.print(uri ? output->file : stdout, format, verbose);
if (uri)
afclose(output);
@ -211,19 +204,19 @@ public:
{
assert(state == STATE_STARTED);
stats_reset(&stats);
stats.reset();
}
virtual void periodic()
{
assert(state == STATE_STARTED);
stats_print_periodic(&stats, uri ? output->file : stdout, format, node);
stats.printPeriodic(uri ? output->file : stdout, format, node);
}
virtual void parse(json_t *cfg)
{
int ret, fmt;
int ret;
json_error_t err;
assert(state != STATE_STARTED);
@ -242,11 +235,11 @@ public:
throw ConfigError(cfg, err, "node-config-hook-stats");
if (f) {
fmt = stats_lookup_format(f);
if (fmt < 0)
try {
format = Stats::lookupFormat(f);
} catch (std::invalid_argument &e) {
throw ConfigError(cfg, "node-config-hook-stats", "Invalid statistic output format: {}", f);
format = static_cast<stats_format>(fmt);
}
}
if (u)

View file

@ -29,6 +29,7 @@
#include <villas/node.h>
#include <villas/signal.h>
using namespace villas;
using namespace villas::utils;
int mapping_parse_str(struct mapping_entry *me, const char *str, struct vlist *nodes)
@ -76,13 +77,8 @@ int mapping_parse_str(struct mapping_entry *me, const char *str, struct vlist *n
if (!type)
goto invalid_format;
me->stats.metric = stats_lookup_metric(metric);
if (me->stats.metric < 0)
goto invalid_format;
me->stats.type = stats_lookup_type(type);
if (me->stats.type < 0)
goto invalid_format;
me->stats.metric = Stats::lookupMetric(metric);
me->stats.type = Stats::lookupType(type);
}
else if (!strcmp(type, "hdr")) {
me->type = MAPPING_TYPE_HEADER;
@ -245,7 +241,7 @@ int mapping_update(const struct mapping_entry *me, struct sample *remapped, cons
switch (me->type) {
case MAPPING_TYPE_STATS:
remapped->data[me->offset] = stats_get_value(me->node->stats, me->stats.metric, me->stats.type);
remapped->data[me->offset] = me->node->stats->getValue(me->stats.metric, me->stats.type);
break;
case MAPPING_TYPE_TIMESTAMP: {
@ -350,8 +346,8 @@ int mapping_to_str(const struct mapping_entry *me, unsigned index, char **str)
switch (me->type) {
case MAPPING_TYPE_STATS:
strcatf(str, "stats.%s.%s",
stats_metrics[me->stats.metric].name,
stats_types[me->stats.type].name
Stats::metrics[me->stats.metric].name,
Stats::types[me->stats.type].name
);
break;

View file

@ -43,6 +43,7 @@
#include <villas/kernel/tc_netem.h>
#endif /* WITH_NETEM */
using namespace villas;
using namespace villas::utils;
int node_init(struct node *n, struct node_type *vt)
@ -427,7 +428,7 @@ int node_read(struct node *n, struct sample *smps[], unsigned cnt, unsigned *rel
int skipped = nread - rread;
if (skipped > 0 && n->stats != nullptr) {
stats_update(n->stats, STATS_METRIC_SMPS_SKIPPED, skipped);
n->stats->update(Stats::Metric::SMPS_SKIPPED, skipped);
}
debug(LOG_NODE | 5, "Received %u samples from node %s of which %d have been skipped", nread, node_name(n), skipped);

View file

@ -42,7 +42,7 @@ extern "C" {
#include <villas/plugin.h>
#include <villas/nodes/socket.hpp>
#include <villas/utils.hpp>
#include <villas/stats.h>
#include <villas/stats.hpp>
#include <villas/hook.h>
#include <villas/format_type.h>
#include <villas/super_node.hpp>
@ -53,6 +53,7 @@ extern "C" {
static pthread_t re_pthread;
using namespace villas;
using namespace villas::node;
using namespace villas::utils;
@ -300,9 +301,9 @@ static void rtcp_handler(const struct sa *src, struct rtcp_msg *msg, void *arg)
rtp_aimd(n, loss_frac);
if (n->stats) {
stats_update(n->stats, STATS_METRIC_RTP_PKTS_LOST, rr->lost);
stats_update(n->stats, STATS_METRIC_RTP_LOSS_FRACTION, loss_frac);
stats_update(n->stats, STATS_METRIC_RTP_JITTER, rr->jitter);
n->stats->update(Stats::Metric::RTP_PKTS_LOST, rr->lost);
n->stats->update(Stats::Metric::RTP_LOSS_FRACTION, loss_frac);
n->stats->update(Stats::Metric::RTP_JITTER, rr->jitter);
}
r->logger->info("RTCP: rr: num_rrs={}, loss_frac={}, pkts_lost={}, jitter={}", r->rtcp.num_rrs, loss_frac, rr->lost, rr->jitter);

View file

@ -25,13 +25,12 @@
#include <villas/nodes/stats.hpp>
#include <villas/hook.h>
#include <villas/plugin.h>
#include <villas/stats.h>
#include <villas/stats.hpp>
#include <villas/super_node.hpp>
#include <villas/sample.h>
#include <villas/node.h>
#define STATS_METRICS 6
using namespace villas;
using namespace villas::node;
using namespace villas::utils;
@ -72,13 +71,8 @@ int stats_node_signal_parse(struct stats_node_signal *s, json_t *cfg)
if (!type)
goto invalid_format;
s->metric = stats_lookup_metric(metric);
if (s->metric < 0)
goto invalid_format;
s->type = stats_lookup_type(type);
if (s->type < 0)
goto invalid_format;
s->metric = Stats::lookupMetric(metric);
s->type = Stats::lookupType(type);
s->node_str = strdup(node);
@ -196,16 +190,16 @@ int stats_node_parse(struct node *n, json_t *cfg)
error("Failed to parse statistics signal definition of node %s", node_name(n));
if (!sig->name) {
const char *metric = stats_metrics[stats_sig->metric].name;
const char *type = stats_types[stats_sig->type].name;
const char *metric = Stats::metrics[stats_sig->metric].name;
const char *type = Stats::types[stats_sig->type].name;
sig->name = strf("%s.%s.%s", stats_sig->node_str, metric, type);
}
if (!sig->unit)
sig->unit = strdup(stats_metrics[stats_sig->metric].unit);
sig->unit = strdup(Stats::metrics[stats_sig->metric].unit);
if (sig->type != stats_types[stats_sig->type].signal_type)
if (sig->type != Stats::types[stats_sig->type].signal_type)
error("Invalid type for signal %zu in node %s", i, node_name(n));
vlist_push(&s->signals, stats_sig);
@ -226,14 +220,14 @@ int stats_node_read(struct node *n, struct sample *smps[], unsigned cnt, unsigne
unsigned len = MIN(vlist_length(&s->signals), smps[0]->capacity);
for (size_t i = 0; i < len; i++) {
struct stats *st;
Stats *st;
struct stats_node_signal *sig = (struct stats_node_signal *) vlist_at(&s->signals, i);
st = sig->node->stats;
if (!st)
return -1;
smps[0]->data[i] = stats_get_value(st, sig->metric, sig->type);
smps[0]->data[i] = st->getValue(sig->metric, sig->type);
}
smps[0]->length = len;

View file

@ -28,6 +28,7 @@
#include <villas/node.h>
#include <villas/mapping.h>
using namespace villas;
using namespace villas::utils;
int signal_init(struct signal *s)
@ -57,7 +58,7 @@ int signal_init_from_mapping(struct signal *s, const struct mapping_entry *me, u
switch (me->type) {
case MAPPING_TYPE_STATS:
s->type = stats_types[me->stats.type].signal_type;
s->type = Stats::types[me->stats.type].signal_type;
break;
case MAPPING_TYPE_HEADER:

View file

@ -22,7 +22,7 @@
#include <string.h>
#include <villas/stats.h>
#include <villas/stats.hpp>
#include <villas/hist.hpp>
#include <villas/timing.h>
#include <villas/node.h>
@ -34,180 +34,155 @@
using namespace villas;
using namespace villas::utils;
struct stats_metric_description stats_metrics[] = {
{ "skipped", STATS_METRIC_SMPS_SKIPPED, "samples", "Skipped samples and the distance between them" },
{ "reordered", STATS_METRIC_SMPS_REORDERED, "samples", "Reordered samples and the distance between them" },
{ "gap_sent", STATS_METRIC_GAP_SAMPLE, "seconds", "Inter-message timestamps (as sent by remote)" },
{ "gap_received", STATS_METRIC_GAP_RECEIVED, "seconds", "Inter-message arrival time (as received by this instance)" },
{ "owd", STATS_METRIC_OWD, "seconds", "One-way-delay (OWD) of received messages" },
{ "age", STATS_METRIC_AGE, "seconds", "Processing time of packets within the from receive to sent" },
{ "rtp.loss_fraction", STATS_METRIC_RTP_LOSS_FRACTION, "percent", "Fraction lost since last RTP SR/RR." },
{ "rtp.pkts_lost", STATS_METRIC_RTP_PKTS_LOST, "packets", "Cumulative number of packtes lost" },
{ "rtp.jitter", STATS_METRIC_RTP_JITTER, "seconds", "Interarrival jitter" },
std::unordered_map<Stats::Metric, Stats::MetricDescription> Stats::metrics = {
{ Stats::Metric::SMPS_SKIPPED, { "skipped", "samples", "Skipped samples and the distance between them" }},
{ Stats::Metric::SMPS_REORDERED, { "reordered", "samples", "Reordered samples and the distance between them" }},
{ Stats::Metric::GAP_SAMPLE, { "gap_sent", "seconds", "Inter-message timestamps (as sent by remote)" }},
{ Stats::Metric::GAP_RECEIVED, { "gap_received", "seconds", "Inter-message arrival time (as received by this instance)" }},
{ Stats::Metric::OWD, { "owd", "seconds", "One-way-delay (OWD) of received messages" }},
{ Stats::Metric::AGE, { "age", "seconds", "Processing time of packets within the from receive to sent" }},
{ Stats::Metric::RTP_LOSS_FRACTION, { "rtp.loss_fraction", "percent", "Fraction lost since last RTP SR/RR." }},
{ Stats::Metric::RTP_PKTS_LOST, { "rtp.pkts_lost", "packets", "Cumulative number of packtes lost" }},
{ Stats::Metric::RTP_JITTER, { "rtp.jitter", "seconds", "Interarrival jitter" }},
};
struct stats_type_description stats_types[] = {
{ "last", STATS_TYPE_LAST, SIGNAL_TYPE_FLOAT },
{ "highest", STATS_TYPE_HIGHEST, SIGNAL_TYPE_FLOAT },
{ "lowest", STATS_TYPE_LOWEST, SIGNAL_TYPE_FLOAT },
{ "mean", STATS_TYPE_MEAN, SIGNAL_TYPE_FLOAT },
{ "var", STATS_TYPE_VAR, SIGNAL_TYPE_FLOAT },
{ "stddev", STATS_TYPE_STDDEV, SIGNAL_TYPE_FLOAT },
{ "total", STATS_TYPE_TOTAL, SIGNAL_TYPE_INTEGER }
std::unordered_map<Stats::Type, Stats::TypeDescription> Stats::types = {
{ Stats::Type::LAST, { "last", SIGNAL_TYPE_FLOAT }},
{ Stats::Type::HIGHEST, { "highest", SIGNAL_TYPE_FLOAT }},
{ Stats::Type::LOWEST, { "lowest", SIGNAL_TYPE_FLOAT }},
{ Stats::Type::MEAN, { "mean", SIGNAL_TYPE_FLOAT }},
{ Stats::Type::VAR, { "var", SIGNAL_TYPE_FLOAT }},
{ Stats::Type::STDDEV, { "stddev", SIGNAL_TYPE_FLOAT }},
{ Stats::Type::TOTAL, { "total", SIGNAL_TYPE_INTEGER }}
};
int stats_lookup_format(const char *str)
std::vector<TableColumn> Stats::columns = {
{ 10, TableColumn::Alignment::LEFT, "Node", "%s" },
{ 10, TableColumn::Alignment::RIGHT, "Recv", "%ju", "pkts" },
{ 10, TableColumn::Alignment::RIGHT, "Sent", "%ju", "pkts" },
{ 10, TableColumn::Alignment::RIGHT, "Drop", "%ju", "pkts" },
{ 10, TableColumn::Alignment::RIGHT, "Skip", "%ju", "pkts" },
{ 10, TableColumn::Alignment::RIGHT, "OWD last", "%lf", "secs" },
{ 10, TableColumn::Alignment::RIGHT, "OWD mean", "%lf", "secs" },
{ 10, TableColumn::Alignment::RIGHT, "Rate last", "%lf", "pkt/sec" },
{ 10, TableColumn::Alignment::RIGHT, "Rate mean", "%lf", "pkt/sec" },
{ 10, TableColumn::Alignment::RIGHT, "Age mean", "%lf", "secs" },
{ 10, TableColumn::Alignment::RIGHT, "Age Max", "%lf", "sec" }
};
enum Stats::Format Stats::lookupFormat(const std::string &str)
{
if (!strcmp(str, "human"))
return STATS_FORMAT_HUMAN;
else if (!strcmp(str, "json"))
return STATS_FORMAT_JSON;
else if (!strcmp(str, "matlab"))
return STATS_FORMAT_MATLAB;
else
return -1;
if (str == "human")
return Format::HUMAN;
else if (str == "json")
return Format::JSON;
else if (str == "matlab")
return Format::MATLAB;
throw std::invalid_argument("Invalid format");
}
enum stats_metric stats_lookup_metric(const char *str)
enum Stats::Metric Stats::lookupMetric(const std::string &str)
{
for (int i = 0; i < STATS_METRIC_COUNT; i++) {
struct stats_metric_description *d = &stats_metrics[i];
if (!strcmp(str, d->name))
return d->metric;
for (auto m : metrics) {
if (str == m.second.name)
return m.first;
}
return STATS_METRIC_INVALID;
throw std::invalid_argument("Invalid metric");
}
enum stats_type stats_lookup_type(const char *str)
enum Stats::Type Stats::lookupType(const std::string &str)
{
for (int i = 0; i < STATS_TYPE_COUNT; i++) {
struct stats_type_description *d = &stats_types[i];
if (!strcmp(str, d->name))
return d->type;
for (auto t : types) {
if (str == t.second.name)
return t.first;
}
return STATS_TYPE_INVALID;
throw std::invalid_argument("Invalid type");
}
int stats_init(struct stats *s, int buckets, int warmup)
Stats::Stats(int buckets, int warmup)
{
assert(s->state == STATE_DESTROYED);
for (int i = 0; i < STATS_METRIC_COUNT; i++)
new (&s->histograms[i]) Hist(buckets, warmup);
s->state = STATE_INITIALIZED;
return 0;
for (auto m : metrics)
histograms[m.first] = Hist(buckets, warmup);
}
int stats_destroy(struct stats *s)
void Stats::update(enum Metric m, double val)
{
assert(s->state != STATE_DESTROYED);
for (int i = 0; i < STATS_METRIC_COUNT; i++)
s->histograms[i].~Hist();
s->state = STATE_DESTROYED;
return 0;
histograms[m].put(val);
}
void stats_update(struct stats *s, enum stats_metric id, double val)
void Stats::reset()
{
assert(s->state == STATE_INITIALIZED);
s->histograms[id].put(val);
for (auto m : metrics)
histograms[m.first].reset();
}
json_t * stats_json(struct stats *s)
json_t * Stats::toJson() const
{
assert(s->state == STATE_INITIALIZED);
json_t *obj = json_object();
for (int i = 0; i < STATS_METRIC_COUNT; i++) {
struct stats_metric_description *d = &stats_metrics[i];
const Hist &h = s->histograms[i];
for (auto m : metrics) {
const Hist &h = histograms.at(m.first);
json_object_set_new(obj, d->name, h.toJson());
json_object_set_new(obj, m.second.name, h.toJson());
}
return obj;
}
void stats_reset(struct stats *s)
{
assert(s->state == STATE_INITIALIZED);
for (int i = 0; i < STATS_METRIC_COUNT; i++)
s->histograms[i].reset();
}
static std::vector<TableColumn> stats_columns = {
{ 10, TableColumn::align::LEFT, "Node", "%s" },
{ 10, TableColumn::align::RIGHT, "Recv", "%ju", "pkts" },
{ 10, TableColumn::align::RIGHT, "Sent", "%ju", "pkts" },
{ 10, TableColumn::align::RIGHT, "Drop", "%ju", "pkts" },
{ 10, TableColumn::align::RIGHT, "Skip", "%ju", "pkts" },
{ 10, TableColumn::align::RIGHT, "OWD last", "%lf", "secs" },
{ 10, TableColumn::align::RIGHT, "OWD mean", "%lf", "secs" },
{ 10, TableColumn::align::RIGHT, "Rate last", "%lf", "pkt/sec" },
{ 10, TableColumn::align::RIGHT, "Rate mean", "%lf", "pkt/sec" },
{ 10, TableColumn::align::RIGHT, "Age mean", "%lf", "secs" },
{ 10, TableColumn::align::RIGHT, "Age Max", "%lf", "sec" }
};
static Table stats_table = Table(stats_columns);
void stats_print_header(enum stats_format fmt)
void Stats::printHeader(enum Format fmt)
{
switch (fmt) {
case STATS_FORMAT_HUMAN:
stats_table.header();
case Format::HUMAN:
setupTable();
table->header();
break;
default: { }
}
}
void stats_print_periodic(struct stats *s, FILE *f, enum stats_format fmt, struct node *n)
void Stats::setupTable()
{
assert(s->state == STATE_INITIALIZED);
if (!table)
table = std::make_shared<Table>(columns);
}
void Stats::printPeriodic(FILE *f, enum Format fmt, struct node *n) const
{
switch (fmt) {
case STATS_FORMAT_HUMAN:
stats_table.row(11,
case Format::HUMAN:
setupTable();
table->row(11,
node_name_short(n),
(uintmax_t) s->histograms[STATS_METRIC_OWD].getTotal(),
(uintmax_t) s->histograms[STATS_METRIC_AGE].getTotal(),
(uintmax_t) s->histograms[STATS_METRIC_SMPS_REORDERED].getTotal(),
(uintmax_t) s->histograms[STATS_METRIC_SMPS_SKIPPED].getTotal(),
(double) s->histograms[STATS_METRIC_OWD].getLast(),
(double) s->histograms[STATS_METRIC_OWD].getMean(),
(double) 1.0 / s->histograms[STATS_METRIC_GAP_RECEIVED].getLast(),
(double) 1.0 / s->histograms[STATS_METRIC_GAP_RECEIVED].getMean(),
(double) s->histograms[STATS_METRIC_AGE].getMean(),
(double) s->histograms[STATS_METRIC_AGE].getHighest()
(uintmax_t) histograms.at(Metric::OWD).getTotal(),
(uintmax_t) histograms.at(Metric::AGE).getTotal(),
(uintmax_t) histograms.at(Metric::SMPS_REORDERED).getTotal(),
(uintmax_t) histograms.at(Metric::SMPS_SKIPPED).getTotal(),
(double) histograms.at(Metric::OWD).getLast(),
(double) histograms.at(Metric::OWD).getMean(),
(double) 1.0 / histograms.at(Metric::GAP_RECEIVED).getLast(),
(double) 1.0 / histograms.at(Metric::GAP_RECEIVED).getMean(),
(double) histograms.at(Metric::AGE).getMean(),
(double) histograms.at(Metric::AGE).getHighest()
);
break;
case STATS_FORMAT_JSON: {
case Format::JSON: {
json_t *json_stats = json_pack("{ s: s, s: i, s: i, s: i, s: i, s: f, s: f, s: f, s: f, s: f, s: f }",
"node", node_name(n),
"recv", s->histograms[STATS_METRIC_OWD].getTotal(),
"sent", s->histograms[STATS_METRIC_AGE].getTotal(),
"dropped", s->histograms[STATS_METRIC_SMPS_REORDERED].getTotal(),
"skipped", s->histograms[STATS_METRIC_SMPS_SKIPPED].getTotal(),
"owd_last", 1.0 / s->histograms[STATS_METRIC_OWD].getLast(),
"owd_mean", 1.0 / s->histograms[STATS_METRIC_OWD].getMean(),
"rate_last", 1.0 / s->histograms[STATS_METRIC_GAP_SAMPLE].getLast(),
"rate_mean", 1.0 / s->histograms[STATS_METRIC_GAP_SAMPLE].getMean(),
"age_mean", s->histograms[STATS_METRIC_AGE].getMean(),
"age_max", s->histograms[STATS_METRIC_AGE].getHighest()
"recv", histograms.at(Metric::OWD).getTotal(),
"sent", histograms.at(Metric::AGE).getTotal(),
"dropped", histograms.at(Metric::SMPS_REORDERED).getTotal(),
"skipped", histograms.at(Metric::SMPS_SKIPPED).getTotal(),
"owd_last", 1.0 / histograms.at(Metric::OWD).getLast(),
"owd_mean", 1.0 / histograms.at(Metric::OWD).getMean(),
"rate_last", 1.0 / histograms.at(Metric::GAP_SAMPLE).getLast(),
"rate_mean", 1.0 / histograms.at(Metric::GAP_SAMPLE).getMean(),
"age_mean", histograms.at(Metric::AGE).getMean(),
"age_max", histograms.at(Metric::AGE).getHighest()
);
json_dumpf(json_stats, f, 0);
break;
@ -217,64 +192,56 @@ void stats_print_periodic(struct stats *s, FILE *f, enum stats_format fmt, struc
}
}
void stats_print(struct stats *s, FILE *f, enum stats_format fmt, int verbose)
void Stats::print(FILE *f, enum Format fmt, int verbose) const
{
assert(s->state == STATE_INITIALIZED);
switch (fmt) {
case STATS_FORMAT_HUMAN:
for (int i = 0; i < STATS_METRIC_COUNT; i++) {
struct stats_metric_description *d = &stats_metrics[i];
info("%s: %s", d->name, d->desc);
s->histograms[i].print(verbose);
case Format::HUMAN:
for (auto m : metrics) {
info("%s: %s", m.second.name, m.second.desc);
histograms.at(m.first).print(verbose);
}
break;
case STATS_FORMAT_JSON: {
json_t *json_stats = stats_json(s);
json_dumpf(json_stats, f, 0);
case Format::JSON:
json_dumpf(toJson(), f, 0);
fflush(f);
break;
}
default: { }
}
}
union signal_data stats_get_value(const struct stats *s, enum stats_metric sm, enum stats_type st)
union signal_data Stats::getValue(enum Metric sm, enum Type st) const
{
assert(s->state == STATE_INITIALIZED);
const Hist &h = s->histograms[sm];
const Hist &h = histograms.at(sm);
union signal_data d;
switch (st) {
case STATS_TYPE_TOTAL:
case Type::TOTAL:
d.i = h.getTotal();
break;
case STATS_TYPE_LAST:
case Type::LAST:
d.f = h.getLast();
break;
case STATS_TYPE_HIGHEST:
case Type::HIGHEST:
d.f = h.getHighest();
break;
case STATS_TYPE_LOWEST:
case Type::LOWEST:
d.f = h.getLowest();
break;
case STATS_TYPE_MEAN:
case Type::MEAN:
d.f = h.getMean();
break;
case STATS_TYPE_STDDEV:
case Type::STDDEV:
d.f = h.getStddev();
break;
case STATS_TYPE_VAR:
case Type::VAR:
d.f = h.getVar();
break;
@ -284,3 +251,10 @@ union signal_data stats_get_value(const struct stats *s, enum stats_metric sm, e
return d;
}
const Hist & Stats::getHistogram(enum Metric sm) const
{
return histograms.at(sm);
}
std::shared_ptr<Table> Stats::table = std::shared_ptr<Table>();

View file

@ -382,7 +382,7 @@ void SuperNode::start()
if (ret)
throw RuntimeError("Failed to create timer");
stats_print_header(STATS_FORMAT_HUMAN);
Stats::printHeader(Stats::Format::HUMAN);
state = STATE_STARTED;
}

View file

@ -28,6 +28,8 @@
#include <villas/utils.hpp>
#include <villas/signal.h>
using namespace villas;
Test(mapping, parse_nodes)
{
int ret;
@ -73,8 +75,8 @@ Test(mapping, parse_nodes)
cr_assert_eq(ret, 0);
cr_assert_eq(m.node, vlist_lookup(&nodes, "cherry"));
cr_assert_eq(m.type, MAPPING_TYPE_STATS);
cr_assert_eq(m.stats.metric, STATS_METRIC_OWD);
cr_assert_eq(m.stats.type, STATS_TYPE_MEAN);
cr_assert_eq(m.stats.metric, Stats::Metric::OWD);
cr_assert_eq(m.stats.type, Stats::Type::MEAN);
ret = mapping_parse_str(&m, "carrot.data[1-2]", &nodes);
cr_assert_eq(ret, 0);
@ -126,8 +128,8 @@ Test(mapping, parse)
ret = mapping_parse_str(&m, "stats.owd.mean", nullptr);
cr_assert_eq(ret, 0);
cr_assert_eq(m.type, MAPPING_TYPE_STATS);
cr_assert_eq(m.stats.metric, STATS_METRIC_OWD);
cr_assert_eq(m.stats.type, STATS_TYPE_MEAN);
cr_assert_eq(m.stats.metric, Stats::Metric::OWD);
cr_assert_eq(m.stats.type, Stats::Type::MEAN);
ret = mapping_parse_str(&m, "data[1-2]", nullptr);
cr_assert_eq(ret, 0);