mirror of
https://github.com/isc-projects/bind9.git
synced 2026-05-23 02:28:55 -04:00
In the current statistics counter implementation, the statistics are backed by an array of counters, which are updated via atomic operations. This leads to contention, especially on high core count machines. This commit introduces a new isc_statsmulti_t counter that keeps a separate array per thread. These counters are then aggregated only when statistics are queried, shifting work off the critical path. These changes lead to a ~2% improvement in perflab.
604 lines
16 KiB
C
604 lines
16 KiB
C
/*
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
*
|
|
* 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 https://mozilla.org/MPL/2.0/.
|
|
*
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
* information regarding copyright ownership.
|
|
*/
|
|
|
|
/*! \file */
|
|
|
|
#include <inttypes.h>
|
|
#include <stdbool.h>
|
|
|
|
#include <isc/log.h>
|
|
#include <isc/magic.h>
|
|
#include <isc/mem.h>
|
|
#include <isc/refcount.h>
|
|
#include <isc/stats.h>
|
|
#include <isc/statsmulti.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_dnssec = 5
|
|
} 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.
|
|
*
|
|
* A rdtypecounter is now 8 bits for RRtypes and 3 bits for flags:
|
|
*
|
|
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
|
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
* | | | | | | S |NX| RRType |
|
|
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
*
|
|
* If the 8 bits for RRtype are all zero, this is an Other RRtype.
|
|
*/
|
|
#define RDTYPECOUNTER_MAXTYPE 0x00ff
|
|
|
|
/*
|
|
*
|
|
* Bit 7 is the NXRRSET (NX) flag and indicates whether this is a
|
|
* positive (0) or a negative (1) RRset.
|
|
*/
|
|
#define RDTYPECOUNTER_NXRRSET 0x0100
|
|
|
|
/*
|
|
* Then bit 5 and 6 mostly tell you if this counter is for an active,
|
|
* stale, or ancient RRtype:
|
|
*
|
|
* S = 0 (0b00) means Active
|
|
* S = 1 (0b01) means Stale
|
|
* S = 2 (0b10) means Ancient
|
|
*
|
|
* Since a counter cannot be stale and ancient at the same time, we
|
|
* treat S = 0b11 as a special case to deal with NXDOMAIN counters.
|
|
*/
|
|
#define RDTYPECOUNTER_STALE (1 << 9)
|
|
#define RDTYPECOUNTER_ANCIENT (1 << 10)
|
|
#define RDTYPECOUNTER_NXDOMAIN ((1 << 9) | (1 << 10))
|
|
|
|
/*
|
|
* S = 0b11 indicates an NXDOMAIN counter and in this case the RRtype
|
|
* field signals the expiry of this cached item:
|
|
*
|
|
* RRType = 0 (0b00) means Active
|
|
* RRType = 1 (0b01) means Stale
|
|
* RRType = 2 (0b02) means Ancient
|
|
*
|
|
*/
|
|
#define RDTYPECOUNTER_NXDOMAIN_STALE 1
|
|
#define RDTYPECOUNTER_NXDOMAIN_ANCIENT 2
|
|
|
|
/*
|
|
* The maximum value for rdtypecounter is for an ancient NXDOMAIN.
|
|
*/
|
|
#define RDTYPECOUNTER_MAXVAL 0x0602
|
|
|
|
/*
|
|
* DNSSEC sign statistics.
|
|
*
|
|
* Per key we maintain 3 counters. The first is actually no counter but
|
|
* a key id reference. The second is the number of signatures the key created.
|
|
* The third is the number of signatures refreshed by the key.
|
|
*/
|
|
|
|
/* Maximum number of keys to keep track of for DNSSEC signing statistics. */
|
|
static int dnssecsign_num_keys = 4;
|
|
static int dnssecsign_block_size = 3;
|
|
|
|
struct dns_stats {
|
|
unsigned int magic;
|
|
dns_statstype_t type;
|
|
isc_mem_t *mctx;
|
|
isc_stats_t *counters;
|
|
isc_refcount_t 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;
|
|
typedef struct dnssecsigndumparg {
|
|
dns_dnssecsignstats_dumper_t fn;
|
|
void *arg;
|
|
} dnssecsigndumparg_t;
|
|
|
|
void
|
|
dns_stats_attach(dns_stats_t *stats, dns_stats_t **statsp) {
|
|
REQUIRE(DNS_STATS_VALID(stats));
|
|
REQUIRE(statsp != NULL && *statsp == NULL);
|
|
|
|
isc_refcount_increment(&stats->references);
|
|
|
|
*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;
|
|
|
|
if (isc_refcount_decrement(&stats->references) == 1) {
|
|
isc_refcount_destroy(&stats->references);
|
|
isc_stats_detach(&stats->counters);
|
|
isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats));
|
|
}
|
|
}
|
|
|
|
/*%
|
|
* Create methods
|
|
*/
|
|
static void
|
|
create_stats(isc_mem_t *mctx, dns_statstype_t type, int ncounters,
|
|
dns_stats_t **statsp) {
|
|
dns_stats_t *stats = isc_mem_get(mctx, sizeof(*stats));
|
|
|
|
stats->counters = NULL;
|
|
isc_refcount_init(&stats->references, 1);
|
|
|
|
isc_stats_create(mctx, &stats->counters, ncounters);
|
|
|
|
stats->magic = DNS_STATS_MAGIC;
|
|
stats->type = type;
|
|
stats->mctx = NULL;
|
|
isc_mem_attach(mctx, &stats->mctx);
|
|
*statsp = stats;
|
|
}
|
|
|
|
void
|
|
dns_generalstats_create(isc_mem_t *mctx, dns_stats_t **statsp, int ncounters) {
|
|
REQUIRE(statsp != NULL && *statsp == NULL);
|
|
|
|
create_stats(mctx, dns_statstype_general, ncounters, statsp);
|
|
}
|
|
|
|
void
|
|
dns_rdatatypestats_create(isc_mem_t *mctx, isc_statsmulti_t **statsp) {
|
|
REQUIRE(statsp != NULL && *statsp == NULL);
|
|
|
|
isc_statsmulti_create(mctx, statsp, RDTYPECOUNTER_MAXTYPE + 1);
|
|
}
|
|
|
|
void
|
|
dns_rdatasetstats_create(isc_mem_t *mctx, dns_stats_t **statsp) {
|
|
REQUIRE(statsp != NULL && *statsp == NULL);
|
|
|
|
create_stats(mctx, dns_statstype_rdataset, RDTYPECOUNTER_MAXVAL + 1,
|
|
statsp);
|
|
}
|
|
|
|
void
|
|
dns_opcodestats_create(isc_mem_t *mctx, isc_statsmulti_t **statsp) {
|
|
REQUIRE(statsp != NULL && *statsp == NULL);
|
|
|
|
isc_statsmulti_create(mctx, statsp, 16);
|
|
}
|
|
|
|
void
|
|
dns_rcodestats_create(isc_mem_t *mctx, isc_statsmulti_t **statsp) {
|
|
REQUIRE(statsp != NULL && *statsp == NULL);
|
|
|
|
isc_statsmulti_create(mctx, statsp, dns_rcode_badcookie + 1);
|
|
}
|
|
|
|
void
|
|
dns_dnssecsignstats_create(isc_mem_t *mctx, dns_stats_t **statsp) {
|
|
REQUIRE(statsp != NULL && *statsp == NULL);
|
|
|
|
/*
|
|
* Create two counters per key, one is the key id, the other two are
|
|
* the actual counters for creating and refreshing signatures.
|
|
*/
|
|
create_stats(mctx, dns_statstype_dnssec,
|
|
dnssecsign_num_keys * dnssecsign_block_size, 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);
|
|
}
|
|
|
|
static isc_statscounter_t
|
|
rdatatype2counter(dns_rdatatype_t type) {
|
|
if (type > (dns_rdatatype_t)RDTYPECOUNTER_MAXTYPE) {
|
|
return 0;
|
|
}
|
|
return (isc_statscounter_t)type;
|
|
}
|
|
|
|
void
|
|
dns_rdatatypestats_increment(isc_statsmulti_t *stats, dns_rdatatype_t type) {
|
|
isc_statscounter_t counter;
|
|
|
|
REQUIRE(stats != NULL);
|
|
|
|
counter = rdatatype2counter(type);
|
|
isc_statsmulti_increment(stats, counter);
|
|
}
|
|
|
|
static void
|
|
update_rdatasetstats(dns_stats_t *stats, dns_rdatastatstype_t rrsettype,
|
|
bool increment) {
|
|
isc_statscounter_t counter;
|
|
|
|
if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
|
|
DNS_RDATASTATSTYPE_ATTR_NXDOMAIN) != 0)
|
|
{
|
|
counter = RDTYPECOUNTER_NXDOMAIN;
|
|
|
|
/*
|
|
* This is an NXDOMAIN counter, save the expiry value
|
|
* (active, stale, or ancient) value in the RRtype part.
|
|
*/
|
|
if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
|
|
DNS_RDATASTATSTYPE_ATTR_ANCIENT) != 0)
|
|
{
|
|
counter |= RDTYPECOUNTER_NXDOMAIN_ANCIENT;
|
|
} else if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
|
|
DNS_RDATASTATSTYPE_ATTR_STALE) != 0)
|
|
{
|
|
counter += RDTYPECOUNTER_NXDOMAIN_STALE;
|
|
}
|
|
} else {
|
|
counter = rdatatype2counter(DNS_RDATASTATSTYPE_BASE(rrsettype));
|
|
|
|
if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
|
|
DNS_RDATASTATSTYPE_ATTR_NXRRSET) != 0)
|
|
{
|
|
counter |= RDTYPECOUNTER_NXRRSET;
|
|
}
|
|
|
|
if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
|
|
DNS_RDATASTATSTYPE_ATTR_ANCIENT) != 0)
|
|
{
|
|
counter |= RDTYPECOUNTER_ANCIENT;
|
|
} else if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
|
|
DNS_RDATASTATSTYPE_ATTR_STALE) != 0)
|
|
{
|
|
counter |= RDTYPECOUNTER_STALE;
|
|
}
|
|
}
|
|
|
|
if (increment) {
|
|
isc_stats_increment(stats->counters, counter);
|
|
} else {
|
|
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(isc_statsmulti_t *stats, dns_opcode_t code) {
|
|
REQUIRE(stats != NULL);
|
|
|
|
isc_statsmulti_increment(stats, (isc_statscounter_t)code);
|
|
}
|
|
|
|
void
|
|
dns_rcodestats_increment(isc_statsmulti_t *stats, dns_rcode_t code) {
|
|
REQUIRE(stats != NULL);
|
|
|
|
if (code <= dns_rcode_badcookie) {
|
|
isc_statsmulti_increment(stats, (isc_statscounter_t)code);
|
|
}
|
|
}
|
|
|
|
void
|
|
dns_dnssecsignstats_increment(dns_stats_t *stats, dns_keytag_t id, uint8_t alg,
|
|
dnssecsignstats_type_t operation) {
|
|
uint32_t kval;
|
|
|
|
REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_dnssec);
|
|
|
|
int num_keys = isc_stats_ncounters(stats->counters) /
|
|
dnssecsign_block_size;
|
|
|
|
/* Shift algorithm in front of key tag, which is 16 bits */
|
|
kval = (uint32_t)(alg << 16 | id);
|
|
|
|
/* Look up correct counter. */
|
|
for (int i = 0; i < num_keys; i++) {
|
|
int idx = i * dnssecsign_block_size;
|
|
uint32_t counter = isc_stats_get_counter(stats->counters, idx);
|
|
if (counter == kval) {
|
|
/* Match */
|
|
isc_stats_increment(stats->counters, idx + operation);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* No match found. Store key in unused slot. */
|
|
for (int i = 0; i < num_keys; i++) {
|
|
int idx = i * dnssecsign_block_size;
|
|
uint32_t counter = isc_stats_get_counter(stats->counters, idx);
|
|
if (counter == 0) {
|
|
isc_stats_set(stats->counters, kval, idx);
|
|
isc_stats_increment(stats->counters, idx + operation);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* No room, grow stats storage. */
|
|
isc_stats_resize(&stats->counters,
|
|
num_keys * dnssecsign_block_size * 2);
|
|
|
|
/* Reset counters for new key (new index, nidx). */
|
|
int nidx = num_keys * dnssecsign_block_size;
|
|
isc_stats_set(stats->counters, kval, nidx);
|
|
isc_stats_set(stats->counters, 0, nidx + dns_dnssecsignstats_sign);
|
|
isc_stats_set(stats->counters, 0, nidx + dns_dnssecsignstats_refresh);
|
|
|
|
/* And increment the counter for the given operation. */
|
|
isc_stats_increment(stats->counters, nidx + operation);
|
|
}
|
|
|
|
void
|
|
dns_dnssecsignstats_clear(dns_stats_t *stats, dns_keytag_t id, uint8_t alg) {
|
|
uint32_t kval;
|
|
|
|
REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_dnssec);
|
|
|
|
int num_keys = isc_stats_ncounters(stats->counters) /
|
|
dnssecsign_block_size;
|
|
|
|
/* Shift algorithm in front of key tag, which is 16 bits */
|
|
kval = (uint32_t)(alg << 16 | id);
|
|
|
|
/* Look up correct counter. */
|
|
for (int i = 0; i < num_keys; i++) {
|
|
int idx = i * dnssecsign_block_size;
|
|
uint32_t counter = isc_stats_get_counter(stats->counters, idx);
|
|
if (counter == kval) {
|
|
/* Match */
|
|
isc_stats_set(stats->counters, 0, idx);
|
|
isc_stats_set(stats->counters, 0,
|
|
idx + dns_dnssecsignstats_sign);
|
|
isc_stats_set(stats->counters, 0,
|
|
idx + dns_dnssecsignstats_refresh);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*%
|
|
* 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_MAXTYPE) == 0) {
|
|
attributes |= DNS_RDATASTATSTYPE_ATTR_OTHERTYPE;
|
|
} else {
|
|
rdtype = (dns_rdatatype_t)(rdcounter & RDTYPECOUNTER_MAXTYPE);
|
|
}
|
|
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(isc_statsmulti_t *stats,
|
|
dns_rdatatypestats_dumper_t dump_fn, void *arg0,
|
|
unsigned int options) {
|
|
rdatadumparg_t arg;
|
|
REQUIRE(stats != NULL);
|
|
|
|
arg.fn = dump_fn;
|
|
arg.arg = arg0;
|
|
isc_statsmulti_dump(stats, rdatatype_dumpcb, &arg, options);
|
|
}
|
|
|
|
static void
|
|
rdataset_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
|
|
rdatadumparg_t *rdatadumparg = arg;
|
|
unsigned int attributes = 0;
|
|
|
|
if ((counter & RDTYPECOUNTER_NXDOMAIN) == RDTYPECOUNTER_NXDOMAIN) {
|
|
attributes |= DNS_RDATASTATSTYPE_ATTR_NXDOMAIN;
|
|
|
|
/*
|
|
* This is an NXDOMAIN counter, check the RRtype part for the
|
|
* expiry value (active, stale, or ancient).
|
|
*/
|
|
if ((counter & RDTYPECOUNTER_MAXTYPE) ==
|
|
RDTYPECOUNTER_NXDOMAIN_STALE)
|
|
{
|
|
attributes |= DNS_RDATASTATSTYPE_ATTR_STALE;
|
|
} else if ((counter & RDTYPECOUNTER_MAXTYPE) ==
|
|
RDTYPECOUNTER_NXDOMAIN_ANCIENT)
|
|
{
|
|
attributes |= DNS_RDATASTATSTYPE_ATTR_ANCIENT;
|
|
}
|
|
} else {
|
|
if ((counter & RDTYPECOUNTER_MAXTYPE) == 0) {
|
|
attributes |= DNS_RDATASTATSTYPE_ATTR_OTHERTYPE;
|
|
}
|
|
if ((counter & RDTYPECOUNTER_NXRRSET) != 0) {
|
|
attributes |= DNS_RDATASTATSTYPE_ATTR_NXRRSET;
|
|
}
|
|
|
|
if ((counter & RDTYPECOUNTER_STALE) != 0) {
|
|
attributes |= DNS_RDATASTATSTYPE_ATTR_STALE;
|
|
} else if ((counter & RDTYPECOUNTER_ANCIENT) != 0) {
|
|
attributes |= DNS_RDATASTATSTYPE_ATTR_ANCIENT;
|
|
}
|
|
}
|
|
|
|
dump_rdentry(counter, 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
|
|
dnssec_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
|
|
dnssecsigndumparg_t *dnssecarg = arg;
|
|
|
|
dnssecarg->fn((uint32_t)counter, value, dnssecarg->arg);
|
|
}
|
|
|
|
static void
|
|
dnssec_statsdump(isc_stats_t *stats, dnssecsignstats_type_t operation,
|
|
isc_stats_dumper_t dump_fn, void *arg, unsigned int options) {
|
|
int i, num_keys;
|
|
|
|
num_keys = isc_stats_ncounters(stats) / dnssecsign_block_size;
|
|
for (i = 0; i < num_keys; i++) {
|
|
int idx = dnssecsign_block_size * i;
|
|
uint32_t kval, val;
|
|
|
|
kval = isc_stats_get_counter(stats, idx);
|
|
if (kval == 0) {
|
|
continue;
|
|
}
|
|
|
|
val = isc_stats_get_counter(stats, idx + operation);
|
|
if ((options & ISC_STATSDUMP_VERBOSE) == 0 && val == 0) {
|
|
continue;
|
|
}
|
|
|
|
dump_fn(kval, val, arg);
|
|
}
|
|
}
|
|
|
|
void
|
|
dns_dnssecsignstats_dump(dns_stats_t *stats, dnssecsignstats_type_t operation,
|
|
dns_dnssecsignstats_dumper_t dump_fn, void *arg0,
|
|
unsigned int options) {
|
|
dnssecsigndumparg_t arg;
|
|
|
|
REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_dnssec);
|
|
|
|
arg.fn = dump_fn;
|
|
arg.arg = arg0;
|
|
|
|
dnssec_statsdump(stats->counters, operation, dnssec_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(isc_statsmulti_t *stats, dns_opcodestats_dumper_t dump_fn,
|
|
void *arg0, unsigned int options) {
|
|
opcodedumparg_t arg;
|
|
|
|
REQUIRE(stats != NULL);
|
|
|
|
arg.fn = dump_fn;
|
|
arg.arg = arg0;
|
|
isc_statsmulti_dump(stats, opcode_dumpcb, &arg, options);
|
|
}
|
|
|
|
void
|
|
dns_rcodestats_dump(isc_statsmulti_t *stats, dns_rcodestats_dumper_t dump_fn,
|
|
void *arg0, unsigned int options) {
|
|
rcodedumparg_t arg;
|
|
|
|
REQUIRE(stats != NULL);
|
|
|
|
arg.fn = dump_fn;
|
|
arg.arg = arg0;
|
|
isc_statsmulti_dump(stats, rcode_dumpcb, &arg, options);
|
|
}
|