diff options
Diffstat (limited to '')
-rw-r--r-- | lib/dns/stats.c | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/lib/dns/stats.c b/lib/dns/stats.c new file mode 100644 index 0000000..617ed3b --- /dev/null +++ b/lib/dns/stats.c @@ -0,0 +1,475 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/stats.h> +#include <isc/util.h> + +#include <dns/opcode.h> +#include <dns/rdatatype.h> +#include <dns/stats.h> + +#define DNS_STATS_MAGIC ISC_MAGIC('D', 's', 't', 't') +#define DNS_STATS_VALID(x) ISC_MAGIC_VALID(x, DNS_STATS_MAGIC) + +/*% + * Statistics types. + */ +typedef enum { + dns_statstype_general = 0, + dns_statstype_rdtype = 1, + dns_statstype_rdataset = 2, + dns_statstype_opcode = 3, + dns_statstype_rcode = 4 +} dns_statstype_t; + +/*% + * It doesn't make sense to have 2^16 counters for all possible types since + * most of them won't be used. We have counters for the first 256 types and + * those explicitly supported in the rdata implementation. + * XXXJT: this introduces tight coupling with the rdata implementation. + * Ideally, we should have rdata handle this type of details. + */ +/* + * types, !types, nxdomain, stale types, stale !types, stale nxdomain + */ +enum { + /* For 0-255, we use the rdtype value as counter indices */ + rdtypecounter_dlv = 256, /* for dns_rdatatype_dlv */ + rdtypecounter_others = 257, /* anything else */ + rdtypecounter_max = 258, + /* The following are used for rdataset */ + rdtypenxcounter_max = rdtypecounter_max * 2, + rdtypecounter_nxdomain = rdtypenxcounter_max, + /* stale counters offset */ + rdtypecounter_stale = rdtypecounter_nxdomain + 1, + rdatasettypecounter_max = rdtypecounter_stale * 2 +}; + +struct dns_stats { + /*% Unlocked */ + unsigned int magic; + dns_statstype_t type; + isc_mem_t *mctx; + isc_mutex_t lock; + isc_stats_t *counters; + + /*% Locked by lock */ + unsigned int references; +}; + +typedef struct rdatadumparg { + dns_rdatatypestats_dumper_t fn; + void *arg; +} rdatadumparg_t; + +typedef struct opcodedumparg { + dns_opcodestats_dumper_t fn; + void *arg; +} opcodedumparg_t; + +typedef struct rcodedumparg { + dns_rcodestats_dumper_t fn; + void *arg; +} rcodedumparg_t; + +void +dns_stats_attach(dns_stats_t *stats, dns_stats_t **statsp) { + REQUIRE(DNS_STATS_VALID(stats)); + REQUIRE(statsp != NULL && *statsp == NULL); + + LOCK(&stats->lock); + stats->references++; + UNLOCK(&stats->lock); + + *statsp = stats; +} + +void +dns_stats_detach(dns_stats_t **statsp) { + dns_stats_t *stats; + + REQUIRE(statsp != NULL && DNS_STATS_VALID(*statsp)); + + stats = *statsp; + *statsp = NULL; + + LOCK(&stats->lock); + stats->references--; + UNLOCK(&stats->lock); + + if (stats->references == 0) { + isc_stats_detach(&stats->counters); + DESTROYLOCK(&stats->lock); + isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats)); + } +} + +/*% + * Create methods + */ +static isc_result_t +create_stats(isc_mem_t *mctx, dns_statstype_t type, int ncounters, + dns_stats_t **statsp) +{ + dns_stats_t *stats; + isc_result_t result; + + stats = isc_mem_get(mctx, sizeof(*stats)); + if (stats == NULL) + return (ISC_R_NOMEMORY); + + stats->counters = NULL; + stats->references = 1; + + result = isc_mutex_init(&stats->lock); + if (result != ISC_R_SUCCESS) + goto clean_stats; + + result = isc_stats_create(mctx, &stats->counters, ncounters); + if (result != ISC_R_SUCCESS) + goto clean_mutex; + + stats->magic = DNS_STATS_MAGIC; + stats->type = type; + stats->mctx = NULL; + isc_mem_attach(mctx, &stats->mctx); + *statsp = stats; + + return (ISC_R_SUCCESS); + + clean_mutex: + DESTROYLOCK(&stats->lock); + clean_stats: + isc_mem_put(mctx, stats, sizeof(*stats)); + + return (result); +} + +isc_result_t +dns_generalstats_create(isc_mem_t *mctx, dns_stats_t **statsp, int ncounters) { + REQUIRE(statsp != NULL && *statsp == NULL); + + return (create_stats(mctx, dns_statstype_general, ncounters, statsp)); +} + +isc_result_t +dns_rdatatypestats_create(isc_mem_t *mctx, dns_stats_t **statsp) { + REQUIRE(statsp != NULL && *statsp == NULL); + + return (create_stats(mctx, dns_statstype_rdtype, rdtypecounter_max, + statsp)); +} + +isc_result_t +dns_rdatasetstats_create(isc_mem_t *mctx, dns_stats_t **statsp) { + REQUIRE(statsp != NULL && *statsp == NULL); + + return (create_stats(mctx, dns_statstype_rdataset, + rdatasettypecounter_max, statsp)); +} + +isc_result_t +dns_opcodestats_create(isc_mem_t *mctx, dns_stats_t **statsp) { + REQUIRE(statsp != NULL && *statsp == NULL); + + return (create_stats(mctx, dns_statstype_opcode, 16, statsp)); +} + +isc_result_t +dns_rcodestats_create(isc_mem_t *mctx, dns_stats_t **statsp) { + REQUIRE(statsp != NULL && *statsp == NULL); + + return (create_stats(mctx, dns_statstype_rcode, + dns_rcode_badcookie + 1, statsp)); +} + +/*% + * Increment/Decrement methods + */ +void +dns_generalstats_increment(dns_stats_t *stats, isc_statscounter_t counter) { + REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_general); + + isc_stats_increment(stats->counters, counter); +} + +void +dns_rdatatypestats_increment(dns_stats_t *stats, dns_rdatatype_t type) { + int counter; + + REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rdtype); + + if (type == dns_rdatatype_dlv) + counter = rdtypecounter_dlv; + else if (type > dns_rdatatype_any) + counter = rdtypecounter_others; + else + counter = (int)type; + + isc_stats_increment(stats->counters, (isc_statscounter_t)counter); +} + +static inline void +update_rdatasetstats(dns_stats_t *stats, dns_rdatastatstype_t rrsettype, + bool increment) +{ + int counter; + dns_rdatatype_t rdtype; + + if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) & + DNS_RDATASTATSTYPE_ATTR_NXDOMAIN) != 0) { + counter = rdtypecounter_nxdomain; + } else { + rdtype = DNS_RDATASTATSTYPE_BASE(rrsettype); + if (rdtype == dns_rdatatype_dlv) + counter = (int)rdtypecounter_dlv; + else if (rdtype > dns_rdatatype_any) + counter = (int)rdtypecounter_others; + else + counter = (int)rdtype; + + if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) & + DNS_RDATASTATSTYPE_ATTR_NXRRSET) != 0) + counter += rdtypecounter_max; + } + + if (increment) { + if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) & + DNS_RDATASTATSTYPE_ATTR_STALE) != 0) { + isc_stats_decrement(stats->counters, counter); + counter += rdtypecounter_stale; + } + isc_stats_increment(stats->counters, counter); + } else { + if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) & + DNS_RDATASTATSTYPE_ATTR_STALE) != 0) + counter += rdtypecounter_stale; + isc_stats_decrement(stats->counters, counter); + } +} + +void +dns_rdatasetstats_increment(dns_stats_t *stats, dns_rdatastatstype_t rrsettype) +{ + REQUIRE(DNS_STATS_VALID(stats) && + stats->type == dns_statstype_rdataset); + + update_rdatasetstats(stats, rrsettype, true); +} + +void +dns_rdatasetstats_decrement(dns_stats_t *stats, dns_rdatastatstype_t rrsettype) +{ + REQUIRE(DNS_STATS_VALID(stats) && + stats->type == dns_statstype_rdataset); + + update_rdatasetstats(stats, rrsettype, false); +} + +void +dns_opcodestats_increment(dns_stats_t *stats, dns_opcode_t code) { + REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_opcode); + + isc_stats_increment(stats->counters, (isc_statscounter_t)code); +} + +void +dns_rcodestats_increment(dns_stats_t *stats, dns_rcode_t code) { + REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rcode); + + if (code <= dns_rcode_badcookie) + isc_stats_increment(stats->counters, (isc_statscounter_t)code); +} + +/*% + * Dump methods + */ +void +dns_generalstats_dump(dns_stats_t *stats, dns_generalstats_dumper_t dump_fn, + void *arg, unsigned int options) +{ + REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_general); + + isc_stats_dump(stats->counters, (isc_stats_dumper_t)dump_fn, + arg, options); +} + +static void +dump_rdentry(int rdcounter, uint64_t value, dns_rdatastatstype_t attributes, + dns_rdatatypestats_dumper_t dump_fn, void * arg) +{ + dns_rdatatype_t rdtype = dns_rdatatype_none; /* sentinel */ + dns_rdatastatstype_t type; + + if (rdcounter == rdtypecounter_others) + attributes |= DNS_RDATASTATSTYPE_ATTR_OTHERTYPE; + else { + if (rdcounter == rdtypecounter_dlv) + rdtype = dns_rdatatype_dlv; + else + rdtype = (dns_rdatatype_t)rdcounter; + } + type = DNS_RDATASTATSTYPE_VALUE((dns_rdatastatstype_t)rdtype, + attributes); + dump_fn(type, value, arg); +} + +static void +rdatatype_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) { + rdatadumparg_t *rdatadumparg = arg; + + dump_rdentry(counter, value, 0, rdatadumparg->fn, rdatadumparg->arg); +} + +void +dns_rdatatypestats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn, + void *arg0, unsigned int options) +{ + rdatadumparg_t arg; + REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rdtype); + + arg.fn = dump_fn; + arg.arg = arg0; + isc_stats_dump(stats->counters, rdatatype_dumpcb, &arg, options); +} + +static void +rdataset_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) { + rdatadumparg_t *rdatadumparg = arg; + unsigned int attributes; + + if (counter < rdtypecounter_max) { + dump_rdentry(counter, value, 0, rdatadumparg->fn, + rdatadumparg->arg); + } else if (counter < rdtypecounter_nxdomain) { + counter -= rdtypecounter_max; + attributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET; + dump_rdentry(counter, value, attributes, rdatadumparg->fn, + rdatadumparg->arg); + } else if (counter == rdtypecounter_nxdomain) { + dump_rdentry(0, value, DNS_RDATASTATSTYPE_ATTR_NXDOMAIN, + rdatadumparg->fn, rdatadumparg->arg); + } else if (counter < rdtypecounter_stale + rdtypecounter_max) { + counter -= rdtypecounter_stale; + attributes = DNS_RDATASTATSTYPE_ATTR_STALE; + dump_rdentry(counter, value, attributes, rdatadumparg->fn, + rdatadumparg->arg); + } else if (counter < rdtypecounter_stale + rdtypecounter_nxdomain) { + counter -= rdtypecounter_stale + rdtypecounter_max; + attributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET | + DNS_RDATASTATSTYPE_ATTR_STALE; + dump_rdentry(counter, value, attributes, rdatadumparg->fn, + rdatadumparg->arg); + } else { + attributes = DNS_RDATASTATSTYPE_ATTR_NXDOMAIN | + DNS_RDATASTATSTYPE_ATTR_STALE; + dump_rdentry(0, value, attributes, rdatadumparg->fn, + rdatadumparg->arg); + } +} + +void +dns_rdatasetstats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn, + void *arg0, unsigned int options) +{ + rdatadumparg_t arg; + + REQUIRE(DNS_STATS_VALID(stats) && + stats->type == dns_statstype_rdataset); + + arg.fn = dump_fn; + arg.arg = arg0; + isc_stats_dump(stats->counters, rdataset_dumpcb, &arg, options); +} + +static void +opcode_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) { + opcodedumparg_t *opcodearg = arg; + + opcodearg->fn((dns_opcode_t)counter, value, opcodearg->arg); +} + +static void +rcode_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) { + rcodedumparg_t *rcodearg = arg; + + rcodearg->fn((dns_rcode_t)counter, value, rcodearg->arg); +} + +void +dns_opcodestats_dump(dns_stats_t *stats, dns_opcodestats_dumper_t dump_fn, + void *arg0, unsigned int options) +{ + opcodedumparg_t arg; + + REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_opcode); + + arg.fn = dump_fn; + arg.arg = arg0; + isc_stats_dump(stats->counters, opcode_dumpcb, &arg, options); +} + +void +dns_rcodestats_dump(dns_stats_t *stats, dns_rcodestats_dumper_t dump_fn, + void *arg0, unsigned int options) +{ + rcodedumparg_t arg; + + REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rcode); + + arg.fn = dump_fn; + arg.arg = arg0; + isc_stats_dump(stats->counters, rcode_dumpcb, &arg, options); +} + +/*** + *** Obsolete variables and functions follow: + ***/ +LIBDNS_EXTERNAL_DATA const char *dns_statscounter_names[DNS_STATS_NCOUNTERS] = + { + "success", + "referral", + "nxrrset", + "nxdomain", + "recursion", + "failure", + "duplicate", + "dropped" + }; + +isc_result_t +dns_stats_alloccounters(isc_mem_t *mctx, uint64_t **ctrp) { + int i; + uint64_t *p = + isc_mem_get(mctx, DNS_STATS_NCOUNTERS * sizeof(uint64_t)); + if (p == NULL) + return (ISC_R_NOMEMORY); + for (i = 0; i < DNS_STATS_NCOUNTERS; i++) + p[i] = 0; + *ctrp = p; + return (ISC_R_SUCCESS); +} + +void +dns_stats_freecounters(isc_mem_t *mctx, uint64_t **ctrp) { + isc_mem_put(mctx, *ctrp, DNS_STATS_NCOUNTERS * sizeof(uint64_t)); + *ctrp = NULL; +} |