]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
modules/stats: persistent metrics table
authorMarek Vavruša <marek.vavrusa@nic.cz>
Mon, 13 Jul 2015 13:37:50 +0000 (15:37 +0200)
committerMarek Vavruša <marek.vavrusa@nic.cz>
Mon, 13 Jul 2015 13:37:50 +0000 (15:37 +0200)
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

modules/stats/stats.c

index 5cce7d37e414beccad76e37f89245b3d85d0b39b..11e2bd89eaee55566976ec74eae33c91f5a5d857 100644 (file)
 
 /* 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 = &param->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();
 }