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