summaryrefslogtreecommitdiffstats
path: root/runtime/dynstats.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 16:28:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 16:28:20 +0000
commitdcc721a95bef6f0d8e6d8775b8efe33e5aecd562 (patch)
tree66a2774cd0ee294d019efd71d2544c70f42b2842 /runtime/dynstats.c
parentInitial commit. (diff)
downloadrsyslog-dcc721a95bef6f0d8e6d8775b8efe33e5aecd562.tar.xz
rsyslog-dcc721a95bef6f0d8e6d8775b8efe33e5aecd562.zip
Adding upstream version 8.2402.0.upstream/8.2402.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'runtime/dynstats.c')
-rw-r--r--runtime/dynstats.c622
1 files changed, 622 insertions, 0 deletions
diff --git a/runtime/dynstats.c b/runtime/dynstats.c
new file mode 100644
index 0000000..1bdba57
--- /dev/null
+++ b/runtime/dynstats.c
@@ -0,0 +1,622 @@
+/*
+ * This file is part of the rsyslog runtime library.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <assert.h>
+
+#include "rsyslog.h"
+#include "srUtils.h"
+#include "errmsg.h"
+#include "rsconf.h"
+#include "unicode-helper.h"
+
+/* definitions for objects we access */
+DEFobjStaticHelpers
+DEFobjCurrIf(statsobj)
+
+#define DYNSTATS_PARAM_NAME "name"
+#define DYNSTATS_PARAM_RESETTABLE "resettable"
+#define DYNSTATS_PARAM_MAX_CARDINALITY "maxCardinality"
+#define DYNSTATS_PARAM_UNUSED_METRIC_LIFE "unusedMetricLife" /* in seconds */
+
+#define DYNSTATS_DEFAULT_RESETTABILITY 1
+#define DYNSTATS_DEFAULT_MAX_CARDINALITY 2000
+#define DYNSTATS_DEFAULT_UNUSED_METRIC_LIFE 3600 /* seconds */
+
+#define DYNSTATS_MAX_BUCKET_NS_METRIC_LENGTH 100
+#define DYNSTATS_METRIC_NAME_SEPARATOR '.'
+#define DYNSTATS_HASHTABLE_SIZE_OVERPROVISIONING 1.25
+
+static struct cnfparamdescr modpdescr[] = {
+ { DYNSTATS_PARAM_NAME, eCmdHdlrString, CNFPARAM_REQUIRED },
+ { DYNSTATS_PARAM_RESETTABLE, eCmdHdlrBinary, 0 },
+ { DYNSTATS_PARAM_MAX_CARDINALITY, eCmdHdlrPositiveInt, 0},
+ { DYNSTATS_PARAM_UNUSED_METRIC_LIFE, eCmdHdlrPositiveInt, 0} /* in minutes */
+};
+
+static struct cnfparamblk modpblk =
+{
+ CNFPARAMBLK_VERSION,
+ sizeof(modpdescr)/sizeof(struct cnfparamdescr),
+ modpdescr
+};
+
+rsRetVal
+dynstatsClassInit(void) {
+ DEFiRet;
+ CHKiRet(objGetObjInterface(&obj));
+ CHKiRet(objUse(statsobj, CORE_COMPONENT));
+finalize_it:
+ RETiRet;
+}
+
+static void
+dynstats_destroyCtr(dynstats_ctr_t *ctr) {
+ statsobj.DestructUnlinkedCounter(ctr->pCtr);
+ free(ctr->metric);
+ free(ctr);
+}
+
+static void /* assumes exclusive access to bucket */
+dynstats_destroyCountersIn(dynstats_bucket_t *b, htable *table, dynstats_ctr_t *ctrs) {
+ dynstats_ctr_t *ctr;
+ int ctrs_purged = 0;
+ hashtable_destroy(table, 0);
+ while (ctrs != NULL) {
+ ctr = ctrs;
+ ctrs = ctrs->next;
+ dynstats_destroyCtr(ctr);
+ ctrs_purged++;
+ }
+ STATSCOUNTER_ADD(b->ctrMetricsPurged, b->mutCtrMetricsPurged, ctrs_purged);
+ ATOMIC_SUB_unsigned(&b->metricCount, ctrs_purged, &b->mutMetricCount);
+}
+
+static void /* assumes exclusive access to bucket */
+dynstats_destroyCounters(dynstats_bucket_t *b) {
+ statsobj.UnlinkAllCounters(b->stats);
+ dynstats_destroyCountersIn(b, b->table, b->ctrs);
+}
+
+static void
+dynstats_destroyBucket(dynstats_buckets_t *bkts, dynstats_bucket_t* b) {
+ pthread_rwlock_wrlock(&b->lock);
+ dynstats_destroyCounters(b);
+ dynstats_destroyCountersIn(b, b->survivor_table, b->survivor_ctrs);
+ statsobj.Destruct(&b->stats);
+ free(b->name);
+ pthread_rwlock_unlock(&b->lock);
+ pthread_rwlock_destroy(&b->lock);
+ pthread_mutex_destroy(&b->mutMetricCount);
+ statsobj.DestructCounter(bkts->global_stats, b->pOpsOverflowCtr);
+ statsobj.DestructCounter(bkts->global_stats, b->pNewMetricAddCtr);
+ statsobj.DestructCounter(bkts->global_stats, b->pNoMetricCtr);
+ statsobj.DestructCounter(bkts->global_stats, b->pMetricsPurgedCtr);
+ statsobj.DestructCounter(bkts->global_stats, b->pOpsIgnoredCtr);
+ statsobj.DestructCounter(bkts->global_stats, b->pPurgeTriggeredCtr);
+ free(b);
+}
+
+static rsRetVal
+dynstats_addBucketMetrics(dynstats_buckets_t *bkts, dynstats_bucket_t *b, const uchar* name) {
+ uchar *metric_name_buff, *metric_suffix;
+ const uchar *suffix_litteral;
+ int name_len;
+ DEFiRet;
+
+ name_len = ustrlen(name);
+ CHKmalloc(metric_name_buff = malloc((name_len + DYNSTATS_MAX_BUCKET_NS_METRIC_LENGTH + 1) * sizeof(uchar)));
+
+ strcpy((char*)metric_name_buff, (char*)name);
+ metric_suffix = metric_name_buff + name_len;
+ *metric_suffix = DYNSTATS_METRIC_NAME_SEPARATOR;
+ metric_suffix++;
+
+ suffix_litteral = UCHAR_CONSTANT("ops_overflow");
+ ustrncpy(metric_suffix, suffix_litteral, DYNSTATS_MAX_BUCKET_NS_METRIC_LENGTH);
+ STATSCOUNTER_INIT(b->ctrOpsOverflow, b->mutCtrOpsOverflow);
+ CHKiRet(statsobj.AddManagedCounter(bkts->global_stats, metric_name_buff, ctrType_IntCtr,
+ CTR_FLAG_RESETTABLE,
+ &(b->ctrOpsOverflow),
+ &b->pOpsOverflowCtr, 1));
+
+ suffix_litteral = UCHAR_CONSTANT("new_metric_add");
+ ustrncpy(metric_suffix, suffix_litteral, DYNSTATS_MAX_BUCKET_NS_METRIC_LENGTH);
+ STATSCOUNTER_INIT(b->ctrNewMetricAdd, b->mutCtrNewMetricAdd);
+ CHKiRet(statsobj.AddManagedCounter(bkts->global_stats, metric_name_buff, ctrType_IntCtr,
+ CTR_FLAG_RESETTABLE,
+ &(b->ctrNewMetricAdd),
+ &b->pNewMetricAddCtr, 1));
+
+ suffix_litteral = UCHAR_CONSTANT("no_metric");
+ ustrncpy(metric_suffix, suffix_litteral, DYNSTATS_MAX_BUCKET_NS_METRIC_LENGTH);
+ STATSCOUNTER_INIT(b->ctrNoMetric, b->mutCtrNoMetric);
+ CHKiRet(statsobj.AddManagedCounter(bkts->global_stats, metric_name_buff, ctrType_IntCtr,
+ CTR_FLAG_RESETTABLE,
+ &(b->ctrNoMetric),
+ &b->pNoMetricCtr, 1));
+
+ suffix_litteral = UCHAR_CONSTANT("metrics_purged");
+ ustrncpy(metric_suffix, suffix_litteral, DYNSTATS_MAX_BUCKET_NS_METRIC_LENGTH);
+ STATSCOUNTER_INIT(b->ctrMetricsPurged, b->mutCtrMetricsPurged);
+ CHKiRet(statsobj.AddManagedCounter(bkts->global_stats, metric_name_buff, ctrType_IntCtr,
+ CTR_FLAG_RESETTABLE,
+ &(b->ctrMetricsPurged),
+ &b->pMetricsPurgedCtr, 1));
+
+ suffix_litteral = UCHAR_CONSTANT("ops_ignored");
+ ustrncpy(metric_suffix, suffix_litteral, DYNSTATS_MAX_BUCKET_NS_METRIC_LENGTH);
+ STATSCOUNTER_INIT(b->ctrOpsIgnored, b->mutCtrOpsIgnored);
+ CHKiRet(statsobj.AddManagedCounter(bkts->global_stats, metric_name_buff, ctrType_IntCtr,
+ CTR_FLAG_RESETTABLE,
+ &(b->ctrOpsIgnored),
+ &b->pOpsIgnoredCtr, 1));
+
+ suffix_litteral = UCHAR_CONSTANT("purge_triggered");
+ ustrncpy(metric_suffix, suffix_litteral, DYNSTATS_MAX_BUCKET_NS_METRIC_LENGTH);
+ STATSCOUNTER_INIT(b->ctrPurgeTriggered, b->mutCtrPurgeTriggered);
+ CHKiRet(statsobj.AddManagedCounter(bkts->global_stats, metric_name_buff, ctrType_IntCtr,
+ CTR_FLAG_RESETTABLE,
+ &(b->ctrPurgeTriggered),
+ &b->pPurgeTriggeredCtr, 1));
+
+finalize_it:
+ free(metric_name_buff);
+ if (iRet != RS_RET_OK) {
+ if (b->pOpsOverflowCtr != NULL) {
+ statsobj.DestructCounter(bkts->global_stats, b->pOpsOverflowCtr);
+ }
+ if (b->pNewMetricAddCtr != NULL) {
+ statsobj.DestructCounter(bkts->global_stats, b->pNewMetricAddCtr);
+ }
+ if (b->pNoMetricCtr != NULL) {
+ statsobj.DestructCounter(bkts->global_stats, b->pNoMetricCtr);
+ }
+ if (b->pMetricsPurgedCtr != NULL) {
+ statsobj.DestructCounter(bkts->global_stats, b->pMetricsPurgedCtr);
+ }
+ if (b->pOpsIgnoredCtr != NULL) {
+ statsobj.DestructCounter(bkts->global_stats, b->pOpsIgnoredCtr);
+ }
+ if (b->pPurgeTriggeredCtr != NULL) {
+ statsobj.DestructCounter(bkts->global_stats, b->pPurgeTriggeredCtr);
+ }
+ }
+ RETiRet;
+}
+
+static void
+no_op_free(void __attribute__((unused)) *ignore) {}
+
+static rsRetVal /* assumes exclusive access to bucket */
+dynstats_rebuildSurvivorTable(dynstats_bucket_t *b) {
+ htable *survivor_table = NULL;
+ htable *new_table = NULL;
+ size_t htab_sz;
+ DEFiRet;
+
+ htab_sz = (size_t) (DYNSTATS_HASHTABLE_SIZE_OVERPROVISIONING * b->maxCardinality + 1);
+ if (b->table == NULL) {
+ CHKmalloc(survivor_table = create_hashtable(htab_sz, hash_from_string, key_equals_string,
+ no_op_free));
+ }
+ CHKmalloc(new_table = create_hashtable(htab_sz, hash_from_string, key_equals_string, no_op_free));
+ statsobj.UnlinkAllCounters(b->stats);
+ if (b->survivor_table != NULL) {
+ dynstats_destroyCountersIn(b, b->survivor_table, b->survivor_ctrs);
+ }
+ b->survivor_table = (b->table == NULL) ? survivor_table : b->table;
+ b->survivor_ctrs = b->ctrs;
+ b->table = new_table;
+ b->ctrs = NULL;
+finalize_it:
+ if (iRet != RS_RET_OK) {
+ LogError(errno, RS_RET_INTERNAL_ERROR, "error trying to evict "
+ "TTL-expired metrics of dyn-stats bucket named: %s", b->name);
+ if (new_table == NULL) {
+ LogError(errno, RS_RET_INTERNAL_ERROR, "error trying to "
+ "initialize hash-table for dyn-stats bucket named: %s", b->name);
+ } else {
+ assert(0); /* "can" not happen -- triggers Coverity CID 184307:
+ hashtable_destroy(new_table, 0);
+ We keep this as guard should code above change in the future */
+ }
+ if (b->table == NULL) {
+ if (survivor_table == NULL) {
+ LogError(errno, RS_RET_INTERNAL_ERROR, "error trying to initialize "
+ "ttl-survivor hash-table for dyn-stats bucket named: %s", b->name);
+ } else {
+ hashtable_destroy(survivor_table, 0);
+ }
+ }
+ }
+ RETiRet;
+}
+
+static rsRetVal
+dynstats_resetBucket(dynstats_bucket_t *b) {
+ DEFiRet;
+ pthread_rwlock_wrlock(&b->lock);
+ CHKiRet(dynstats_rebuildSurvivorTable(b));
+ STATSCOUNTER_INC(b->ctrPurgeTriggered, b->mutCtrPurgeTriggered);
+ timeoutComp(&b->metricCleanupTimeout, b->unusedMetricLife);
+finalize_it:
+ pthread_rwlock_unlock(&b->lock);
+ RETiRet;
+}
+
+static void
+dynstats_resetIfExpired(dynstats_bucket_t *b) {
+ long timeout;
+ pthread_rwlock_rdlock(&b->lock);
+ timeout = timeoutVal(&b->metricCleanupTimeout);
+ pthread_rwlock_unlock(&b->lock);
+ if (timeout == 0) {
+ LogMsg(0, RS_RET_TIMED_OUT, LOG_INFO, "dynstats: bucket '%s' is being reset", b->name);
+ dynstats_resetBucket(b);
+ }
+}
+
+static void
+dynstats_readCallback(statsobj_t __attribute__((unused)) *ignore, void *b) {
+ dynstats_buckets_t *bkts;
+ bkts = &runConf->dynstats_buckets;
+
+ pthread_rwlock_rdlock(&bkts->lock);
+ dynstats_resetIfExpired((dynstats_bucket_t *) b);
+ pthread_rwlock_unlock(&bkts->lock);
+}
+
+static rsRetVal
+dynstats_initNewBucketStats(dynstats_bucket_t *b) {
+ DEFiRet;
+
+ CHKiRet(statsobj.Construct(&b->stats));
+ CHKiRet(statsobj.SetOrigin(b->stats, UCHAR_CONSTANT("dynstats.bucket")));
+ CHKiRet(statsobj.SetName(b->stats, b->name));
+ CHKiRet(statsobj.SetReportingNamespace(b->stats, UCHAR_CONSTANT("values")));
+ statsobj.SetReadNotifier(b->stats, dynstats_readCallback, b);
+ CHKiRet(statsobj.ConstructFinalize(b->stats));
+
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+dynstats_newBucket(const uchar* name, uint8_t resettable, uint32_t maxCardinality, uint32_t unusedMetricLife) {
+ dynstats_bucket_t *b;
+ dynstats_buckets_t *bkts;
+ uint8_t lock_initialized, metric_count_mutex_initialized;
+ pthread_rwlockattr_t bucket_lock_attr;
+ DEFiRet;
+
+ lock_initialized = metric_count_mutex_initialized = 0;
+ b = NULL;
+
+ bkts = &loadConf->dynstats_buckets;
+
+ if (bkts->initialized) {
+ CHKmalloc(b = calloc(1, sizeof(dynstats_bucket_t)));
+ b->resettable = resettable;
+ b->maxCardinality = maxCardinality;
+ b->unusedMetricLife = 1000 * unusedMetricLife;
+ CHKmalloc(b->name = ustrdup(name));
+
+ pthread_rwlockattr_init(&bucket_lock_attr);
+#ifdef HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP
+ pthread_rwlockattr_setkind_np(&bucket_lock_attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
+#endif
+
+ pthread_rwlock_init(&b->lock, &bucket_lock_attr);
+ lock_initialized = 1;
+ pthread_mutex_init(&b->mutMetricCount, NULL);
+ metric_count_mutex_initialized = 1;
+
+ CHKiRet(dynstats_initNewBucketStats(b));
+
+ CHKiRet(dynstats_resetBucket(b));
+
+ CHKiRet(dynstats_addBucketMetrics(bkts, b, name));
+
+ pthread_rwlock_wrlock(&bkts->lock);
+ if (bkts->list == NULL) {
+ bkts->list = b;
+ } else {
+ b->next = bkts->list;
+ bkts->list = b;
+ }
+ pthread_rwlock_unlock(&bkts->lock);
+ } else {
+ LogError(0, RS_RET_INTERNAL_ERROR, "dynstats: bucket creation failed, as "
+ "global-initialization of buckets was unsuccessful");
+ ABORT_FINALIZE(RS_RET_INTERNAL_ERROR);
+ }
+finalize_it:
+ if (iRet != RS_RET_OK) {
+ if (metric_count_mutex_initialized) {
+ pthread_mutex_destroy(&b->mutMetricCount);
+ }
+ if (lock_initialized) {
+ pthread_rwlock_destroy(&b->lock);
+ }
+ if (b != NULL) {
+ dynstats_destroyBucket(bkts, b);
+ }
+ }
+ RETiRet;
+}
+
+rsRetVal
+dynstats_processCnf(struct cnfobj *o) {
+ struct cnfparamvals *pvals;
+ short i;
+ uchar *name = NULL;
+ uint8_t resettable = DYNSTATS_DEFAULT_RESETTABILITY;
+ uint32_t maxCardinality = DYNSTATS_DEFAULT_MAX_CARDINALITY;
+ uint32_t unusedMetricLife = DYNSTATS_DEFAULT_UNUSED_METRIC_LIFE;
+ DEFiRet;
+
+ pvals = nvlstGetParams(o->nvlst, &modpblk, NULL);
+ if(pvals == NULL) {
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ for(i = 0 ; i < modpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(modpblk.descr[i].name, DYNSTATS_PARAM_NAME)) {
+ CHKmalloc(name = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL));
+ } else if (!strcmp(modpblk.descr[i].name, DYNSTATS_PARAM_RESETTABLE)) {
+ resettable = (pvals[i].val.d.n != 0);
+ } else if (!strcmp(modpblk.descr[i].name, DYNSTATS_PARAM_MAX_CARDINALITY)) {
+ maxCardinality = (uint32_t) pvals[i].val.d.n;
+ } else if (!strcmp(modpblk.descr[i].name, DYNSTATS_PARAM_UNUSED_METRIC_LIFE)) {
+ unusedMetricLife = (uint32_t) pvals[i].val.d.n;
+ } else {
+ dbgprintf("dyn_stats: program error, non-handled "
+ "param '%s'\n", modpblk.descr[i].name);
+ }
+ }
+ if (name != NULL) {
+ CHKiRet(dynstats_newBucket(name, resettable, maxCardinality, unusedMetricLife));
+ }
+
+finalize_it:
+ free(name);
+ cnfparamvalsDestruct(pvals, &modpblk);
+ RETiRet;
+}
+
+rsRetVal
+dynstats_initCnf(dynstats_buckets_t *bkts) {
+ DEFiRet;
+
+ bkts->initialized = 0;
+
+ bkts->list = NULL;
+ CHKiRet(statsobj.Construct(&bkts->global_stats));
+ CHKiRet(statsobj.SetOrigin(bkts->global_stats, UCHAR_CONSTANT("dynstats")));
+ CHKiRet(statsobj.SetName(bkts->global_stats, UCHAR_CONSTANT("global")));
+ CHKiRet(statsobj.SetReportingNamespace(bkts->global_stats, UCHAR_CONSTANT("values")));
+ CHKiRet(statsobj.ConstructFinalize(bkts->global_stats));
+ pthread_rwlock_init(&bkts->lock, NULL);
+
+ bkts->initialized = 1;
+
+finalize_it:
+ if (iRet != RS_RET_OK) {
+ statsobj.Destruct(&bkts->global_stats);
+ }
+ RETiRet;
+}
+
+void
+dynstats_destroyAllBuckets(void) {
+ dynstats_buckets_t *bkts;
+ dynstats_bucket_t *b;
+ bkts = &runConf->dynstats_buckets;
+ if (bkts->initialized) {
+ pthread_rwlock_wrlock(&bkts->lock);
+ while(1) {
+ b = bkts->list;
+ if (b == NULL) {
+ break;
+ } else {
+ bkts->list = b->next;
+ dynstats_destroyBucket(bkts, b);
+ }
+ }
+ statsobj.Destruct(&bkts->global_stats);
+ pthread_rwlock_unlock(&bkts->lock);
+ pthread_rwlock_destroy(&bkts->lock);
+ }
+}
+
+dynstats_bucket_t *
+dynstats_findBucket(const uchar* name) {
+ dynstats_buckets_t *bkts;
+ dynstats_bucket_t *b;
+ bkts = &loadConf->dynstats_buckets;
+ if (bkts->initialized) {
+ pthread_rwlock_rdlock(&bkts->lock);
+ b = bkts->list;
+ while(b != NULL) {
+ if (! ustrcmp(name, b->name)) {
+ break;
+ }
+ b = b->next;
+ }
+ pthread_rwlock_unlock(&bkts->lock);
+ } else {
+ b = NULL;
+ LogError(0, RS_RET_INTERNAL_ERROR, "dynstats: bucket lookup failed, as global-initialization "
+ "of buckets was unsuccessful");
+ }
+
+ return b;
+}
+
+static rsRetVal
+dynstats_createCtr(dynstats_bucket_t *b, const uchar* metric, dynstats_ctr_t **ctr) {
+ DEFiRet;
+
+ CHKmalloc(*ctr = calloc(1, sizeof(dynstats_ctr_t)));
+ CHKmalloc((*ctr)->metric = ustrdup(metric));
+ STATSCOUNTER_INIT((*ctr)->ctr, (*ctr)->mutCtr);
+ CHKiRet(statsobj.AddManagedCounter(b->stats, metric, ctrType_IntCtr,
+ b->resettable ? CTR_FLAG_MUST_RESET : CTR_FLAG_NONE,
+ &(*ctr)->ctr, &(*ctr)->pCtr, 0));
+finalize_it:
+ if (iRet != RS_RET_OK) {
+ if ((*ctr) != NULL) {
+ free((*ctr)->metric);
+ free(*ctr);
+ *ctr = NULL;
+ }
+ }
+ RETiRet;
+}
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" /* TODO: how can we fix these warnings? */
+#endif
+static rsRetVal
+dynstats_addNewCtr(dynstats_bucket_t *b, const uchar* metric, uint8_t doInitialIncrement) {
+ dynstats_ctr_t *ctr;
+ dynstats_ctr_t *found_ctr, *survivor_ctr, *effective_ctr;
+ int created;
+ uchar *copy_of_key = NULL;
+ DEFiRet;
+
+ created = 0;
+ ctr = NULL;
+
+ if ((unsigned) ATOMIC_FETCH_32BIT_unsigned(&b->metricCount, &b->mutMetricCount) >= b->maxCardinality) {
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+
+ CHKiRet(dynstats_createCtr(b, metric, &ctr));
+
+ pthread_rwlock_wrlock(&b->lock);
+ found_ctr = (dynstats_ctr_t*) hashtable_search(b->table, ctr->metric);
+ if (found_ctr != NULL) {
+ if (doInitialIncrement) {
+ STATSCOUNTER_INC(found_ctr->ctr, found_ctr->mutCtr);
+ }
+ } else {
+ copy_of_key = ustrdup(ctr->metric);
+ if (copy_of_key != NULL) {
+ survivor_ctr = (dynstats_ctr_t*) hashtable_search(b->survivor_table, ctr->metric);
+ if (survivor_ctr == NULL) {
+ effective_ctr = ctr;
+ } else {
+ effective_ctr = survivor_ctr;
+ if (survivor_ctr->prev != NULL) {
+ survivor_ctr->prev->next = survivor_ctr->next;
+ }
+ if (survivor_ctr->next != NULL) {
+ survivor_ctr->next->prev = survivor_ctr->prev;
+ }
+ if (survivor_ctr == b->survivor_ctrs) {
+ b->survivor_ctrs = survivor_ctr->next;
+ }
+ }
+ if ((created = hashtable_insert(b->table, copy_of_key, effective_ctr))) {
+ statsobj.AddPreCreatedCtr(b->stats, effective_ctr->pCtr);
+ }
+ }
+ if (created) {
+ if (b->ctrs != NULL) {
+ b->ctrs->prev = effective_ctr;
+ }
+ effective_ctr->prev = NULL;
+ effective_ctr->next = b->ctrs;
+ b->ctrs = effective_ctr;
+ if (doInitialIncrement) {
+ STATSCOUNTER_INC(effective_ctr->ctr, effective_ctr->mutCtr);
+ }
+ }
+ }
+ pthread_rwlock_unlock(&b->lock);
+
+ if (found_ctr != NULL) {
+ //ignore
+ } else if (created && (effective_ctr != survivor_ctr)) {
+ ATOMIC_INC(&b->metricCount, &b->mutMetricCount);
+ STATSCOUNTER_INC(b->ctrNewMetricAdd, b->mutCtrNewMetricAdd);
+ } else if (! created) {
+ if (copy_of_key != NULL) {
+ free(copy_of_key);
+ }
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+
+finalize_it:
+ if (((! created) || (effective_ctr != ctr)) && (ctr != NULL)) {
+ dynstats_destroyCtr(ctr);
+ }
+ RETiRet;
+}
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+rsRetVal
+dynstats_inc(dynstats_bucket_t *b, uchar* metric) {
+ dynstats_ctr_t *ctr;
+ DEFiRet;
+
+ if (! GatherStats) {
+ FINALIZE;
+ }
+
+ if (ustrlen(metric) == 0) {
+ STATSCOUNTER_INC(b->ctrNoMetric, b->mutCtrNoMetric);
+ FINALIZE;
+ }
+
+ if (pthread_rwlock_tryrdlock(&b->lock) == 0) {
+ ctr = (dynstats_ctr_t *) hashtable_search(b->table, metric);
+ if (ctr != NULL) {
+ STATSCOUNTER_INC(ctr->ctr, ctr->mutCtr);
+ }
+ pthread_rwlock_unlock(&b->lock);
+ } else {
+ ABORT_FINALIZE(RS_RET_NOENTRY);
+ }
+
+ if (ctr == NULL) {
+ CHKiRet(dynstats_addNewCtr(b, metric, 1));
+ }
+finalize_it:
+ if (iRet != RS_RET_OK) {
+ if (iRet == RS_RET_NOENTRY) {
+ /* NOTE: this is not tested (because it requires very strong orchestration to
+ guarantee contended lock for testing) */
+ STATSCOUNTER_INC(b->ctrOpsIgnored, b->mutCtrOpsIgnored);
+ } else {
+ STATSCOUNTER_INC(b->ctrOpsOverflow, b->mutCtrOpsOverflow);
+ }
+ }
+ RETiRet;
+}
+