Annotation of embedaddon/ntp/lib/isc/stats.c, revision 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>