From: Marek VavruĊĦa Date: Mon, 13 Jul 2015 13:37:50 +0000 (+0200) Subject: modules/stats: persistent metrics table X-Git-Tag: v1.0.0-beta1~77^2~13^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=275533cca3dd78cd4ac4af2a8fbdede50a8532a6;p=thirdparty%2Fknot-resolver.git modules/stats: persistent metrics table enumerated metrics are always present (set to 0 if not used), this fixes missing values when not measuring and provides a faster access to common data --- diff --git a/modules/stats/stats.c b/modules/stats/stats.c index 5cce7d37e..11e2bd89e 100644 --- a/modules/stats/stats.c +++ b/modules/stats/stats.c @@ -37,6 +37,39 @@ /* Defaults */ #define DEBUG_MSG(qry, fmt...) QRDEBUG(qry, "stat", fmt) +/** @cond internal Fixed-size map of predefined metrics. */ +#define CONST_METRICS(X) \ + X(answer,total) X(answer,noerror) X(answer,nxdomain) X(answer,servfail) \ + X(answer,cached) X(answer,slow) \ + X(query,edns) X(query,dnssec) \ + X(const,end) + +enum const_metric { + #define X(a,b) metric_ ## a ## _ ## b, + CONST_METRICS(X) + #undef X +}; +struct const_metric_elm { + const char *key; + size_t val; +}; +static struct const_metric_elm const_metrics[] = { + #define X(a,b) [metric_ ## a ## _ ## b] = { #a "." #b, 0 }, + CONST_METRICS(X) + #undef X +}; +/** @endcond */ + +/** @internal LRU hash of most frequent names. */ +typedef lru_hash(unsigned) namehash_t; + +/** @internal Stats data structure. */ +struct stat_data { + map_t map; + struct { + namehash_t *names; + } frequent; +}; /** @internal Subtract time (best effort) */ float time_diff(struct timeval *begin, struct timeval *end) @@ -47,10 +80,16 @@ float time_diff(struct timeval *begin, struct timeval *end) } /** @internal Add to map counter */ -static inline void stat_add(map_t *map, const char *key, ssize_t incr) +static inline void stat_add(struct stat_data *data, const char *key, ssize_t incr) +{ + void *val = map_get(&data->map, key); + map_set(&data->map, key, (void *)((size_t)val + incr)); +} + +/** @internal Add to const map counter */ +static inline void stat_const_add(struct stat_data *data, enum const_metric key, ssize_t incr) { - void *val = map_get(map, key); - map_set(map, key, (void *)((size_t)val + incr)); + const_metrics[key].val += incr; } static int begin(knot_layer_t *ctx, void *module_param) @@ -59,14 +98,14 @@ static int begin(knot_layer_t *ctx, void *module_param) return ctx->state; } -static int collect_answer(map_t *map, knot_pkt_t *pkt) +static int collect_answer(struct stat_data *data, knot_pkt_t *pkt) { - stat_add(map, "answer.total", 1); + stat_const_add(data, metric_answer_total, 1); /* Count per-rcode */ switch(knot_wire_get_rcode(pkt->wire)) { - case KNOT_RCODE_NOERROR: stat_add(map, "answer.noerror", 1); break; - case KNOT_RCODE_NXDOMAIN: stat_add(map, "answer.nxdomain", 1); break; - case KNOT_RCODE_SERVFAIL: stat_add(map, "answer.servfail", 1); break; + case KNOT_RCODE_NOERROR: stat_const_add(data, metric_answer_noerror, 1); break; + case KNOT_RCODE_NXDOMAIN: stat_const_add(data, metric_answer_nxdomain, 1); break; + case KNOT_RCODE_SERVFAIL: stat_const_add(data, metric_answer_servfail, 1); break; default: break; } @@ -78,15 +117,15 @@ static int collect(knot_layer_t *ctx) struct kr_request *param = ctx->data; struct kr_module *module = ctx->api->data; struct kr_rplan *rplan = ¶m->rplan; - map_t *map = module->data; + struct stat_data *data = module->data; /* Collect data on final answer */ - collect_answer(map, param->answer); + collect_answer(data, param->answer); /* Count cached and unresolved */ if (!EMPTY_LIST(rplan->resolved)) { struct kr_query *last = TAIL(rplan->resolved); if (last->flags & QUERY_CACHED) { - stat_add(map, "answer.cached", 1); + stat_const_add(data, metric_answer_cached, 1); } /* Count slow queries (>1000ms) */ struct kr_query *first = HEAD(rplan->resolved); @@ -94,14 +133,14 @@ static int collect(knot_layer_t *ctx) gettimeofday(&now, NULL); float elapsed = time_diff(&first->timestamp, &now); if (elapsed > 1000.0) { - stat_add(map, "answer.slow", 1); + stat_const_add(data, metric_answer_slow, 1); } } /* Query parameters and transport mode */ if (knot_pkt_has_edns(param->answer)) { - stat_add(map, "query.edns", 1); + stat_const_add(data, metric_query_edns, 1); if (knot_pkt_has_dnssec(param->answer)) { - stat_add(map, "query.dnssec", 1); + stat_const_add(data, metric_query_dnssec, 1); } } return ctx->state; @@ -115,13 +154,19 @@ static int collect(knot_layer_t *ctx) */ static char* stats_set(void *env, struct kr_module *module, const char *args) { - map_t *map = module->data; + struct stat_data *data = module->data; auto_free char *pair = strdup(args); char *val = strchr(pair, ' '); if (val) { *val = '\0'; size_t number = strtoul(val + 1, NULL, 10); - map_set(map, pair, (void *)number); + for (unsigned i = 0; i < metric_const_end; ++i) { + if (strcmp(const_metrics[i].key, pair) == 0) { + const_metrics[i].val = number; + return NULL; + } + } + map_set(&data->map, pair, (void *)number); } return NULL; @@ -135,10 +180,7 @@ static char* stats_set(void *env, struct kr_module *module, const char *args) */ static char* stats_get(void *env, struct kr_module *module, const char *args) { - map_t *map = module->data; - if (!map_contains(map, args)) { - return NULL; - } + struct stat_data *data = module->data; /* Expecting CHAR_BIT to be 8, this is a safe bet */ char *ret = malloc(3 * sizeof(ret) + 2); @@ -146,7 +188,19 @@ static char* stats_get(void *env, struct kr_module *module, const char *args) return NULL; } - void *val = map_get(map, args); + /* Check if it exists in const map. */ + for (unsigned i = 0; i < metric_const_end; ++i) { + if (strcmp(const_metrics[i].key, args) == 0) { + sprintf(ret, "%zu", const_metrics[i].val); + return ret; + } + } + /* Check in variable map */ + if (!map_contains(&data->map, args)) { + free(ret); + return NULL; + } + void *val = map_get(&data->map, args); sprintf(ret, "%zu", (size_t) val); return ret; } @@ -166,9 +220,17 @@ static int list_entry(const char *key, void *val, void *baton) */ static char* stats_list(void *env, struct kr_module *module, const char *args) { - map_t *map = module->data; + struct stat_data *data = module->data; JsonNode *root = json_mkobject(); - map_walk_prefixed(map, args ? args : "", list_entry, root); + /* Walk const metrics map */ + size_t args_len = args ? strlen(args) : 0; + for (unsigned i = 0; i < metric_const_end; ++i) { + struct const_metric_elm *elm = &const_metrics[i]; + if (strncmp(elm->key, args, args_len) == 0) { + json_append_member(root, elm->key, json_mknumber(elm->val)); + } + } + map_walk_prefixed(&data->map, args ? args : "", list_entry, root); char *ret = json_encode(root); json_delete(root); return ret; @@ -191,21 +253,21 @@ const knot_layer_api_t *stats_layer(struct kr_module *module) int stats_init(struct kr_module *module) { - map_t *map = malloc(sizeof(*map)); - if (!map) { + struct stat_data *data = malloc(sizeof(*data)); + if (!data) { return kr_error(ENOMEM); } - *map = map_make(); - module->data = map; + data->map = map_make(); + module->data = data; return kr_ok(); } int stats_deinit(struct kr_module *module) { - map_t *map = module->data; - if (map) { - map_clear(map); - free(map); + struct stat_data *data = module->data; + if (data) { + map_clear(&data->map); + free(data); } return kr_ok(); }