/* 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)
}
/** @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)
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;
}
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);
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;
*/
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;
*/
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);
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;
}
*/
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;
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();
}