Coverage Report

Created: 2026-06-15 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/stats.c
Line
Count
Source
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
5
 *
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 *
10
 * See the COPYRIGHT file distributed with this work for additional
11
 * information regarding copyright ownership.
12
 */
13
14
/*! \file */
15
16
#include <inttypes.h>
17
#include <stdbool.h>
18
19
#include <isc/log.h>
20
#include <isc/magic.h>
21
#include <isc/mem.h>
22
#include <isc/refcount.h>
23
#include <isc/stats.h>
24
#include <isc/statsmulti.h>
25
#include <isc/util.h>
26
27
#include <dns/opcode.h>
28
#include <dns/rdatatype.h>
29
#include <dns/stats.h>
30
31
0
#define DNS_STATS_MAGIC    ISC_MAGIC('D', 's', 't', 't')
32
#define DNS_STATS_VALID(x) ISC_MAGIC_VALID(x, DNS_STATS_MAGIC)
33
34
/*%
35
 * Statistics types.
36
 */
37
typedef enum {
38
  dns_statstype_general = 0,
39
  dns_statstype_rdtype = 1,
40
  dns_statstype_rdataset = 2,
41
  dns_statstype_opcode = 3,
42
  dns_statstype_rcode = 4,
43
  dns_statstype_dnssec = 5
44
} dns_statstype_t;
45
46
/*%
47
 * It doesn't make sense to have 2^16 counters for all possible types since
48
 * most of them won't be used.  We have counters for the first 256 types.
49
 *
50
 * A rdtypecounter is now 8 bits for RRtypes and 3 bits for flags:
51
 *
52
 *       0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
53
 *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
54
 *     |  |  |  |  |  |  S  |NX|         RRType        |
55
 *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
56
 *
57
 * If the 8 bits for RRtype are all zero, this is an Other RRtype.
58
 */
59
0
#define RDTYPECOUNTER_MAXTYPE 0x00ff
60
61
/*
62
 *
63
 * Bit 7 is the NXRRSET (NX) flag and indicates whether this is a
64
 * positive (0) or a negative (1) RRset.
65
 */
66
0
#define RDTYPECOUNTER_NXRRSET 0x0100
67
68
/*
69
 * Then bit 5 and 6 mostly tell you if this counter is for an active,
70
 * stale, or ancient RRtype:
71
 *
72
 *     S = 0 (0b00) means Active
73
 *     S = 1 (0b01) means Stale
74
 *     S = 2 (0b10) means Ancient
75
 *
76
 * Since a counter cannot be stale and ancient at the same time, we
77
 * treat S = 0b11 as a special case to deal with NXDOMAIN counters.
78
 */
79
0
#define RDTYPECOUNTER_STALE    (1 << 9)
80
0
#define RDTYPECOUNTER_ANCIENT  (1 << 10)
81
0
#define RDTYPECOUNTER_NXDOMAIN ((1 << 9) | (1 << 10))
82
83
/*
84
 * S = 0b11 indicates an NXDOMAIN counter and in this case the RRtype
85
 * field signals the expiry of this cached item:
86
 *
87
 *     RRType = 0 (0b00) means Active
88
 *     RRType = 1 (0b01) means Stale
89
 *     RRType = 2 (0b02) means Ancient
90
 *
91
 */
92
0
#define RDTYPECOUNTER_NXDOMAIN_STALE   1
93
0
#define RDTYPECOUNTER_NXDOMAIN_ANCIENT 2
94
95
/*
96
 * The maximum value for rdtypecounter is for an ancient NXDOMAIN.
97
 */
98
0
#define RDTYPECOUNTER_MAXVAL 0x0602
99
100
/*
101
 * DNSSEC sign statistics.
102
 *
103
 * Per key we maintain 3 counters. The first is actually no counter but
104
 * a key id reference. The second is the number of signatures the key created.
105
 * The third is the number of signatures refreshed by the key.
106
 */
