stats.c revision 1.2.2.3 1 /* $NetBSD: stats.c,v 1.2.2.3 2019/01/18 08:49:57 pgoyette Exp $ */
2
3 /*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
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 http://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
15 /*! \file */
16
17 #include <config.h>
18
19 #include <inttypes.h>
20 #include <string.h>
21
22 #include <isc/atomic.h>
23 #include <isc/buffer.h>
24 #include <isc/magic.h>
25 #include <isc/mem.h>
26 #include <isc/platform.h>
27 #include <isc/print.h>
28 #include <isc/rwlock.h>
29 #include <isc/stats.h>
30 #include <isc/util.h>
31
32 #define ISC_STATS_MAGIC ISC_MAGIC('S', 't', 'a', 't')
33 #define ISC_STATS_VALID(x) ISC_MAGIC_VALID(x, ISC_STATS_MAGIC)
34
35 #ifndef _LP64
36 typedef atomic_int_fast32_t isc_stat_t;
37 #else
38 typedef atomic_int_fast64_t isc_stat_t;
39 #endif
40
41 struct isc_stats {
42 /*% Unlocked */
43 unsigned int magic;
44 isc_mem_t *mctx;
45 int ncounters;
46
47 isc_mutex_t lock;
48 unsigned int references; /* locked by lock */
49
50 /*%
51 * Locked by counterlock or unlocked if efficient rwlock is not
52 * available.
53 */
54 isc_stat_t *counters;
55
56 /*%
57 * We don't want to lock the counters while we are dumping, so we first
58 * copy the current counter values into a local array. This buffer
59 * will be used as the copy destination. It's allocated on creation
60 * of the stats structure so that the dump operation won't fail due
61 * to memory allocation failure.
62 * XXX: this approach is weird for non-threaded build because the
63 * additional memory and the copy overhead could be avoided. We prefer
64 * simplicity here, however, under the assumption that this function
65 * should be only rarely called.
66 */
67 uint64_t *copiedcounters;
68 };
69
70 static isc_result_t
71 create_stats(isc_mem_t *mctx, int ncounters, isc_stats_t **statsp) {
72 isc_stats_t *stats;
73 isc_result_t result = ISC_R_SUCCESS;
74
75 REQUIRE(statsp != NULL && *statsp == NULL);
76
77 stats = isc_mem_get(mctx, sizeof(*stats));
78 if (stats == NULL)
79 return (ISC_R_NOMEMORY);
80
81 isc_mutex_init(&stats->lock);
82
83 stats->counters = isc_mem_get(mctx, sizeof(isc_stat_t) * ncounters);
84 if (stats->counters == NULL) {
85 result = ISC_R_NOMEMORY;
86 goto clean_mutex;
87 }
88 stats->copiedcounters = isc_mem_get(mctx,
89 sizeof(uint64_t) * ncounters);
90 if (stats->copiedcounters == NULL) {
91 result = ISC_R_NOMEMORY;
92 goto clean_counters;
93 }
94
95 stats->references = 1;
96 memset(stats->counters, 0, sizeof(isc_stat_t) * ncounters);
97 stats->mctx = NULL;
98 isc_mem_attach(mctx, &stats->mctx);
99 stats->ncounters = ncounters;
100 stats->magic = ISC_STATS_MAGIC;
101
102 *statsp = stats;
103
104 return (result);
105
106 clean_counters:
107 isc_mem_put(mctx, stats->counters, sizeof(isc_stat_t) * ncounters);
108
109 clean_mutex:
110 isc_mutex_destroy(&stats->lock);
111 isc_mem_put(mctx, stats, sizeof(*stats));
112
113 return (result);
114 }
115
116 void
117 isc_stats_attach(isc_stats_t *stats, isc_stats_t **statsp) {
118 REQUIRE(ISC_STATS_VALID(stats));
119 REQUIRE(statsp != NULL && *statsp == NULL);
120
121 LOCK(&stats->lock);
122 stats->references++;
123 UNLOCK(&stats->lock);
124
125 *statsp = stats;
126 }
127
128 void
129 isc_stats_detach(isc_stats_t **statsp) {
130 isc_stats_t *stats;
131
132 REQUIRE(statsp != NULL && ISC_STATS_VALID(*statsp));
133
134 stats = *statsp;
135 *statsp = NULL;
136
137 LOCK(&stats->lock);
138 stats->references--;
139
140 if (stats->references == 0) {
141 isc_mem_put(stats->mctx, stats->copiedcounters,
142 sizeof(isc_stat_t) * stats->ncounters);
143 isc_mem_put(stats->mctx, stats->counters,
144 sizeof(isc_stat_t) * stats->ncounters);
145 UNLOCK(&stats->lock);
146 isc_mutex_destroy(&stats->lock);
147 isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats));
148 return;
149 }
150
151 UNLOCK(&stats->lock);
152 }
153
154 int
155 isc_stats_ncounters(isc_stats_t *stats) {
156 REQUIRE(ISC_STATS_VALID(stats));
157
158 return (stats->ncounters);
159 }
160
161 isc_result_t
162 isc_stats_create(isc_mem_t *mctx, isc_stats_t **statsp, int ncounters) {
163 REQUIRE(statsp != NULL && *statsp == NULL);
164
165 return (create_stats(mctx, ncounters, statsp));
166 }
167
168 void
169 isc_stats_increment(isc_stats_t *stats, isc_statscounter_t counter) {
170 REQUIRE(ISC_STATS_VALID(stats));
171 REQUIRE(counter < stats->ncounters);
172
173 atomic_fetch_add_explicit(&stats->counters[counter], 1,
174 memory_order_relaxed);
175 }
176
177 void
178 isc_stats_decrement(isc_stats_t *stats, isc_statscounter_t counter) {
179 REQUIRE(ISC_STATS_VALID(stats));
180 REQUIRE(counter < stats->ncounters);
181
182 atomic_fetch_sub_explicit(&stats->counters[counter], 1,
183 memory_order_relaxed);
184 }
185
186 void
187 isc_stats_dump(isc_stats_t *stats, isc_stats_dumper_t dump_fn,
188 void *arg, unsigned int options)
189 {
190 int i;
191
192 REQUIRE(ISC_STATS_VALID(stats));
193
194 for (i = 0; i < stats->ncounters; i++) {
195 stats->copiedcounters[i] =
196 atomic_load_explicit(&stats->counters[i],
197 memory_order_relaxed);
198 }
199
200 for (i = 0; i < stats->ncounters; i++) {
201 if ((options & ISC_STATSDUMP_VERBOSE) == 0 &&
202 stats->copiedcounters[i] == 0)
203 continue;
204 dump_fn((isc_statscounter_t)i, stats->copiedcounters[i], arg);
205 }
206 }
207
208 void
209 isc_stats_set(isc_stats_t *stats, uint64_t val,
210 isc_statscounter_t counter)
211 {
212 REQUIRE(ISC_STATS_VALID(stats));
213 REQUIRE(counter < stats->ncounters);
214
215 atomic_store_explicit(&stats->counters[counter], val,
216 memory_order_relaxed);
217 }
218