Home | History | Annotate | Line # | Download | only in isc
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