107
108
/* Maximum number of keys to keep track of for DNSSEC signing statistics. */
109
static int dnssecsign_num_keys = 4;
110
static int dnssecsign_block_size = 3;
111
112
struct dns_stats {
113
  unsigned int magic;
114
  dns_statstype_t type;
115
  isc_mem_t *mctx;
116
  isc_stats_t *counters;
117
  isc_refcount_t references;
118
};
119
120
typedef struct rdatadumparg {
121
  dns_rdatatypestats_dumper_t fn;
122
  void *arg;
123
} rdatadumparg_t;
124
125
typedef struct opcodedumparg {
126
  dns_opcodestats_dumper_t fn;
127
  void *arg;
128
} opcodedumparg_t;
129
130
typedef struct rcodedumparg {
131
  dns_rcodestats_dumper_t fn;
132
  void *arg;
133
} rcodedumparg_t;
134
typedef struct dnssecsigndumparg {
135
  dns_dnssecsignstats_dumper_t fn;
136
  void *arg;
137
} dnssecsigndumparg_t;
138
139
void
140
0
dns_stats_attach(dns_stats_t *stats, dns_stats_t **statsp) {
141
0
  REQUIRE(DNS_STATS_VALID(stats));
142
0
  REQUIRE(statsp != NULL && *statsp == NULL);
143
144
0
  isc_refcount_increment(&stats->references);
145
146
0
  *statsp = stats;
147
0
}
148
149
void
150
0
dns_stats_detach(dns_stats_t **statsp) {
151
0
  dns_stats_t *stats;
152
153
0
  REQUIRE(statsp != NULL && DNS_STATS_VALID(*statsp));
154
155
0
  stats = *statsp;
156
0
  *statsp = NULL;
157
158
0
  if (isc_refcount_decrement(&stats->references) == 1) {
159
0
    isc_refcount_destroy(&stats->references);
160
0
    isc_stats_detach(&stats->counters);
161
0
    isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats));
162
0
  }
163
0
}
164
165
/*%
166
 * Create methods
167
 */
168
static void
169
create_stats(isc_mem_t *mctx, dns_statstype_t type, int ncounters,
170
0
       dns_stats_t **statsp) {
171
0
  dns_stats_t *stats = isc_mem_get(mctx, sizeof(*stats));
172
173
0
  stats->counters = NULL;
174
0
  isc_refcount_init(&stats->references, 1);
175
176
0
  isc_stats_create(mctx, &stats->counters, ncounters);
177
178
0
  stats->magic = DNS_STATS_MAGIC;
179
0
  stats->type = type;
180
0
  stats->mctx = NULL;
181
0
  isc_mem_attach(mctx, &stats->mctx);
182
0
  *statsp = stats;
183
0
}
184
185
void
186
0
dns_generalstats_create(isc_mem_t *mctx, dns_stats_t **statsp, int ncounters) {
187
0
  REQUIRE(statsp != NULL && *statsp == NULL);
188
189
0
  create_stats(mctx, dns_statstype_general, ncounters, statsp);
190
0
}
191
192
void
193
0
dns_rdatatypestats_create(isc_mem_t *mctx, isc_statsmulti_t **statsp) {
194
0
  REQUIRE(statsp != NULL && *statsp == NULL);
195
196
0
  isc_statsmulti_create(mctx, statsp, RDTYPECOUNTER_MAXTYPE + 1);
197
0
}
198
199
void
200
0
dns_rdatasetstats_create(isc_mem_t *mctx, dns_stats_t **statsp) {
201
0
  REQUIRE(statsp != NULL && *statsp == NULL);
202
203
0
  create_stats(mctx, dns_statstype_rdataset, RDTYPECOUNTER_MAXVAL + 1,
204
0
         statsp);
205
0
}
206
207
void
208
0
dns_opcodestats_create(isc_mem_t *mctx, isc_statsmulti_t **statsp) {
209
0
  REQUIRE(statsp != NULL && *statsp == NULL);
210
211
0
  isc_statsmulti_create(mctx, statsp, 16);
212
0
}
213
214
void
215
0
dns_rcodestats_create(isc_mem_t *mctx, isc_statsmulti_t **statsp) {
216
0
  REQUIRE(statsp != NULL && *statsp == NULL);
217
218
0
  isc_statsmulti_create(mctx, statsp, dns_rcode_badcookie + 1);
219
0
}
220
221
void
222
0
dns_dnssecsignstats_create(isc_mem_t *mctx, dns_stats_t **statsp) {
223
0
  REQUIRE(statsp != NULL && *statsp == NULL);
224
225
  /*
226
   * Create two counters per key, one is the key id, the other two are
227
   * the actual counters for creating and refreshing signatures.
228
   */
229
0
  create_stats(mctx, dns_statstype_dnssec,
230
0
         dnssecsign_num_keys * dnssecsign_block_size, statsp);
231
0
}
232
233
/*%
234
 * Increment/Decrement methods
235
 */
