From d83b96faeb02d7a3574e63880141d6b23f31ced1 Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Wed, 22 Aug 2018 08:56:53 -0700 Subject: fixes #4 Statistics support This introduces new public APIs for obtaining statistics, and adds some generic stats for dialers, listeners, pipes, and sockets. Also added are stats for inproc and pairv1 protocol. The other protocols and transports will have stats added incrementally as time goes on. A simple test program, and man pages are provided for this. Start by looking at nng_stat(5). Statistics does have some impact, and they can be disabled by using the advanced NNG_ENABLE_STATS (setting it to OFF, it's ON by default) if you need to build a minimized configuration. --- src/core/stats.c | 525 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 525 insertions(+) create mode 100644 src/core/stats.c (limited to 'src/core/stats.c') diff --git a/src/core/stats.c b/src/core/stats.c new file mode 100644 index 00000000..0363a932 --- /dev/null +++ b/src/core/stats.c @@ -0,0 +1,525 @@ +// +// Copyright 2018 Staysail Systems, Inc. +// Copyright 2018 Capitar IT Group BV +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include +#include + +#include "core/nng_impl.h" + +typedef struct nng_stat nni_stat; + +struct nng_stat { + char * s_name; + const char * s_desc; + const char * s_string; + uint64_t s_value; + nni_time s_time; + nni_stat_type s_type; + nni_stat_unit s_unit; + nni_stat_item *s_item; // Used during snapshot collection + nni_list s_children; + nni_stat * s_parent; + nni_list_node s_node; +}; + +#ifdef NNG_ENABLE_STATS +static nni_stat_item stats_root; +static nni_mtx stats_lock; +static nni_mtx * stats_held = NULL; +#endif + +void +nni_stat_append(nni_stat_item *parent, nni_stat_item *child) +{ +#ifdef NNG_ENABLE_STATS + if (parent == NULL) { + parent = &stats_root; + } + nni_mtx_lock(&stats_lock); + // Make sure that the lists for both children and parents + // are correctly initialized. + if (parent->si_children.ll_head.ln_next == NULL) { + NNI_LIST_INIT(&parent->si_children, nni_stat_item, si_node); + } + if (child->si_children.ll_head.ln_next == NULL) { + NNI_LIST_INIT(&child->si_children, nni_stat_item, si_node); + } + nni_list_append(&parent->si_children, child); + child->si_parent = parent; + nni_mtx_unlock(&stats_lock); +#else + NNI_ARG_UNUSED(parent); + NNI_ARG_UNUSED(child); +#endif +} + +void +nni_stat_remove(nni_stat_item *child) +{ +#ifdef NNG_ENABLE_STATS + nni_stat_item *parent; + nni_mtx_lock(&stats_lock); + if ((parent = child->si_parent) != NULL) { + nni_list_remove(&parent->si_children, child); + child->si_parent = NULL; + } + nni_mtx_unlock(&stats_lock); +#else + NNI_ARG_UNUSED(child); +#endif +} + +#ifdef NNG_ENABLE_STATS +void +nni_stat_init(nni_stat_item *stat, const char *name, const char *desc) +{ + NNI_LIST_INIT(&stat->si_children, nni_stat_item, si_node); + stat->si_parent = NULL; + stat->si_name = name; + stat->si_desc = desc; + stat->si_lock = NULL; + stat->si_update = NULL; + stat->si_private = NULL; + stat->si_string = NULL; + stat->si_value = 0; + stat->si_type = NNG_STAT_COUNTER; + stat->si_unit = NNG_UNIT_NONE; +} + +void +nni_stat_init_scope(nni_stat_item *stat, const char *name, const char *desc) +{ + nni_stat_init(stat, name, desc); + stat->si_type = NNG_STAT_SCOPE; + stat->si_unit = NNG_UNIT_NONE; +} + +void +nni_stat_init_string( + nni_stat_item *stat, const char *name, const char *desc, const char *str) +{ + nni_stat_init(stat, name, desc); + stat->si_string = str; + stat->si_type = NNG_STAT_STRING; + stat->si_unit = NNG_UNIT_NONE; +} + +void +nni_stat_init_id( + nni_stat_item *stat, const char *name, const char *desc, uint64_t id) +{ + nni_stat_init(stat, name, desc); + stat->si_value = id; + stat->si_type = NNG_STAT_ID; + stat->si_unit = NNG_UNIT_NONE; +} + +void +nni_stat_init_bool( + nni_stat_item *stat, const char *name, const char *desc, bool v) +{ + nni_stat_init(stat, name, desc); + stat->si_value = v ? 1 : 0; + stat->si_type = NNG_STAT_BOOLEAN; + stat->si_unit = NNG_UNIT_NONE; +} + +static void +stat_atomic_update(nni_stat_item *stat, void *notused) +{ + NNI_ARG_UNUSED(notused); + stat->si_value = nni_atomic_get64(&stat->si_atomic); +} + +void +nni_stat_init_atomic(nni_stat_item *stat, const char *name, const char *desc) +{ + + nni_stat_init(stat, name, desc); + stat->si_value = 0; + stat->si_private = NULL; + stat->si_update = stat_atomic_update; + nni_atomic_init64(&stat->si_atomic); +} + +void +nni_stat_inc_atomic(nni_stat_item *stat, uint64_t inc) +{ + nni_atomic_inc64(&stat->si_atomic, inc); +} + +void +nni_stat_dec_atomic(nni_stat_item *stat, uint64_t inc) +{ + nni_atomic_dec64(&stat->si_atomic, inc); +} +#endif + +void +nni_stat_set_value(nni_stat_item *stat, uint64_t v) +{ +#ifdef NNG_ENABLE_STATS + stat->si_value = v; +#else + NNI_ARG_UNUSED(stat); + NNI_ARG_UNUSED(v); +#endif +} + +void +nni_stat_set_string(nni_stat_item *stat, const char *str) +{ +#ifdef NNG_ENABLE_STATS + stat->si_string = str; +#else + NNI_ARG_UNUSED(stat); + NNI_ARG_UNUSED(str); +#endif +} + +void +nni_stat_set_lock(nni_stat_item *stat, nni_mtx *mtx) +{ +#ifdef NNG_ENABLE_STATS + stat->si_lock = mtx; +#else + NNI_ARG_UNUSED(stat); + NNI_ARG_UNUSED(mtx); +#endif +} + +void +nni_stat_set_update(nni_stat_item *stat, nni_stat_update f, void *a) +{ +#ifdef NNG_ENABLE_STATS + stat->si_update = f; + stat->si_private = a; +#else + NNI_ARG_UNUSED(stat); + NNI_ARG_UNUSED(f); + NNI_ARG_UNUSED(a); +#endif +} + +#ifdef NNG_ENABLE_STATS +void +nni_stat_set_type(nni_stat_item *stat, int type) +{ + stat->si_type = type; +} + +void +nni_stat_set_unit(nni_stat_item *stat, int unit) +{ + stat->si_unit = unit; +} +#endif + +void +nng_stats_free(nni_stat *st) +{ +#ifdef NNG_ENABLE_STATS + nni_stat *child; + + while ((child = nni_list_first(&st->s_children)) != NULL) { + nni_list_remove(&st->s_children, child); + nng_stats_free(child); + } + nni_strfree(st->s_name); + NNI_FREE_STRUCT(st); +#else + NNI_ARG_UNUSED(st); +#endif +} + +#ifdef NNG_ENABLE_STATS +static int +stat_make_tree(nni_stat_item *item, nni_stat **sp) +{ + nni_stat * stat; + nni_stat_item *child; + + if ((stat = NNI_ALLOC_STRUCT(stat)) == NULL) { + return (NNG_ENOMEM); + } + if ((stat->s_name = nni_strdup(item->si_name)) == NULL) { + NNI_FREE_STRUCT(stat); + return (NNG_ENOMEM); + } + NNI_LIST_INIT(&stat->s_children, nni_stat, s_node); + stat->s_item = item; + stat->s_type = item->si_type; + stat->s_unit = item->si_unit; + stat->s_desc = item->si_desc; + stat->s_parent = NULL; + + NNI_LIST_FOREACH (&item->si_children, child) { + nni_stat *cs; + int rv; + if ((rv = stat_make_tree(child, &cs)) != 0) { + nng_stats_free(stat); + return (rv); + } + nni_list_append(&stat->s_children, cs); + cs->s_parent = stat; + } + *sp = stat; + return (0); +} + +static void +stat_update(nni_stat *stat) +{ + nni_stat_item *item = stat->s_item; + + if (item->si_lock != stats_held) { + if (stats_held != NULL) { + nni_mtx_unlock(stats_held); + stats_held = NULL; + } + if (item->si_lock != NULL) { + nni_mtx_lock(item->si_lock); + stats_held = item->si_lock; + } + } + if (item->si_update != NULL) { + item->si_update(item, item->si_private); + } + stat->s_value = item->si_value; + stat->s_string = item->si_string; + stat->s_time = nni_clock(); +} + +static void +stat_update_tree(nni_stat *stat) +{ + nni_stat *child; + stat_update(stat); + NNI_LIST_FOREACH (&stat->s_children, child) { + stat_update_tree(child); + } +} + +int +nni_stat_snapshot(nni_stat **statp, nni_stat_item *item) +{ + int rv; + nni_stat *stat; + + if (item == NULL) { + item = &stats_root; + } + nni_mtx_lock(&stats_lock); + if ((rv = stat_make_tree(item, &stat)) != 0) { + nni_mtx_unlock(&stats_lock); + return (rv); + } + stat_update_tree(stat); + if (stats_held != NULL) { + nni_mtx_unlock(stats_held); + stats_held = NULL; + } + nni_mtx_unlock(&stats_lock); + *statp = stat; + return (0); +} +#endif + +int +nng_stats_get(nng_stat **statp) +{ +#ifdef NNG_ENABLE_STATS + return (nni_stat_snapshot(statp, &stats_root)); +#else + NNI_ARG_UNUSED(statp); + return (NNG_ENOTSUP); +#endif +} + +nng_stat * +nng_stat_parent(nng_stat *stat) +{ + return (stat->s_parent); +} + +nng_stat * +nng_stat_next(nng_stat *stat) +{ + if (stat->s_parent == NULL) { + return (NULL); // Root node, no siblings. + } + return (nni_list_next(&stat->s_parent->s_children, stat)); +} + +nng_stat * +nng_stat_child(nng_stat *stat) +{ + return (nni_list_first(&stat->s_children)); +} + +const char * +nng_stat_name(nni_stat *stat) +{ + return (stat->s_name); +} + +uint64_t +nng_stat_value(nni_stat *stat) +{ + return (stat->s_value); +} + +const char * +nng_stat_string(nng_stat *stat) +{ + return (stat->s_string); +} + +uint64_t +nng_stat_timestamp(nng_stat *stat) +{ + return ((uint64_t) stat->s_time); +} + +int +nng_stat_type(nng_stat *stat) +{ + return (stat->s_type); +} + +int +nng_stat_unit(nng_stat *stat) +{ + return (stat->s_unit); +} + +const char * +nng_stat_desc(nng_stat *stat) +{ + return (stat->s_desc); +} + +int +nni_stat_sys_init(void) +{ +#ifdef NNG_ENABLE_STATS + nni_mtx_init(&stats_lock); + NNI_LIST_INIT(&stats_root.si_children, nni_stat_item, si_node); + stats_root.si_name = ""; + stats_root.si_desc = "all statistsics"; +#endif + + return (0); +} + +void +nni_stat_sys_fini(void) +{ +#ifdef NNG_ENABLE_STATS + nni_mtx_fini(&stats_lock); +#endif +} + +#ifdef NNG_ENABLE_STATS +void +stat_sprint_scope(nni_stat *stat, char **scope, int *lenp) +{ + if (stat->s_parent != NULL) { + stat_sprint_scope(stat->s_parent, scope, lenp); + } + if (strlen(stat->s_name) > 0) { + snprintf(*scope, *lenp, "%s.", stat->s_name); + } else { + (*scope)[0] = '\0'; + } + *lenp -= strlen(*scope); + *scope += strlen(*scope); +} +#endif + +void +nng_stats_dump(nng_stat *stat) +{ +#ifdef NNG_ENABLE_STATS + static char buf[128]; // to minimize recursion, not thread safe + static char line[128]; + int len; + char * scope; + char * indent = " "; + unsigned long long val; + nni_stat * child; + + switch (nng_stat_type(stat)) { + case NNG_STAT_SCOPE: + scope = buf; + len = sizeof(buf); + stat_sprint_scope(stat, &scope, &len); + len = strlen(buf); + if (len > 0) { + if (buf[len - 1] == '.') { + buf[--len] = '\0'; + } + } + if (len > 0) { + snprintf(line, sizeof(line), "\n%s:", buf); + } + break; + case NNG_STAT_STRING: + snprintf(line, sizeof(line), "%s%-32s\"%s\"", indent, + nng_stat_name(stat), nng_stat_string(stat)); + break; + case NNG_STAT_BOOLEAN: + val = nng_stat_value(stat); + snprintf(line, sizeof(line), "%s%-32s%s", indent, + nng_stat_name(stat), val != 0 ? "true" : "false"); + break; + case NNG_STAT_LEVEL: + case NNG_STAT_COUNTER: + val = nng_stat_value(stat); + switch (nng_stat_unit(stat)) { + case NNG_UNIT_BYTES: + snprintf(line, sizeof(line), "%s%-32s%llu bytes", + indent, nng_stat_name(stat), val); + break; + case NNG_UNIT_MESSAGES: + snprintf(line, sizeof(line), "%s%-32s%llu msgs", + indent, nng_stat_name(stat), val); + break; + case NNG_UNIT_MILLIS: + snprintf(line, sizeof(line), "%s%-32s%llu msec", + indent, nng_stat_name(stat), val); + break; + case NNG_UNIT_NONE: + case NNG_UNIT_EVENTS: + default: + snprintf(line, sizeof(line), "%s%-32s%llu", indent, + nng_stat_name(stat), val); + break; + } + break; + case NNG_STAT_ID: + val = nng_stat_value(stat); + snprintf(line, (sizeof line), "%s%-32s%llu", indent, + nng_stat_name(stat), val); + break; + default: + snprintf(line, (sizeof line), "%s%-32s", indent, + nng_stat_name(stat)); + break; + } + nni_plat_println(line); + + NNI_LIST_FOREACH (&stat->s_children, child) { + nng_stats_dump(child); + } +#else + NNI_ARG_UNUSED(stat); +#endif +} -- cgit v1.2.3-70-g09d2