Home | History | Annotate | Line # | Download | only in isc
      1  1.12  christos /*	$NetBSD: stats.c,v 1.12 2025/01/26 16:25:38 christos Exp $	*/
      2   1.1  christos 
      3   1.1  christos /*
      4   1.1  christos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5   1.1  christos  *
      6  1.10  christos  * SPDX-License-Identifier: MPL-2.0
      7  1.10  christos  *
      8   1.1  christos  * This Source Code Form is subject to the terms of the Mozilla Public
      9   1.1  christos  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10   1.8  christos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11   1.1  christos  *
     12   1.1  christos  * See the COPYRIGHT file distributed with this work for additional
     13   1.1  christos  * information regarding copyright ownership.
     14   1.1  christos  */
     15   1.1  christos 
     16   1.1  christos /*! \file */
     17   1.1  christos 
     18   1.3  christos #include <inttypes.h>
     19   1.1  christos #include <string.h>
     20   1.1  christos 
     21   1.1  christos #include <isc/atomic.h>
     22   1.1  christos #include <isc/buffer.h>
     23   1.1  christos #include <isc/magic.h>
     24   1.1  christos #include <isc/mem.h>
     25   1.5  christos #include <isc/refcount.h>
     26   1.1  christos #include <isc/stats.h>
     27   1.1  christos #include <isc/util.h>
     28   1.1  christos 
     29   1.7  christos #define ISC_STATS_MAGIC	   ISC_MAGIC('S', 't', 'a', 't')
     30   1.7  christos #define ISC_STATS_VALID(x) ISC_MAGIC_VALID(x, ISC_STATS_MAGIC)
     31   1.1  christos 
     32  1.11  christos /*
     33  1.11  christos  * Statistics are counted with an atomic int_fast64_t but exported to functions
     34  1.11  christos  * taking uint64_t (isc_stats_dumper_t). A 128-bit native and fast architecture
     35  1.11  christos  * doesn't exist in reality so these two are the same thing in practise.
     36  1.11  christos  * However, a silent truncation happening silently in the future is still not
     37  1.11  christos  * acceptable.
     38  1.11  christos  */
     39  1.11  christos STATIC_ASSERT(sizeof(isc_statscounter_t) <= sizeof(uint64_t),
     40  1.11  christos 	      "Exported statistics must fit into the statistic counter size");
     41   1.1  christos 
     42   1.1  christos struct isc_stats {
     43   1.7  christos 	unsigned int magic;
     44   1.7  christos 	isc_mem_t *mctx;
     45   1.7  christos 	isc_refcount_t references;
     46   1.7  christos 	int ncounters;
     47  1.11  christos 	isc_atomic_statscounter_t *counters;
     48   1.1  christos };
     49   1.1  christos 
     50   1.1  christos void
     51   1.1  christos isc_stats_attach(isc_stats_t *stats, isc_stats_t **statsp) {
     52   1.1  christos 	REQUIRE(ISC_STATS_VALID(stats));
     53   1.1  christos 	REQUIRE(statsp != NULL && *statsp == NULL);
     54   1.1  christos 
     55   1.7  christos 	isc_refcount_increment(&stats->references);
     56   1.1  christos 	*statsp = stats;
     57   1.1  christos }
     58   1.1  christos 
     59   1.1  christos void
     60   1.1  christos isc_stats_detach(isc_stats_t **statsp) {
     61   1.1  christos 	isc_stats_t *stats;
     62   1.1  christos 
     63   1.1  christos 	REQUIRE(statsp != NULL && ISC_STATS_VALID(*statsp));
     64   1.1  christos 
     65   1.1  christos 	stats = *statsp;
     66   1.1  christos 	*statsp = NULL;
     67   1.1  christos 
     68   1.7  christos 	if (isc_refcount_decrement(&stats->references) == 1) {
     69   1.7  christos 		isc_refcount_destroy(&stats->references);
     70  1.12  christos 		isc_mem_cput(stats->mctx, stats->counters, stats->ncounters,
     71  1.12  christos 			     sizeof(isc_atomic_statscounter_t));
     72   1.1  christos 		isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats));
     73   1.1  christos 	}
     74   1.1  christos }
     75   1.1  christos 
     76   1.1  christos int
     77   1.1  christos isc_stats_ncounters(isc_stats_t *stats) {
     78   1.1  christos 	REQUIRE(ISC_STATS_VALID(stats));
     79   1.1  christos 
     80  1.12  christos 	return stats->ncounters;
     81   1.1  christos }
     82   1.1  christos 
     83  1.12  christos void
     84   1.1  christos isc_stats_create(isc_mem_t *mctx, isc_stats_t **statsp, int ncounters) {
     85   1.1  christos 	REQUIRE(statsp != NULL && *statsp == NULL);
     86   1.1  christos 
     87  1.12  christos 	isc_stats_t *stats = isc_mem_get(mctx, sizeof(*stats));
     88  1.12  christos 	size_t counters_alloc_size = sizeof(isc_atomic_statscounter_t) *
     89  1.12  christos 				     ncounters;
     90  1.12  christos 	stats->counters = isc_mem_get(mctx, counters_alloc_size);
     91  1.12  christos 	isc_refcount_init(&stats->references, 1);
     92  1.12  christos 	for (int i = 0; i < ncounters; i++) {
     93  1.12  christos 		atomic_init(&stats->counters[i], 0);
     94  1.12  christos 	}
     95  1.12  christos 	stats->mctx = NULL;
     96  1.12  christos 	isc_mem_attach(mctx, &stats->mctx);
     97  1.12  christos 	stats->ncounters = ncounters;
     98  1.12  christos 	stats->magic = ISC_STATS_MAGIC;
     99  1.12  christos 	*statsp = stats;
    100   1.1  christos }
    101   1.1  christos 
    102  1.12  christos isc_statscounter_t
    103   1.1  christos isc_stats_increment(isc_stats_t *stats, isc_statscounter_t counter) {
    104   1.1  christos 	REQUIRE(ISC_STATS_VALID(stats));
    105   1.1  christos 	REQUIRE(counter < stats->ncounters);
    106   1.1  christos 
    107  1.12  christos 	return atomic_fetch_add_relaxed(&stats->counters[counter], 1);
    108   1.1  christos }
    109   1.1  christos 
    110   1.1  christos void
    111   1.1  christos isc_stats_decrement(isc_stats_t *stats, isc_statscounter_t counter) {
    112   1.1  christos 	REQUIRE(ISC_STATS_VALID(stats));
    113   1.1  christos 	REQUIRE(counter < stats->ncounters);
    114  1.12  christos #if ISC_STATS_CHECKUNDERFLOW
    115  1.12  christos 	REQUIRE(atomic_fetch_sub_release(&stats->counters[counter], 1) > 0);
    116  1.12  christos #else
    117   1.7  christos 	atomic_fetch_sub_release(&stats->counters[counter], 1);
    118  1.12  christos #endif
    119   1.1  christos }
    120   1.1  christos 
    121   1.1  christos void
    122   1.7  christos isc_stats_dump(isc_stats_t *stats, isc_stats_dumper_t dump_fn, void *arg,
    123   1.7  christos 	       unsigned int options) {
    124   1.1  christos 	int i;
    125   1.1  christos 
    126   1.1  christos 	REQUIRE(ISC_STATS_VALID(stats));
    127   1.1  christos 
    128   1.3  christos 	for (i = 0; i < stats->ncounters; i++) {
    129  1.11  christos 		isc_statscounter_t counter =
    130  1.11  christos 			atomic_load_acquire(&stats->counters[i]);
    131   1.5  christos 		if ((options & ISC_STATSDUMP_VERBOSE) == 0 && counter == 0) {
    132   1.5  christos 			continue;
    133   1.5  christos 		}
    134   1.5  christos 		dump_fn((isc_statscounter_t)i, counter, arg);
    135   1.1  christos 	}
    136   1.1  christos }
    137   1.1  christos 
    138   1.1  christos void
    139   1.7  christos isc_stats_set(isc_stats_t *stats, uint64_t val, isc_statscounter_t counter) {
    140   1.1  christos 	REQUIRE(ISC_STATS_VALID(stats));
    141   1.1  christos 	REQUIRE(counter < stats->ncounters);
    142   1.1  christos 
    143   1.7  christos 	atomic_store_release(&stats->counters[counter], val);
    144   1.1  christos }
    145   1.6  christos 
    146   1.7  christos void
    147   1.7  christos isc_stats_update_if_greater(isc_stats_t *stats, isc_statscounter_t counter,
    148   1.7  christos 			    isc_statscounter_t value) {
    149   1.6  christos 	REQUIRE(ISC_STATS_VALID(stats));
    150   1.6  christos 	REQUIRE(counter < stats->ncounters);
    151   1.6  christos 
    152   1.6  christos 	isc_statscounter_t curr_value =
    153   1.7  christos 		atomic_load_acquire(&stats->counters[counter]);
    154   1.6  christos 	do {
    155   1.6  christos 		if (curr_value >= value) {
    156   1.6  christos 			break;
    157   1.6  christos 		}
    158   1.7  christos 	} while (!atomic_compare_exchange_weak_acq_rel(
    159   1.7  christos 		&stats->counters[counter], &curr_value, value));
    160   1.6  christos }
    161   1.6  christos 
    162   1.6  christos isc_statscounter_t
    163   1.7  christos isc_stats_get_counter(isc_stats_t *stats, isc_statscounter_t counter) {
    164   1.6  christos 	REQUIRE(ISC_STATS_VALID(stats));
    165   1.6  christos 	REQUIRE(counter < stats->ncounters);
    166   1.6  christos 
    167  1.12  christos 	return atomic_load_acquire(&stats->counters[counter]);
    168   1.6  christos }
    169  1.10  christos 
    170  1.10  christos void
    171  1.10  christos isc_stats_resize(isc_stats_t **statsp, int ncounters) {
    172  1.10  christos 	isc_stats_t *stats;
    173  1.10  christos 	size_t counters_alloc_size;
    174  1.11  christos 	isc_atomic_statscounter_t *newcounters;
    175  1.10  christos 
    176  1.10  christos 	REQUIRE(statsp != NULL && *statsp != NULL);
    177  1.10  christos 	REQUIRE(ISC_STATS_VALID(*statsp));
    178  1.10  christos 	REQUIRE(ncounters > 0);
    179  1.10  christos 
    180  1.10  christos 	stats = *statsp;
    181  1.10  christos 	if (stats->ncounters >= ncounters) {
    182  1.10  christos 		/* We already have enough counters. */
    183  1.10  christos 		return;
    184  1.10  christos 	}
    185  1.10  christos 
    186  1.10  christos 	/* Grow number of counters. */
    187  1.11  christos 	counters_alloc_size = sizeof(isc_atomic_statscounter_t) * ncounters;
    188  1.10  christos 	newcounters = isc_mem_get(stats->mctx, counters_alloc_size);
    189  1.10  christos 	for (int i = 0; i < ncounters; i++) {
    190  1.10  christos 		atomic_init(&newcounters[i], 0);
    191  1.10  christos 	}
    192  1.10  christos 	for (int i = 0; i < stats->ncounters; i++) {
    193  1.10  christos 		uint32_t counter = atomic_load_acquire(&stats->counters[i]);
    194  1.10  christos 		atomic_store_release(&newcounters[i], counter);
    195  1.10  christos 	}
    196  1.12  christos 	isc_mem_cput(stats->mctx, stats->counters, stats->ncounters,
    197  1.12  christos 		     sizeof(isc_atomic_statscounter_t));
    198  1.10  christos 	stats->counters = newcounters;
    199  1.10  christos 	stats->ncounters = ncounters;
    200  1.10  christos }
    201