236
void
237
0
dns_generalstats_increment(dns_stats_t *stats, isc_statscounter_t counter) {
238
0
  REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_general);
239
240
0
  isc_stats_increment(stats->counters, counter);
241
0
}
242
243
static isc_statscounter_t
244
0
rdatatype2counter(dns_rdatatype_t type) {
245
0
  if (type > (dns_rdatatype_t)RDTYPECOUNTER_MAXTYPE) {
246
0
    return 0;
247
0
  }
248
0
  return (isc_statscounter_t)type;
249
0
}
250
251
void
252
0
dns_rdatatypestats_increment(isc_statsmulti_t *stats, dns_rdatatype_t type) {
253
0
  isc_statscounter_t counter;
254
255
0
  REQUIRE(stats != NULL);
256
257
0
  counter = rdatatype2counter(type);
258
0
  isc_statsmulti_increment(stats, counter);
259
0
}
260
261
static void
262
update_rdatasetstats(dns_stats_t *stats, dns_rdatastatstype_t rrsettype,
263
0
         bool increment) {
264
0
  isc_statscounter_t counter;
265
266
0
  if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
267
0
       DNS_RDATASTATSTYPE_ATTR_NXDOMAIN) != 0)
268
0
  {
269
0
    counter = RDTYPECOUNTER_NXDOMAIN;
270
271
    /*
272
     * This is an NXDOMAIN counter, save the expiry value
273
     * (active, stale, or ancient) value in the RRtype part.
274
     */
275
0
    if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
276
0
         DNS_RDATASTATSTYPE_ATTR_ANCIENT) != 0)
277
0
    {
278
0
      counter |= RDTYPECOUNTER_NXDOMAIN_ANCIENT;
279
0
    } else if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
280
0
          DNS_RDATASTATSTYPE_ATTR_STALE) != 0)
281
0
    {
282
0
      counter += RDTYPECOUNTER_NXDOMAIN_STALE;
283
0
    }
284
0
  } else {
285
0
    counter = rdatatype2counter(DNS_RDATASTATSTYPE_BASE(rrsettype));
286
287
0
    if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
288
0
         DNS_RDATASTATSTYPE_ATTR_NXRRSET) != 0)
289
0
    {
290
0
      counter |= RDTYPECOUNTER_NXRRSET;
291
0
    }
292
293
0
    if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
294
0
         DNS_RDATASTATSTYPE_ATTR_ANCIENT) != 0)
295
0
    {
296
0
      counter |= RDTYPECOUNTER_ANCIENT;
297
0
    } else if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
298
0
          DNS_RDATASTATSTYPE_ATTR_STALE) != 0)
299
0
    {
300
0
      counter |= RDTYPECOUNTER_STALE;
301
0
    }
302
0
  }
303
304
0
  if (increment) {
305
0
    isc_stats_increment(stats->counters, counter);
306
0
  } else {
307
0
    isc_stats_decrement(stats->counters, counter);
308
0
  }
