Annotation of embedaddon/ntp/lib/isc/stats.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
3: *
4: * Permission to use, copy, modify, and/or distribute this software for any
5: * purpose with or without fee is hereby granted, provided that the above
6: * copyright notice and this permission notice appear in all copies.
7: *
8: * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9: * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10: * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11: * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12: * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13: * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14: * PERFORMANCE OF THIS SOFTWARE.
15: */
16:
17: /* $Id: stats.c,v 1.3.6.2 2009/01/29 23:47:44 tbox Exp $ */
18:
19: /*! \file */
20:
21: #include <config.h>
22:
23: #include <string.h>
24:
25: #include <isc/atomic.h>
26: #include <isc/buffer.h>
27: #include <isc/magic.h>
28: #include <isc/mem.h>
29: #include <isc/platform.h>
30: #include <isc/print.h>
31: #include <isc/rwlock.h>
32: #include <isc/stats.h>
33: #include <isc/util.h>
34:
35: #define ISC_STATS_MAGIC ISC_MAGIC('S', 't', 'a', 't')
36: #define ISC_STATS_VALID(x) ISC_MAGIC_VALID(x, ISC_STATS_MAGIC)
37:
38: #ifndef ISC_STATS_USEMULTIFIELDS
39: #if defined(ISC_RWLOCK_USEATOMIC) && defined(ISC_PLATFORM_HAVEXADD) && !defined(ISC_PLATFORM_HAVEXADDQ)
40: #define ISC_STATS_USEMULTIFIELDS 1
41: #else
42: #define ISC_STATS_USEMULTIFIELDS 0
43: #endif
44: #endif /* ISC_STATS_USEMULTIFIELDS */
45:
46: #if ISC_STATS_USEMULTIFIELDS
47: typedef struct {
48: isc_uint32_t hi;
49: isc_uint32_t lo;
50: } isc_stat_t;
51: #else
52: typedef isc_uint64_t isc_stat_t;
53: #endif
54:
55: struct isc_stats {
56: /*% Unlocked */
57: unsigned int magic;
58: isc_mem_t *mctx;
59: int ncounters;
60:
61: isc_mutex_t lock;
62: unsigned int references; /* locked by lock */
63:
64: /*%
65: * Locked by counterlock or unlocked if efficient rwlock is not
66: * available.
67: */
68: #ifdef ISC_RWLOCK_USEATOMIC
69: isc_rwlock_t counterlock;
70: #endif
71: isc_stat_t *counters;
72:
73: /*%
74: * We don't want to lock the counters while we are dumping, so we first
75: * copy the current counter values into a local array. This buffer
76: * will be used as the copy destination. It's allocated on creation
77: * of the stats structure so that the dump operation won't fail due
78: * to memory allocation failure.
79: * XXX: this approach is weird for non-threaded build because the
80: * additional memory and the copy overhead could be avoided. We prefer
81: * simplicity here, however, under the assumption that this function
82: * should be only rarely called.
83: */
84: isc_uint64_t *copiedcounters;
85: };
86:
87: static isc_result_t
88: create_stats(isc_mem_t *mctx, int ncounters, isc_stats_t **statsp) {
89: isc_stats_t *stats;
90: isc_result_t result = ISC_R_SUCCESS;
91:
92: REQUIRE(statsp != NULL && *statsp == NULL);
93:
94: stats = isc_mem_get(mctx, sizeof(*stats));
95: if (stats == NULL)
96: return (ISC_R_NOMEMORY);
97:
98: result = isc_mutex_init(&stats->lock);
99: if (result != ISC_R_SUCCESS)
100: goto clean_stats;
101:
102: stats->counters = isc_mem_get(mctx, sizeof(isc_stat_t) * ncounters);
103: if (stats->counters == NULL) {
104: result = ISC_R_NOMEMORY;
105: goto clean_mutex;
106: }
107: stats->copiedcounters = isc_mem_get(mctx,
108: sizeof(isc_uint64_t) * ncounters);
109: if (stats->copiedcounters == NULL) {
110: result = ISC_R_NOMEMORY;
111: goto clean_counters;
112: }
113:
114: #ifdef ISC_RWLOCK_USEATOMIC
115: result = isc_rwlock_init(&stats->counterlock, 0, 0);
116: if (result != ISC_R_SUCCESS)
117: goto clean_copiedcounters;
118: #endif
119:
120: stats->references = 1;
121: memset(stats->counters, 0, sizeof(isc_stat_t) * ncounters);
122: stats->mctx = NULL;
123: isc_mem_attach(mctx, &stats->mctx);
124: stats->ncounters = ncounters;
125: stats->magic = ISC_STATS_MAGIC;
126:
127: *statsp = stats;
128:
129: return (result);
130:
131: clean_counters:
132: isc_mem_put(mctx, stats->counters, sizeof(isc_stat_t) * ncounters);
133:
134: #ifdef ISC_RWLOCK_USEATOMIC
135: clean_copiedcounters:
136: isc_mem_put(mctx, stats->copiedcounters,
137: sizeof(isc_stat_t) * ncounters);
138: #endif
139:
140: clean_mutex:
141: DESTROYLOCK(&stats->lock);
142:
143: clean_stats:
144: isc_mem_put(mctx, stats, sizeof(*stats));
145:
146: return (result);
147: }
148:
149: void
150: isc_stats_attach(isc_stats_t *stats, isc_stats_t **statsp) {
151: REQUIRE(ISC_STATS_VALID(stats));
152: REQUIRE(statsp != NULL && *statsp == NULL);
153:
154: LOCK(&stats->lock);
155: stats->references++;
156: UNLOCK(&stats->lock);
157:
158: *statsp = stats;
159: }
160:
161: void
162: isc_stats_detach(isc_stats_t **statsp) {
163: isc_stats_t *stats;
164:
165: REQUIRE(statsp != NULL && ISC_STATS_VALID(*statsp));
166:
167: stats = *statsp;
168: *statsp = NULL;
169:
170: LOCK(&stats->lock);
171: stats->references--;
172: UNLOCK(&stats->lock);
173:
174: if (stats->references == 0) {
175: isc_mem_put(stats->mctx, stats->copiedcounters,
176: sizeof(isc_stat_t) * stats->ncounters);
177: isc_mem_put(stats->mctx, stats->counters,
178: sizeof(isc_stat_t) * stats->ncounters);
179: DESTROYLOCK(&stats->lock);
180: #ifdef ISC_RWLOCK_USEATOMIC
181: isc_rwlock_destroy(&stats->counterlock);
182: #endif
183: isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats));
184: }
185: }
186:
187: int
188: isc_stats_ncounters(isc_stats_t *stats) {
189: REQUIRE(ISC_STATS_VALID(stats));
190:
191: return (stats->ncounters);
192: }
193:
194: static inline void
195: incrementcounter(isc_stats_t *stats, int counter) {
196: isc_int32_t prev;
197:
198: #ifdef ISC_RWLOCK_USEATOMIC
199: /*
200: * We use a "read" lock to prevent other threads from reading the
201: * counter while we "writing" a counter field. The write access itself
202: * is protected by the atomic operation.
203: */
204: isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_read);
205: #endif
206:
207: #if ISC_STATS_USEMULTIFIELDS
208: prev = isc_atomic_xadd((isc_int32_t *)&stats->counters[counter].lo, 1);
209: /*
210: * If the lower 32-bit field overflows, increment the higher field.
211: * Note that it's *theoretically* possible that the lower field
212: * overlaps again before the higher field is incremented. It doesn't
213: * matter, however, because we don't read the value until
214: * isc_stats_copy() is called where the whole process is protected
215: * by the write (exclusive) lock.
216: */
217: if (prev == (isc_int32_t)0xffffffff)
218: isc_atomic_xadd((isc_int32_t *)&stats->counters[counter].hi, 1);
219: #elif defined(ISC_PLATFORM_HAVEXADDQ)
220: UNUSED(prev);
221: isc_atomic_xaddq((isc_int64_t *)&stats->counters[counter], 1);
222: #else
223: UNUSED(prev);
224: stats->counters[counter]++;
225: #endif
226:
227: #ifdef ISC_RWLOCK_USEATOMIC
228: isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_read);
229: #endif
230: }
231:
232: static inline void
233: decrementcounter(isc_stats_t *stats, int counter) {
234: isc_int32_t prev;
235:
236: #ifdef ISC_RWLOCK_USEATOMIC
237: isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_read);
238: #endif
239:
240: #if ISC_STATS_USEMULTIFIELDS
241: prev = isc_atomic_xadd((isc_int32_t *)&stats->counters[counter].lo, -1);
242: if (prev == 0)
243: isc_atomic_xadd((isc_int32_t *)&stats->counters[counter].hi,
244: -1);
245: #elif defined(ISC_PLATFORM_HAVEXADDQ)
246: UNUSED(prev);
247: isc_atomic_xaddq((isc_int64_t *)&stats->counters[counter], -1);
248: #else
249: UNUSED(prev);
250: stats->counters[counter]--;
251: #endif
252:
253: #ifdef ISC_RWLOCK_USEATOMIC
254: isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_read);
255: #endif
256: }
257:
258: static void
259: copy_counters(isc_stats_t *stats) {
260: int i;
261:
262: #ifdef ISC_RWLOCK_USEATOMIC
263: /*
264: * We use a "write" lock before "reading" the statistics counters as
265: * an exclusive lock.
266: */
267: isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_write);
268: #endif
269:
270: #if ISC_STATS_USEMULTIFIELDS
271: for (i = 0; i < stats->ncounters; i++) {
272: stats->copiedcounters[i] =
273: (isc_uint64_t)(stats->counters[i].hi) << 32 |
274: stats->counters[i].lo;
275: }
276: #else
277: UNUSED(i);
278: memcpy(stats->copiedcounters, stats->counters,
279: stats->ncounters * sizeof(isc_stat_t));
280: #endif
281:
282: #ifdef ISC_RWLOCK_USEATOMIC
283: isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_write);
284: #endif
285: }
286:
287: isc_result_t
288: isc_stats_create(isc_mem_t *mctx, isc_stats_t **statsp, int ncounters) {
289: REQUIRE(statsp != NULL && *statsp == NULL);
290:
291: return (create_stats(mctx, ncounters, statsp));
292: }
293:
294: void
295: isc_stats_increment(isc_stats_t *stats, isc_statscounter_t counter) {
296: REQUIRE(ISC_STATS_VALID(stats));
297: REQUIRE(counter < stats->ncounters);
298:
299: incrementcounter(stats, (int)counter);
300: }
301:
302: void
303: isc_stats_decrement(isc_stats_t *stats, isc_statscounter_t counter) {
304: REQUIRE(ISC_STATS_VALID(stats));
305: REQUIRE(counter < stats->ncounters);
306:
307: decrementcounter(stats, (int)counter);
308: }
309:
310: void
311: isc_stats_dump(isc_stats_t *stats, isc_stats_dumper_t dump_fn,
312: void *arg, unsigned int options)
313: {
314: int i;
315:
316: REQUIRE(ISC_STATS_VALID(stats));
317:
318: copy_counters(stats);
319:
320: for (i = 0; i < stats->ncounters; i++) {
321: if ((options & ISC_STATSDUMP_VERBOSE) == 0 &&
322: stats->copiedcounters[i] == 0)
323: continue;
324: dump_fn((isc_statscounter_t)i, stats->copiedcounters[i], arg);
325: }
326: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>