1135446Strhodes/* 2193149Sdougb * Copyright (C) 2004, 2005, 2007-2009 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 2000, 2001 Internet Software Consortium. 4135446Strhodes * 5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18234010Sdougb/* $Id: stats.c,v 1.18 2009/01/27 23:47:54 tbox Exp $ */ 19135446Strhodes 20170222Sdougb/*! \file */ 21170222Sdougb 22135446Strhodes#include <config.h> 23135446Strhodes 24193149Sdougb#include <isc/magic.h> 25135446Strhodes#include <isc/mem.h> 26193149Sdougb#include <isc/stats.h> 27193149Sdougb#include <isc/util.h> 28135446Strhodes 29193149Sdougb#include <dns/opcode.h> 30193149Sdougb#include <dns/rdatatype.h> 31135446Strhodes#include <dns/stats.h> 32135446Strhodes 33193149Sdougb#define DNS_STATS_MAGIC ISC_MAGIC('D', 's', 't', 't') 34193149Sdougb#define DNS_STATS_VALID(x) ISC_MAGIC_VALID(x, DNS_STATS_MAGIC) 35193149Sdougb 36193149Sdougb/*% 37193149Sdougb * Statistics types. 38193149Sdougb */ 39193149Sdougbtypedef enum { 40193149Sdougb dns_statstype_general = 0, 41193149Sdougb dns_statstype_rdtype = 1, 42193149Sdougb dns_statstype_rdataset = 2, 43193149Sdougb dns_statstype_opcode = 3 44193149Sdougb} dns_statstype_t; 45193149Sdougb 46193149Sdougb/*% 47193149Sdougb * It doesn't make sense to have 2^16 counters for all possible types since 48193149Sdougb * most of them won't be used. We have counters for the first 256 types and 49193149Sdougb * those explicitly supported in the rdata implementation. 50193149Sdougb * XXXJT: this introduces tight coupling with the rdata implementation. 51193149Sdougb * Ideally, we should have rdata handle this type of details. 52193149Sdougb */ 53193149Sdougbenum { 54193149Sdougb /* For 0-255, we use the rdtype value as counter indices */ 55193149Sdougb rdtypecounter_dlv = 256, /* for dns_rdatatype_dlv */ 56193149Sdougb rdtypecounter_others = 257, /* anything else */ 57193149Sdougb rdtypecounter_max = 258, 58193149Sdougb /* The following are used for rdataset */ 59193149Sdougb rdtypenxcounter_max = rdtypecounter_max * 2, 60193149Sdougb rdtypecounter_nxdomain = rdtypenxcounter_max, 61193149Sdougb rdatasettypecounter_max = rdtypecounter_nxdomain + 1 62193149Sdougb}; 63193149Sdougb 64193149Sdougbstruct dns_stats { 65193149Sdougb /*% Unlocked */ 66193149Sdougb unsigned int magic; 67193149Sdougb dns_statstype_t type; 68193149Sdougb isc_mem_t *mctx; 69193149Sdougb isc_mutex_t lock; 70193149Sdougb isc_stats_t *counters; 71193149Sdougb 72193149Sdougb /*% Locked by lock */ 73193149Sdougb unsigned int references; 74193149Sdougb}; 75193149Sdougb 76193149Sdougbtypedef struct rdatadumparg { 77193149Sdougb dns_rdatatypestats_dumper_t fn; 78193149Sdougb void *arg; 79193149Sdougb} rdatadumparg_t; 80193149Sdougb 81193149Sdougbtypedef struct opcodedumparg { 82193149Sdougb dns_opcodestats_dumper_t fn; 83193149Sdougb void *arg; 84193149Sdougb} opcodedumparg_t; 85193149Sdougb 86193149Sdougbvoid 87193149Sdougbdns_stats_attach(dns_stats_t *stats, dns_stats_t **statsp) { 88193149Sdougb REQUIRE(DNS_STATS_VALID(stats)); 89193149Sdougb REQUIRE(statsp != NULL && *statsp == NULL); 90193149Sdougb 91193149Sdougb LOCK(&stats->lock); 92193149Sdougb stats->references++; 93193149Sdougb UNLOCK(&stats->lock); 94193149Sdougb 95193149Sdougb *statsp = stats; 96193149Sdougb} 97193149Sdougb 98193149Sdougbvoid 99193149Sdougbdns_stats_detach(dns_stats_t **statsp) { 100193149Sdougb dns_stats_t *stats; 101193149Sdougb 102193149Sdougb REQUIRE(statsp != NULL && DNS_STATS_VALID(*statsp)); 103193149Sdougb 104193149Sdougb stats = *statsp; 105193149Sdougb *statsp = NULL; 106193149Sdougb 107193149Sdougb LOCK(&stats->lock); 108193149Sdougb stats->references--; 109193149Sdougb UNLOCK(&stats->lock); 110193149Sdougb 111193149Sdougb if (stats->references == 0) { 112193149Sdougb isc_stats_detach(&stats->counters); 113193149Sdougb DESTROYLOCK(&stats->lock); 114193149Sdougb isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats)); 115193149Sdougb } 116193149Sdougb} 117193149Sdougb 118193149Sdougb/*% 119193149Sdougb * Create methods 120193149Sdougb */ 121193149Sdougbstatic isc_result_t 122193149Sdougbcreate_stats(isc_mem_t *mctx, dns_statstype_t type, int ncounters, 123193149Sdougb dns_stats_t **statsp) 124193149Sdougb{ 125193149Sdougb dns_stats_t *stats; 126193149Sdougb isc_result_t result; 127193149Sdougb 128193149Sdougb stats = isc_mem_get(mctx, sizeof(*stats)); 129193149Sdougb if (stats == NULL) 130193149Sdougb return (ISC_R_NOMEMORY); 131193149Sdougb 132193149Sdougb stats->counters = NULL; 133193149Sdougb stats->references = 1; 134193149Sdougb 135193149Sdougb result = isc_mutex_init(&stats->lock); 136193149Sdougb if (result != ISC_R_SUCCESS) 137193149Sdougb goto clean_stats; 138193149Sdougb 139193149Sdougb result = isc_stats_create(mctx, &stats->counters, ncounters); 140193149Sdougb if (result != ISC_R_SUCCESS) 141193149Sdougb goto clean_mutex; 142193149Sdougb 143193149Sdougb stats->magic = DNS_STATS_MAGIC; 144193149Sdougb stats->type = type; 145193149Sdougb stats->mctx = NULL; 146193149Sdougb isc_mem_attach(mctx, &stats->mctx); 147193149Sdougb *statsp = stats; 148193149Sdougb 149193149Sdougb return (ISC_R_SUCCESS); 150193149Sdougb 151193149Sdougb clean_mutex: 152193149Sdougb DESTROYLOCK(&stats->lock); 153193149Sdougb clean_stats: 154193149Sdougb isc_mem_put(mctx, stats, sizeof(*stats)); 155193149Sdougb 156193149Sdougb return (result); 157193149Sdougb} 158193149Sdougb 159193149Sdougbisc_result_t 160193149Sdougbdns_generalstats_create(isc_mem_t *mctx, dns_stats_t **statsp, int ncounters) { 161193149Sdougb REQUIRE(statsp != NULL && *statsp == NULL); 162193149Sdougb 163193149Sdougb return (create_stats(mctx, dns_statstype_general, ncounters, statsp)); 164193149Sdougb} 165193149Sdougb 166193149Sdougbisc_result_t 167193149Sdougbdns_rdatatypestats_create(isc_mem_t *mctx, dns_stats_t **statsp) { 168193149Sdougb REQUIRE(statsp != NULL && *statsp == NULL); 169193149Sdougb 170193149Sdougb return (create_stats(mctx, dns_statstype_rdtype, rdtypecounter_max, 171193149Sdougb statsp)); 172193149Sdougb} 173193149Sdougb 174193149Sdougbisc_result_t 175193149Sdougbdns_rdatasetstats_create(isc_mem_t *mctx, dns_stats_t **statsp) { 176193149Sdougb REQUIRE(statsp != NULL && *statsp == NULL); 177193149Sdougb 178193149Sdougb return (create_stats(mctx, dns_statstype_rdataset, 179193149Sdougb (rdtypecounter_max * 2) + 1, statsp)); 180193149Sdougb} 181193149Sdougb 182193149Sdougbisc_result_t 183193149Sdougbdns_opcodestats_create(isc_mem_t *mctx, dns_stats_t **statsp) { 184193149Sdougb REQUIRE(statsp != NULL && *statsp == NULL); 185193149Sdougb 186193149Sdougb return (create_stats(mctx, dns_statstype_opcode, 16, statsp)); 187193149Sdougb} 188193149Sdougb 189193149Sdougb/*% 190193149Sdougb * Increment/Decrement methods 191193149Sdougb */ 192193149Sdougbvoid 193193149Sdougbdns_generalstats_increment(dns_stats_t *stats, isc_statscounter_t counter) { 194193149Sdougb REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_general); 195193149Sdougb 196193149Sdougb isc_stats_increment(stats->counters, counter); 197193149Sdougb} 198193149Sdougb 199193149Sdougbvoid 200193149Sdougbdns_rdatatypestats_increment(dns_stats_t *stats, dns_rdatatype_t type) { 201193149Sdougb int counter; 202193149Sdougb 203193149Sdougb REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rdtype); 204193149Sdougb 205193149Sdougb if (type == dns_rdatatype_dlv) 206193149Sdougb counter = rdtypecounter_dlv; 207193149Sdougb else if (type > dns_rdatatype_any) 208193149Sdougb counter = rdtypecounter_others; 209193149Sdougb else 210193149Sdougb counter = (int)type; 211193149Sdougb 212193149Sdougb isc_stats_increment(stats->counters, (isc_statscounter_t)counter); 213193149Sdougb} 214193149Sdougb 215193149Sdougbstatic inline void 216193149Sdougbupdate_rdatasetstats(dns_stats_t *stats, dns_rdatastatstype_t rrsettype, 217193149Sdougb isc_boolean_t increment) 218193149Sdougb{ 219193149Sdougb int counter; 220193149Sdougb dns_rdatatype_t rdtype; 221193149Sdougb 222193149Sdougb if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) & 223193149Sdougb DNS_RDATASTATSTYPE_ATTR_NXDOMAIN) != 0) { 224193149Sdougb counter = rdtypecounter_nxdomain; 225193149Sdougb } else { 226193149Sdougb rdtype = DNS_RDATASTATSTYPE_BASE(rrsettype); 227193149Sdougb if (rdtype == dns_rdatatype_dlv) 228193149Sdougb counter = (int)rdtypecounter_dlv; 229193149Sdougb else if (rdtype > dns_rdatatype_any) 230193149Sdougb counter = (int)rdtypecounter_others; 231193149Sdougb else 232193149Sdougb counter = (int)rdtype; 233193149Sdougb 234193149Sdougb if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) & 235193149Sdougb DNS_RDATASTATSTYPE_ATTR_NXRRSET) != 0) 236193149Sdougb counter += rdtypecounter_max; 237193149Sdougb } 238193149Sdougb 239193149Sdougb if (increment) 240193149Sdougb isc_stats_increment(stats->counters, counter); 241193149Sdougb else 242193149Sdougb isc_stats_decrement(stats->counters, counter); 243193149Sdougb} 244193149Sdougb 245193149Sdougbvoid 246193149Sdougbdns_rdatasetstats_increment(dns_stats_t *stats, dns_rdatastatstype_t rrsettype) 247193149Sdougb{ 248193149Sdougb REQUIRE(DNS_STATS_VALID(stats) && 249193149Sdougb stats->type == dns_statstype_rdataset); 250193149Sdougb 251193149Sdougb update_rdatasetstats(stats, rrsettype, ISC_TRUE); 252193149Sdougb} 253193149Sdougb 254193149Sdougbvoid 255193149Sdougbdns_rdatasetstats_decrement(dns_stats_t *stats, dns_rdatastatstype_t rrsettype) 256193149Sdougb{ 257193149Sdougb REQUIRE(DNS_STATS_VALID(stats) && 258193149Sdougb stats->type == dns_statstype_rdataset); 259193149Sdougb 260193149Sdougb update_rdatasetstats(stats, rrsettype, ISC_FALSE); 261193149Sdougb} 262193149Sdougbvoid 263193149Sdougbdns_opcodestats_increment(dns_stats_t *stats, dns_opcode_t code) { 264193149Sdougb REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_opcode); 265193149Sdougb 266193149Sdougb isc_stats_increment(stats->counters, (isc_statscounter_t)code); 267193149Sdougb} 268193149Sdougb 269193149Sdougb/*% 270193149Sdougb * Dump methods 271193149Sdougb */ 272193149Sdougbvoid 273193149Sdougbdns_generalstats_dump(dns_stats_t *stats, dns_generalstats_dumper_t dump_fn, 274193149Sdougb void *arg, unsigned int options) 275193149Sdougb{ 276193149Sdougb REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_general); 277193149Sdougb 278193149Sdougb isc_stats_dump(stats->counters, (isc_stats_dumper_t)dump_fn, 279193149Sdougb arg, options); 280193149Sdougb} 281193149Sdougb 282193149Sdougbstatic void 283193149Sdougbdump_rdentry(int rdcounter, isc_uint64_t value, dns_rdatastatstype_t attributes, 284193149Sdougb dns_rdatatypestats_dumper_t dump_fn, void * arg) 285193149Sdougb{ 286193149Sdougb dns_rdatatype_t rdtype = dns_rdatatype_none; /* sentinel */ 287193149Sdougb dns_rdatastatstype_t type; 288193149Sdougb 289193149Sdougb if (rdcounter == rdtypecounter_others) 290193149Sdougb attributes |= DNS_RDATASTATSTYPE_ATTR_OTHERTYPE; 291193149Sdougb else { 292193149Sdougb if (rdcounter == rdtypecounter_dlv) 293193149Sdougb rdtype = dns_rdatatype_dlv; 294193149Sdougb else 295193149Sdougb rdtype = (dns_rdatatype_t)rdcounter; 296193149Sdougb } 297193149Sdougb type = DNS_RDATASTATSTYPE_VALUE((dns_rdatastatstype_t)rdtype, 298193149Sdougb attributes); 299193149Sdougb dump_fn(type, value, arg); 300193149Sdougb} 301193149Sdougb 302193149Sdougbstatic void 303193149Sdougbrdatatype_dumpcb(isc_statscounter_t counter, isc_uint64_t value, void *arg) { 304193149Sdougb rdatadumparg_t *rdatadumparg = arg; 305193149Sdougb 306193149Sdougb dump_rdentry(counter, value, 0, rdatadumparg->fn, rdatadumparg->arg); 307193149Sdougb} 308193149Sdougb 309193149Sdougbvoid 310193149Sdougbdns_rdatatypestats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn, 311193149Sdougb void *arg0, unsigned int options) 312193149Sdougb{ 313193149Sdougb rdatadumparg_t arg; 314193149Sdougb REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rdtype); 315193149Sdougb 316193149Sdougb arg.fn = dump_fn; 317193149Sdougb arg.arg = arg0; 318193149Sdougb isc_stats_dump(stats->counters, rdatatype_dumpcb, &arg, options); 319193149Sdougb} 320193149Sdougb 321193149Sdougbstatic void 322193149Sdougbrdataset_dumpcb(isc_statscounter_t counter, isc_uint64_t value, void *arg) { 323193149Sdougb rdatadumparg_t *rdatadumparg = arg; 324193149Sdougb 325193149Sdougb if (counter < rdtypecounter_max) { 326193149Sdougb dump_rdentry(counter, value, 0, rdatadumparg->fn, 327193149Sdougb rdatadumparg->arg); 328193149Sdougb } else if (counter < rdtypenxcounter_max) { 329193149Sdougb dump_rdentry(counter - rdtypecounter_max, value, 330193149Sdougb DNS_RDATASTATSTYPE_ATTR_NXRRSET, 331193149Sdougb rdatadumparg->fn, rdatadumparg->arg); 332193149Sdougb } else { 333193149Sdougb dump_rdentry(0, value, DNS_RDATASTATSTYPE_ATTR_NXDOMAIN, 334193149Sdougb rdatadumparg->fn, rdatadumparg->arg); 335193149Sdougb } 336193149Sdougb} 337193149Sdougb 338193149Sdougbvoid 339193149Sdougbdns_rdatasetstats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn, 340193149Sdougb void *arg0, unsigned int options) 341193149Sdougb{ 342193149Sdougb rdatadumparg_t arg; 343193149Sdougb 344193149Sdougb REQUIRE(DNS_STATS_VALID(stats) && 345193149Sdougb stats->type == dns_statstype_rdataset); 346193149Sdougb 347193149Sdougb arg.fn = dump_fn; 348193149Sdougb arg.arg = arg0; 349193149Sdougb isc_stats_dump(stats->counters, rdataset_dumpcb, &arg, options); 350193149Sdougb} 351193149Sdougb 352193149Sdougbstatic void 353193149Sdougbopcode_dumpcb(isc_statscounter_t counter, isc_uint64_t value, void *arg) { 354193149Sdougb opcodedumparg_t *opcodearg = arg; 355193149Sdougb 356193149Sdougb opcodearg->fn((dns_opcode_t)counter, value, opcodearg->arg); 357193149Sdougb} 358193149Sdougb 359193149Sdougbvoid 360193149Sdougbdns_opcodestats_dump(dns_stats_t *stats, dns_opcodestats_dumper_t dump_fn, 361193149Sdougb void *arg0, unsigned int options) 362193149Sdougb{ 363193149Sdougb opcodedumparg_t arg; 364193149Sdougb 365193149Sdougb REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_opcode); 366193149Sdougb 367193149Sdougb arg.fn = dump_fn; 368193149Sdougb arg.arg = arg0; 369193149Sdougb isc_stats_dump(stats->counters, opcode_dumpcb, &arg, options); 370193149Sdougb} 371193149Sdougb 372193149Sdougb/*** 373193149Sdougb *** Obsolete variables and functions follow: 374193149Sdougb ***/ 375135446StrhodesLIBDNS_EXTERNAL_DATA const char *dns_statscounter_names[DNS_STATS_NCOUNTERS] = 376135446Strhodes { 377135446Strhodes "success", 378135446Strhodes "referral", 379135446Strhodes "nxrrset", 380135446Strhodes "nxdomain", 381135446Strhodes "recursion", 382170222Sdougb "failure", 383170222Sdougb "duplicate", 384170222Sdougb "dropped" 385135446Strhodes }; 386135446Strhodes 387135446Strhodesisc_result_t 388135446Strhodesdns_stats_alloccounters(isc_mem_t *mctx, isc_uint64_t **ctrp) { 389135446Strhodes int i; 390135446Strhodes isc_uint64_t *p = 391135446Strhodes isc_mem_get(mctx, DNS_STATS_NCOUNTERS * sizeof(isc_uint64_t)); 392135446Strhodes if (p == NULL) 393135446Strhodes return (ISC_R_NOMEMORY); 394135446Strhodes for (i = 0; i < DNS_STATS_NCOUNTERS; i++) 395135446Strhodes p[i] = 0; 396135446Strhodes *ctrp = p; 397135446Strhodes return (ISC_R_SUCCESS); 398135446Strhodes} 399135446Strhodes 400135446Strhodesvoid 401135446Strhodesdns_stats_freecounters(isc_mem_t *mctx, isc_uint64_t **ctrp) { 402135446Strhodes isc_mem_put(mctx, *ctrp, DNS_STATS_NCOUNTERS * sizeof(isc_uint64_t)); 403135446Strhodes *ctrp = NULL; 404135446Strhodes} 405