309
0
}
310
311
void
312
dns_rdatasetstats_increment(dns_stats_t *stats,
313
0
          dns_rdatastatstype_t rrsettype) {
314
0
  REQUIRE(DNS_STATS_VALID(stats) &&
315
0
    stats->type == dns_statstype_rdataset);
316
317
0
  update_rdatasetstats(stats, rrsettype, true);
318
0
}
319
320
void
321
dns_rdatasetstats_decrement(dns_stats_t *stats,
322
0
          dns_rdatastatstype_t rrsettype) {
323
0
  REQUIRE(DNS_STATS_VALID(stats) &&
324
0
    stats->type == dns_statstype_rdataset);
325
326
0
  update_rdatasetstats(stats, rrsettype, false);
327
0
}
328
329
void
330
0
dns_opcodestats_increment(isc_statsmulti_t *stats, dns_opcode_t code) {
331
0
  REQUIRE(stats != NULL);
332
333
0
  isc_statsmulti_increment(stats, (isc_statscounter_t)code);
334
0
}
335
336
void
337
0
dns_rcodestats_increment(isc_statsmulti_t *stats, dns_rcode_t code) {
338
0
  REQUIRE(stats != NULL);
339
340
0
  if (code <= dns_rcode_badcookie) {
341
0
    isc_statsmulti_increment(stats, (isc_statscounter_t)code);
342
0
  }
343
0
}
344
345
void
346
dns_dnssecsignstats_increment(dns_stats_t *stats, dns_keytag_t id, uint8_t alg,
347
0
            dnssecsignstats_type_t operation) {
348
0
  uint32_t kval;
349
350
0
  REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_dnssec);
351
352
0
  int num_keys = isc_stats_ncounters(stats->counters) /
353
0
           dnssecsign_block_size;
354
355
  /* Shift algorithm in front of key tag, which is 16 bits */
356
0
  kval = (uint32_t)(alg << 16 | id);
357
358
  /* Look up correct counter. */
359
0
  for (int i = 0; i < num_keys; i++) {
360
0
    int idx = i * dnssecsign_block_size;
361
0
    uint32_t counter = isc_stats_get_counter(stats->counters, idx);
362
0
    if (counter == kval) {
363
      /* Match */
364
0
      isc_stats_increment(stats->counters, idx + operation);
365
0
      return;
366
0
    }
367
0
  }
368
369
  /* No match found. Store key in unused slot. */
370
0
  for (int i = 0; i < num_keys; i++) {
371
0
    int idx = i * dnssecsign_block_size;
372
0
    uint32_t counter = isc_stats_get_counter(stats->counters, idx);
373
0
    if (counter == 0) {
374
0
      isc_stats_set(stats->counters, kval, idx);
375
0
      isc_stats_increment(stats->counters, idx + operation);
376
0
      return;
377
0
    }
378
0
  }
379
380
  /* No room, grow stats storage. */
381
0
  isc_stats_resize(&stats->counters,
382
0
       num_keys * dnssecsign_block_size * 2);
383
384
  /* Reset counters for new key (new index, nidx). */
385
0
  int nidx = num_keys * dnssecsign_block_size;
386
0
  isc_stats_set(stats->counters, kval, nidx);
387
0
  isc_stats_set(stats->counters, 0, nidx + dns_dnssecsignstats_sign);
388
0
  isc_stats_set(stats->counters, 0, nidx + dns_dnssecsignstats_refresh);
389
390
  /* And increment the counter for the given operation. */
391
0
  isc_stats_increment(stats->counters, nidx + operation);
392
0
}
393
394
void
395
0
dns_dnssecsignstats_clear(dns_stats_t *stats, dns_keytag_t id, uint8_t alg) {
396
0
  uint32_t kval;
397
398
0
  REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_dnssec);
399
400
0
  int num_keys = isc_stats_ncounters(stats->counters) /
401
0
           dnssecsign_block_size;
402
403
  /* Shift algorithm in front of key tag, which is 16 bits */
404
0
  kval = (uint32_t)(alg << 16 | id);
405
406
  /* Look up correct counter. */
407
0
  for (int i = 0; i < num_keys; i++) {
408
0
    int idx = i * dnssecsign_block_size;
409
0
    uint32_t counter = isc_stats_get_counter(stats->counters, idx);
410
0
    if (counter == kval) {
411
      /* Match */
412
0
      isc_stats_set(stats->counters, 0, idx);
413
0
      isc_stats_set(stats->counters, 0,
414
0
              idx + dns_dnssecsignstats_sign);
415
0
      isc_stats_set(stats->counters, 0,
416
0
              idx + dns_dnssecsignstats_refresh);
417
0
      return;
418
0
    }
419
0
  }
