stats.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2009, 2012-2015  Internet Systems Consortium, Inc. ("ISC")
00003  *
00004  * Permission to use, copy, modify, and/or distribute this software for any
00005  * purpose with or without fee is hereby granted, provided that the above
00006  * copyright notice and this permission notice appear in all copies.
00007  *
00008  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00009  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00010  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00011  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00012  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00013  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00014  * PERFORMANCE OF THIS SOFTWARE.
00015  */
00016 
00017 /* $Id$ */
00018 
00019 /*! \file */
00020 
00021 #include <config.h>
00022 
00023 #include <string.h>
00024 
00025 #include <isc/atomic.h>
00026 #include <isc/buffer.h>
00027 #include <isc/magic.h>
00028 #include <isc/mem.h>
00029 #include <isc/platform.h>
00030 #include <isc/print.h>
00031 #include <isc/rwlock.h>
00032 #include <isc/stats.h>
00033 #include <isc/util.h>
00034 
00035 #define ISC_STATS_MAGIC                 ISC_MAGIC('S', 't', 'a', 't')
00036 #define ISC_STATS_VALID(x)              ISC_MAGIC_VALID(x, ISC_STATS_MAGIC)
00037 
00038 #ifndef ISC_STATS_USEMULTIFIELDS
00039 #if defined(ISC_RWLOCK_USEATOMIC) && defined(ISC_PLATFORM_HAVEXADD) && !defined(ISC_PLATFORM_HAVEXADDQ)
00040 #define ISC_STATS_USEMULTIFIELDS 1
00041 #else
00042 #define ISC_STATS_USEMULTIFIELDS 0
00043 #endif
00044 #endif  /* ISC_STATS_USEMULTIFIELDS */
00045 
00046 #if ISC_STATS_USEMULTIFIELDS
00047 typedef struct {
00048         isc_uint32_t hi;
00049         isc_uint32_t lo;
00050 } isc_stat_t;
00051 #else
00052 typedef isc_uint64_t isc_stat_t;
00053 #endif
00054 
00055 struct isc_stats {
00056         /*% Unlocked */
00057         unsigned int    magic;
00058         isc_mem_t       *mctx;
00059         int             ncounters;
00060 
00061         isc_mutex_t     lock;
00062         unsigned int    references; /* locked by lock */
00063 
00064         /*%
00065          * Locked by counterlock or unlocked if efficient rwlock is not
00066          * available.
00067          */
00068 #ifdef ISC_RWLOCK_USEATOMIC
00069         isc_rwlock_t    counterlock;
00070 #endif
00071         isc_stat_t      *counters;
00072 
00073         /*%
00074          * We don't want to lock the counters while we are dumping, so we first
00075          * copy the current counter values into a local array.  This buffer
00076          * will be used as the copy destination.  It's allocated on creation
00077          * of the stats structure so that the dump operation won't fail due
00078          * to memory allocation failure.
00079          * XXX: this approach is weird for non-threaded build because the
00080          * additional memory and the copy overhead could be avoided.  We prefer
00081          * simplicity here, however, under the assumption that this function
00082          * should be only rarely called.
00083          */
00084         isc_uint64_t    *copiedcounters;
00085 };
00086 
00087 static isc_result_t
00088 create_stats(isc_mem_t *mctx, int ncounters, isc_stats_t **statsp) {
00089         isc_stats_t *stats;
00090         isc_result_t result = ISC_R_SUCCESS;
00091 
00092         REQUIRE(statsp != NULL && *statsp == NULL);
00093 
00094         stats = isc_mem_get(mctx, sizeof(*stats));
00095         if (stats == NULL)
00096                 return (ISC_R_NOMEMORY);
00097 
00098         result = isc_mutex_init(&stats->lock);
00099         if (result != ISC_R_SUCCESS)
00100                 goto clean_stats;
00101 
00102         stats->counters = isc_mem_get(mctx, sizeof(isc_stat_t) * ncounters);
00103         if (stats->counters == NULL) {
00104                 result = ISC_R_NOMEMORY;
00105                 goto clean_mutex;
00106         }
00107         stats->copiedcounters = isc_mem_get(mctx,
00108                                             sizeof(isc_uint64_t) * ncounters);
00109         if (stats->copiedcounters == NULL) {
00110                 result = ISC_R_NOMEMORY;
00111                 goto clean_counters;
00112         }
00113 
00114 #ifdef ISC_RWLOCK_USEATOMIC
00115         result = isc_rwlock_init(&stats->counterlock, 0, 0);
00116         if (result != ISC_R_SUCCESS)
00117                 goto clean_copiedcounters;
00118 #endif
00119 
00120         stats->references = 1;
00121         memset(stats->counters, 0, sizeof(isc_stat_t) * ncounters);
00122         stats->mctx = NULL;
00123         isc_mem_attach(mctx, &stats->mctx);
00124         stats->ncounters = ncounters;
00125         stats->magic = ISC_STATS_MAGIC;
00126 
00127         *statsp = stats;
00128 
00129         return (result);
00130 
00131 clean_counters:
00132         isc_mem_put(mctx, stats->counters, sizeof(isc_stat_t) * ncounters);
00133 
00134 #ifdef ISC_RWLOCK_USEATOMIC
00135 clean_copiedcounters:
00136         isc_mem_put(mctx, stats->copiedcounters,
00137                     sizeof(isc_stat_t) * ncounters);
00138 #endif
00139 
00140 clean_mutex:
00141         DESTROYLOCK(&stats->lock);
00142 
00143 clean_stats:
00144         isc_mem_put(mctx, stats, sizeof(*stats));
00145 
00146         return (result);
00147 }
00148 
00149 void
00150 isc_stats_attach(isc_stats_t *stats, isc_stats_t **statsp) {
00151         REQUIRE(ISC_STATS_VALID(stats));
00152         REQUIRE(statsp != NULL && *statsp == NULL);
00153 
00154         LOCK(&stats->lock);
00155         stats->references++;
00156         UNLOCK(&stats->lock);
00157 
00158         *statsp = stats;
00159 }
00160 
00161 void
00162 isc_stats_detach(isc_stats_t **statsp) {
00163         isc_stats_t *stats;
00164 
00165         REQUIRE(statsp != NULL && ISC_STATS_VALID(*statsp));
00166 
00167         stats = *statsp;
00168         *statsp = NULL;
00169 
00170         LOCK(&stats->lock);
00171         stats->references--;
00172 
00173         if (stats->references == 0) {
00174                 isc_mem_put(stats->mctx, stats->copiedcounters,
00175                             sizeof(isc_stat_t) * stats->ncounters);
00176                 isc_mem_put(stats->mctx, stats->counters,
00177                             sizeof(isc_stat_t) * stats->ncounters);
00178                 UNLOCK(&stats->lock);
00179                 DESTROYLOCK(&stats->lock);
00180 #ifdef ISC_RWLOCK_USEATOMIC
00181                 isc_rwlock_destroy(&stats->counterlock);
00182 #endif
00183                 isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats));
00184                 return;
00185         }
00186 
00187         UNLOCK(&stats->lock);
00188 }
00189 
00190 int
00191 isc_stats_ncounters(isc_stats_t *stats) {
00192         REQUIRE(ISC_STATS_VALID(stats));
00193 
00194         return (stats->ncounters);
00195 }
00196 
00197 static inline void
00198 incrementcounter(isc_stats_t *stats, int counter) {
00199         isc_int32_t prev;
00200 
00201 #ifdef ISC_RWLOCK_USEATOMIC
00202         /*
00203          * We use a "read" lock to prevent other threads from reading the
00204          * counter while we "writing" a counter field.  The write access itself
00205          * is protected by the atomic operation.
00206          */
00207         isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_read);
00208 #endif
00209 
00210 #if ISC_STATS_USEMULTIFIELDS
00211         prev = isc_atomic_xadd((isc_int32_t *)&stats->counters[counter].lo, 1);
00212         /*
00213          * If the lower 32-bit field overflows, increment the higher field.
00214          * Note that it's *theoretically* possible that the lower field
00215          * overlaps again before the higher field is incremented.  It doesn't
00216          * matter, however, because we don't read the value until
00217          * isc_stats_copy() is called where the whole process is protected
00218          * by the write (exclusive) lock.
00219          */
00220         if (prev == (isc_int32_t)0xffffffff)
00221                 isc_atomic_xadd((isc_int32_t *)&stats->counters[counter].hi, 1);
00222 #elif defined(ISC_PLATFORM_HAVEXADDQ)
00223         UNUSED(prev);
00224         isc_atomic_xaddq((isc_int64_t *)&stats->counters[counter], 1);
00225 #else
00226         UNUSED(prev);
00227         stats->counters[counter]++;
00228 #endif
00229 
00230 #ifdef ISC_RWLOCK_USEATOMIC
00231         isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_read);
00232 #endif
00233 }
00234 
00235 static inline void
00236 decrementcounter(isc_stats_t *stats, int counter) {
00237         isc_int32_t prev;
00238 
00239 #ifdef ISC_RWLOCK_USEATOMIC
00240         isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_read);
00241 #endif
00242 
00243 #if ISC_STATS_USEMULTIFIELDS
00244         prev = isc_atomic_xadd((isc_int32_t *)&stats->counters[counter].lo, -1);
00245         if (prev == 0)
00246                 isc_atomic_xadd((isc_int32_t *)&stats->counters[counter].hi,
00247                                 -1);
00248 #elif defined(ISC_PLATFORM_HAVEXADDQ)
00249         UNUSED(prev);
00250         isc_atomic_xaddq((isc_int64_t *)&stats->counters[counter], -1);
00251 #else
00252         UNUSED(prev);
00253         stats->counters[counter]--;
00254 #endif
00255 
00256 #ifdef ISC_RWLOCK_USEATOMIC
00257         isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_read);
00258 #endif
00259 }
00260 
00261 static void
00262 copy_counters(isc_stats_t *stats) {
00263         int i;
00264 
00265 #ifdef ISC_RWLOCK_USEATOMIC
00266         /*
00267          * We use a "write" lock before "reading" the statistics counters as
00268          * an exclusive lock.
00269          */
00270         isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_write);
00271 #endif
00272 
00273 #if ISC_STATS_USEMULTIFIELDS
00274         for (i = 0; i < stats->ncounters; i++) {
00275                 stats->copiedcounters[i] =
00276                                 (isc_uint64_t)(stats->counters[i].hi) << 32 |
00277                                 stats->counters[i].lo;
00278         }
00279 #else
00280         UNUSED(i);
00281         memmove(stats->copiedcounters, stats->counters,
00282                 stats->ncounters * sizeof(isc_stat_t));
00283 #endif
00284 
00285 #ifdef ISC_RWLOCK_USEATOMIC
00286         isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_write);
00287 #endif
00288 }
00289 
00290 isc_result_t
00291 isc_stats_create(isc_mem_t *mctx, isc_stats_t **statsp, int ncounters) {
00292         REQUIRE(statsp != NULL && *statsp == NULL);
00293 
00294         return (create_stats(mctx, ncounters, statsp));
00295 }
00296 
00297 void
00298 isc_stats_increment(isc_stats_t *stats, isc_statscounter_t counter) {
00299         REQUIRE(ISC_STATS_VALID(stats));
00300         REQUIRE(counter < stats->ncounters);
00301 
00302         incrementcounter(stats, (int)counter);
00303 }
00304 
00305 void
00306 isc_stats_decrement(isc_stats_t *stats, isc_statscounter_t counter) {
00307         REQUIRE(ISC_STATS_VALID(stats));
00308         REQUIRE(counter < stats->ncounters);
00309 
00310         decrementcounter(stats, (int)counter);
00311 }
00312 
00313 void
00314 isc_stats_dump(isc_stats_t *stats, isc_stats_dumper_t dump_fn,
00315                void *arg, unsigned int options)
00316 {
00317         int i;
00318 
00319         REQUIRE(ISC_STATS_VALID(stats));
00320 
00321         copy_counters(stats);
00322 
00323         for (i = 0; i < stats->ncounters; i++) {
00324                 if ((options & ISC_STATSDUMP_VERBOSE) == 0 &&
00325                     stats->copiedcounters[i] == 0)
00326                                 continue;
00327                 dump_fn((isc_statscounter_t)i, stats->copiedcounters[i], arg);
00328         }
00329 }
00330 
00331 void
00332 isc_stats_set(isc_stats_t *stats, isc_uint64_t val,
00333               isc_statscounter_t counter)
00334 {
00335         REQUIRE(ISC_STATS_VALID(stats));
00336         REQUIRE(counter < stats->ncounters);
00337 
00338 #ifdef ISC_RWLOCK_USEATOMIC
00339         /*
00340          * We use a "write" lock before "reading" the statistics counters as
00341          * an exclusive lock.
00342          */
00343         isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_write);
00344 #endif
00345 
00346 #if ISC_STATS_USEMULTIFIELDS
00347         stats->counters[counter].hi = (isc_uint32_t)((val >> 32) & 0xffffffff);
00348         stats->counters[counter].lo = (isc_uint32_t)(val & 0xffffffff);
00349 #else
00350         stats->counters[counter] = val;
00351 #endif
00352 
00353 #ifdef ISC_RWLOCK_USEATOMIC
00354         isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_write);
00355 #endif
00356 }
00357 

Generated on Tue Apr 28 17:41:05 2015 by Doxygen 1.5.4 for BIND9 Internals 9.11.0pre-alpha