420
0
}
421
422
/*%
423
 * Dump methods
424
 */
425
void
426
dns_generalstats_dump(dns_stats_t *stats, dns_generalstats_dumper_t dump_fn,
427
0
          void *arg, unsigned int options) {
428
0
  REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_general);
429
430
0
  isc_stats_dump(stats->counters, (isc_stats_dumper_t)dump_fn, arg,
431
0
           options);
432
0
}
433
434
static void
435
dump_rdentry(int rdcounter, uint64_t value, dns_rdatastatstype_t attributes,
436
0
       dns_rdatatypestats_dumper_t dump_fn, void *arg) {
437
0
  dns_rdatatype_t rdtype = dns_rdatatype_none; /* sentinel */
438
0
  dns_rdatastatstype_t type;
439
440
0
  if ((rdcounter & RDTYPECOUNTER_MAXTYPE) == 0) {
441
0
    attributes |= DNS_RDATASTATSTYPE_ATTR_OTHERTYPE;
442
0
  } else {
443
0
    rdtype = (dns_rdatatype_t)(rdcounter & RDTYPECOUNTER_MAXTYPE);
444
0
  }
445
0
  type = DNS_RDATASTATSTYPE_VALUE((dns_rdatastatstype_t)rdtype,
446
0
          attributes);
447
0
  dump_fn(type, value, arg);
448
0
}
449
450
static void
451
0
rdatatype_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
452
0
  rdatadumparg_t *rdatadumparg = arg;
453
454
0
  dump_rdentry(counter, value, 0, rdatadumparg->fn, rdatadumparg->arg);
455
0
}
456
457
void
458
dns_rdatatypestats_dump(isc_statsmulti_t *stats,
459
      dns_rdatatypestats_dumper_t dump_fn, void *arg0,
460
0
      unsigned int options) {
461
0
  rdatadumparg_t arg;
462
0
  REQUIRE(stats != NULL);
463
464
0
  arg.fn = dump_fn;
465
0
  arg.arg = arg0;
466
0
  isc_statsmulti_dump(stats, rdatatype_dumpcb, &arg, options);
467
0
}
468
469
static void
470
0
rdataset_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
471
0
  rdatadumparg_t *rdatadumparg = arg;
472
0
  unsigned int attributes = 0;
473
474
0
  if ((counter & RDTYPECOUNTER_NXDOMAIN) == RDTYPECOUNTER_NXDOMAIN) {
475
0
    attributes |= DNS_RDATASTATSTYPE_ATTR_NXDOMAIN;
476
477
    /*
478
     * This is an NXDOMAIN counter, check the RRtype part for the
479
     * expiry value (active, stale, or ancient).
480
     */
481
0
    if ((counter & RDTYPECOUNTER_MAXTYPE) ==
482
0
        RDTYPECOUNTER_NXDOMAIN_STALE)
483
0
    {
484
0
      attributes |= DNS_RDATASTATSTYPE_ATTR_STALE;
485
0
    } else if ((counter & RDTYPECOUNTER_MAXTYPE) ==
486
0
         RDTYPECOUNTER_NXDOMAIN_ANCIENT)
487
0
    {
488
0
      attributes |= DNS_RDATASTATSTYPE_ATTR_ANCIENT;
489
0
    }
490
0
  } else {
491
0
    if ((counter & RDTYPECOUNTER_MAXTYPE) == 0) {
492
0
      attributes |= DNS_RDATASTATSTYPE_ATTR_OTHERTYPE;
493
0
    }
494
0
    if ((counter & RDTYPECOUNTER_NXRRSET) != 0) {
495
0
      attributes |= DNS_RDATASTATSTYPE_ATTR_NXRRSET;
496
0
    }
497
498
0
    if ((counter & RDTYPECOUNTER_STALE) != 0) {
499
0
      attributes |= DNS_RDATASTATSTYPE_ATTR_STALE;
500
0
    } else if ((counter & RDTYPECOUNTER_ANCIENT) != 0) {
501
0
      attributes |= DNS_RDATASTATSTYPE_ATTR_ANCIENT;
502
0
    }
503
0
  }
504
505
0
  dump_rdentry(counter, value, attributes, rdatadumparg->fn,
506
0
         rdatadumparg->arg);
507
0
}
508
509
void
510
dns_rdatasetstats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn,
511
0
           void *arg0, unsigned int options) {
512
0
  rdatadumparg_t arg;
513
514
0
  REQUIRE(DNS_STATS_VALID(stats) &&
515
0
    stats->type == dns_statstype_rdataset);
516
517
0
  arg.fn = dump_fn;
518
0
  arg.arg = arg0;
519
0
  isc_stats_dump(stats->counters, rdataset_dumpcb, &arg, options);
520
0
}
521
522
static void
523
0
dnssec_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
524
0
  dnssecsigndumparg_t *dnssecarg = arg;
525
526
0
  dnssecarg->fn((uint32_t)counter, value, dnssecarg->arg);
527
0
}
528
529
static void
530
dnssec_statsdump(isc_stats_t *stats, dnssecsignstats_type_t operation,
531
0
     isc_stats_dumper_t dump_fn, void *arg, unsigned int options) {
532
0
  int i, num_keys;
533
534
0
  num_keys = isc_stats_ncounters(stats) / dnssecsign_block_size;
535
0
  for (i = 0; i < num_keys; i++) {
536
0
    int idx = dnssecsign_block_size * i;
537
0
    uint32_t kval, val;
538
539
0
    kval = isc_stats_get_counter(stats, idx);
540
0
    if (kval == 0) {
541
0
      continue;
542
0
    }
543
544
0
    val = isc_stats_get_counter(stats, idx + operation);
545
0
    if ((options & ISC_STATSDUMP_VERBOSE) == 0 && val == 0) {
546
0
      continue;
547
0
    }
548
549
0
    dump_fn(kval, val, arg);
550
0
  }
551
0
}
552
553
void
554
dns_dnssecsignstats_dump(dns_stats_t *stats, dnssecsignstats_type_t operation,
555
       dns_dnssecsignstats_dumper_t dump_fn, void *arg0,
556
0
       unsigned int options) {
557
0
  dnssecsigndumparg_t arg;
558
559
0
  REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_dnssec);
560
561
0
  arg.fn = dump_fn;
562
0
  arg.arg = arg0;
563
564
0
  dnssec_statsdump(stats->counters, operation, dnssec_dumpcb, &arg,
565
0
       options);
566
0
}
567
568
static void
569
0
opcode_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
570
0
  opcodedumparg_t *opcodearg = arg;
571
572
0
  opcodearg->fn((dns_opcode_t)counter, value, opcodearg->arg);
573
0
}
574
575
static void
576
0
rcode_dumpcb(isc_statscounter_t counter, uint64_t value, void *arg) {
577
0
  rcodedumparg_t *rcodearg = arg;
578
579
0
  rcodearg->fn((dns_rcode_t)counter, value, rcodearg->arg);
580
0
}
581
582
void
583
dns_opcodestats_dump(isc_statsmulti_t *stats, dns_opcodestats_dumper_t dump_fn,
584
0
         void *arg0, unsigned int options) {
585
0
  opcodedumparg_t arg;
586
587
0
  REQUIRE(stats != NULL);
588
589
0
  arg.fn = dump_fn;
590
0
  arg.arg = arg0;
591
0
  isc_statsmulti_dump(stats, opcode_dumpcb, &arg, options);
592
0
}
593
594
void
595
dns_rcodestats_dump(isc_statsmulti_t *stats, dns_rcodestats_dumper_t dump_fn,
596
0
        void *arg0, unsigned int options) {
597
0
  rcodedumparg_t arg;
598
599
0
  REQUIRE(stats != NULL);
600
601
0
  arg.fn = dump_fn;
602
0
  arg.arg = arg0;
603
0
  isc_statsmulti_dump(stats, rcode_dumpcb, &arg, options);
604
0
}