/* * 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 #include #include #include #include #include "unicode-helper.h" #include "rsyslog.h" #include "rsconf.h" #include "errmsg.h" #include "perctile_stats.h" #include "hashtable_itr.h" #include "perctile_ringbuf.h" #include "datetime.h" #include #include #include /* Use this macro to enable debug for this module */ #ifdef PERCTILE_STATS_DEBUG #define _DEBUG 1 #else #define _DEBUG 0 #endif #define PERCTILE_STATS_LOG(...) do { if(_DEBUG) fprintf(stderr, __VA_ARGS__); } while(0) /* definitions for objects we access */ DEFobjStaticHelpers DEFobjCurrIf(statsobj) DEFobjCurrIf(datetime) #define PERCTILE_CONF_PARAM_NAME "name" #define PERCTILE_CONF_PARAM_PERCENTILES "percentiles" #define PERCTILE_CONF_PARAM_WINDOW_SIZE "windowsize" #define PERCTILE_CONF_PARAM_DELIM "delimiter" #define PERCTILE_MAX_BUCKET_NS_METRIC_LENGTH 128 #define PERCTILE_METRIC_NAME_SEPARATOR '.' static struct cnfparamdescr modpdescr[] = { { PERCTILE_CONF_PARAM_NAME, eCmdHdlrString, CNFPARAM_REQUIRED }, { PERCTILE_CONF_PARAM_DELIM, eCmdHdlrString, 0}, { PERCTILE_CONF_PARAM_PERCENTILES, eCmdHdlrArray, 0}, { PERCTILE_CONF_PARAM_WINDOW_SIZE, eCmdHdlrPositiveInt, 0}, }; static struct cnfparamblk modpblk = { CNFPARAMBLK_VERSION, sizeof(modpdescr)/sizeof(struct cnfparamdescr), modpdescr }; rsRetVal perctileClassInit(void) { DEFiRet; CHKiRet(objGetObjInterface(&obj)); CHKiRet(objUse(statsobj, CORE_COMPONENT)); CHKiRet(objUse(datetime, CORE_COMPONENT)); finalize_it: RETiRet; } static uint64_t min(uint64_t a, uint64_t b) { return a < b ? a : b; } static uint64_t max(uint64_t a, uint64_t b) { return a > b ? a : b; } static void perctileStatDestruct(perctile_bucket_t *b, perctile_stat_t *pstat) { if (pstat) { if (pstat->rb_observed_stats) { ringbuf_del(pstat->rb_observed_stats); } if (pstat->ctrs) { for (size_t i = 0; i < pstat->perctile_ctrs_count; ++i) { perctile_ctr_t *ctr = &pstat->ctrs[i]; if (ctr->ref_ctr_percentile_stat) { statsobj.DestructCounter(b->statsobj, ctr->ref_ctr_percentile_stat); } } free(pstat->ctrs); } if (pstat->refCtrWindowCount) { statsobj.DestructCounter(b->statsobj, pstat->refCtrWindowCount); } if (pstat->refCtrWindowMin) { statsobj.DestructCounter(b->statsobj, pstat->refCtrWindowMin); } if (pstat->refCtrWindowMax) { statsobj.DestructCounter(b->statsobj, pstat->refCtrWindowMax); } if (pstat->refCtrWindowSum) { statsobj.DestructCounter(b->statsobj, pstat->refCtrWindowSum); } pthread_rwlock_destroy(&pstat->stats_lock); free(pstat); } } static void perctileBucketDestruct(perctile_bucket_t *bkt) { PERCTILE_STATS_LOG("destructing perctile bucket\n"); if (bkt) { pthread_rwlock_wrlock(&bkt->lock); // Delete all items in hashtable size_t count = hashtable_count(bkt->htable); if (count) { int ret = 0; struct hashtable_itr *itr = hashtable_iterator(bkt->htable); dbgprintf("%s() - All container instances, count=%zu...\n", __FUNCTION__, count); do { perctile_stat_t *pstat = hashtable_iterator_value(itr); perctileStatDestruct(bkt, pstat); ret = hashtable_iterator_advance(itr); } while (ret); free (itr); dbgprintf("End of container instances.\n"); } hashtable_destroy(bkt->htable, 0); statsobj.Destruct(&bkt->statsobj); pthread_rwlock_unlock(&bkt->lock); pthread_rwlock_destroy(&bkt->lock); free(bkt->perctile_values); free(bkt->delim); free(bkt->name); free(bkt); } } void perctileBucketsDestruct(void) { perctile_buckets_t *bkts = &runConf->perctile_buckets; if (bkts->initialized) { perctile_bucket_t *head = bkts->listBuckets; if (head) { pthread_rwlock_wrlock(&bkts->lock); perctile_bucket_t *pnode = head, *pnext = NULL; while (pnode) { pnext = pnode->next; perctileBucketDestruct(pnode); pnode = pnext; } pthread_rwlock_unlock(&bkts->lock); } statsobj.Destruct(&bkts->global_stats); // destroy any global stats we keep specifically for this. pthread_rwlock_destroy(&bkts->lock); } } static perctile_bucket_t* findBucket(perctile_bucket_t *head, const uchar *name) { perctile_bucket_t *pbkt_found = NULL; // walk the linked list until the name is found pthread_rwlock_rdlock(&head->lock); for (perctile_bucket_t *pnode = head; pnode != NULL; pnode = pnode->next) { if (ustrcmp(name, pnode->name) == 0) { // found. pbkt_found = pnode; } } pthread_rwlock_unlock(&head->lock); return pbkt_found; } #ifdef PERCTILE_STATS_DEBUG static void print_perctiles(perctile_bucket_t *bkt) { if (hashtable_count(bkt->htable)) { struct hashtable_itr *itr = hashtable_iterator(bkt->htable); do { uchar* key = hashtable_iterator_key(itr); perctile_stat_t *perc_stat = hashtable_iterator_value(itr); PERCTILE_STATS_LOG("print_perctile() - key: %s, perctile stat name: %s ", key, perc_stat->name); } while (hashtable_iterator_advance(itr)); PERCTILE_STATS_LOG("\n"); } } #endif // Assumes a fully created pstat and bkt, also initiliazes some values in pstat. static rsRetVal initAndAddPerctileMetrics(perctile_stat_t *pstat, perctile_bucket_t *bkt, uchar* key) { char stat_name[128]; int bytes = 0; int stat_name_len = sizeof(stat_name); DEFiRet; bytes = snprintf((char*)pstat->name, sizeof(pstat->name), "%s", key); if (bytes < 0 || bytes >= (int) sizeof(pstat->name)) { LogError(0, iRet, "statname '%s' truncated - too long for buffer size: %d\n", stat_name, stat_name_len); ABORT_FINALIZE(RS_RET_CONF_PARAM_INVLD); } int offset = snprintf(stat_name, stat_name_len, "%s%s", (char*)pstat->name, (char*)bkt->delim); if (offset < 0 || offset >= stat_name_len) { LogError(0, iRet, "statname '%s' truncated - too long for buffer size: %d\n", stat_name, stat_name_len); ABORT_FINALIZE(RS_RET_CONF_PARAM_INVLD); } int remaining_size = stat_name_len - offset - 1; // initialize the counters array for (size_t i = 0; i < pstat->perctile_ctrs_count; ++i) { perctile_ctr_t *ctr = &pstat->ctrs[i]; // bucket contains the supported percentile values. ctr->percentile = bkt->perctile_values[i]; bytes = snprintf(stat_name+offset, remaining_size, "p%d", bkt->perctile_values[i]); if (bytes < 0 || bytes >= remaining_size) { LogError(0, iRet, "statname '%s' truncated - too long for buffer size: %d\n", stat_name, stat_name_len); ABORT_FINALIZE(RS_RET_CONF_PARAM_INVLD); } CHKiRet(statsobj.AddManagedCounter(bkt->statsobj, (uchar *)stat_name, ctrType_IntCtr, CTR_FLAG_NONE, &ctr->ctr_perctile_stat, &ctr->ref_ctr_percentile_stat, 1)); } bytes = snprintf(stat_name+offset, remaining_size, "window_min"); if (bytes < 0 || bytes >= remaining_size) { LogError(0, iRet, "statname '%s' truncated - too long for buffer size: %d\n", stat_name, stat_name_len); ABORT_FINALIZE(RS_RET_CONF_PARAM_INVLD); } CHKiRet(statsobj.AddManagedCounter(bkt->statsobj, (uchar *)stat_name, ctrType_IntCtr, CTR_FLAG_NONE, &pstat->ctrWindowMin, &pstat->refCtrWindowMin, 1)); bytes = snprintf(stat_name+offset, remaining_size, "window_max"); if (bytes < 0 || bytes >= remaining_size) { LogError(0, iRet, "statname '%s' truncated - too long for buffer size: %d\n", stat_name, stat_name_len); ABORT_FINALIZE(RS_RET_CONF_PARAM_INVLD); } CHKiRet(statsobj.AddManagedCounter(bkt->statsobj, (uchar *)stat_name, ctrType_IntCtr, CTR_FLAG_NONE, &pstat->ctrWindowMax, &pstat->refCtrWindowMax, 1)); bytes = snprintf(stat_name+offset, remaining_size, "window_sum"); if (bytes < 0 || bytes >= remaining_size) { LogError(0, iRet, "statname '%s' truncated - too long for buffer size: %d\n", stat_name, stat_name_len); ABORT_FINALIZE(RS_RET_CONF_PARAM_INVLD); } CHKiRet(statsobj.AddManagedCounter(bkt->statsobj, (uchar *)stat_name, ctrType_IntCtr, CTR_FLAG_NONE, &pstat->ctrWindowSum, &pstat->refCtrWindowSum, 1)); bytes = snprintf(stat_name+offset, remaining_size, "window_count"); if (bytes < 0 || bytes >= remaining_size) { LogError(0, iRet, "statname '%s' truncated - too long for buffer size: %d\n", stat_name, stat_name_len); ABORT_FINALIZE(RS_RET_CONF_PARAM_INVLD); } CHKiRet(statsobj.AddManagedCounter(bkt->statsobj, (uchar *)stat_name, ctrType_IntCtr, CTR_FLAG_NONE, &pstat->ctrWindowCount, &pstat->refCtrWindowCount, 1)); finalize_it: if (iRet != RS_RET_OK) { LogError(0, iRet, "Could not initialize percentile stats."); } RETiRet; } static rsRetVal perctile_observe(perctile_bucket_t *bkt, uchar* key, int64_t value) { uint8_t lock_initialized = 0; uchar* hash_key = NULL; DEFiRet; time_t now; datetime.GetTime(&now); pthread_rwlock_wrlock(&bkt->lock); lock_initialized = 1; perctile_stat_t *pstat = (perctile_stat_t*) hashtable_search(bkt->htable, key); if (!pstat) { PERCTILE_STATS_LOG("perctile_observe(): key '%s' not found - creating new pstat", key); // create the pstat if not found CHKmalloc(pstat = calloc(1, sizeof(perctile_stat_t))); pstat->ctrs = calloc(bkt->perctile_values_count, sizeof(perctile_ctr_t)); if (!pstat->ctrs) { free(pstat); ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); } pstat->perctile_ctrs_count = bkt->perctile_values_count; pstat->rb_observed_stats = ringbuf_new(bkt->window_size); if (!pstat->rb_observed_stats) { free(pstat->ctrs); free(pstat); ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); } pstat->bReported = 0; pthread_rwlock_init(&pstat->stats_lock, NULL); // init all stat counters here pthread_rwlock_wrlock(&pstat->stats_lock); pstat->ctrWindowCount = pstat->ctrWindowMax = pstat->ctrWindowSum = 0; pstat->ctrWindowMin = value; pthread_rwlock_unlock(&pstat->stats_lock); iRet = initAndAddPerctileMetrics(pstat, bkt, key); if (iRet != RS_RET_OK) { perctileStatDestruct(bkt, pstat); ABORT_FINALIZE(iRet); } CHKmalloc(hash_key = ustrdup(key)); if (!hashtable_insert(bkt->htable, hash_key, pstat)) { perctileStatDestruct(bkt, pstat); free(hash_key); ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); } PERCTILE_STATS_LOG("perctile_observe - new pstat created - name: %s\n", pstat->name); STATSCOUNTER_INC(bkt->ctrNewKeyAdd, bkt->mutCtrNewKeyAdd); } // add this value into the ringbuffer assert(pstat->rb_observed_stats); if (ringbuf_append_with_overwrite(pstat->rb_observed_stats, value) != 0) { // ringbuffer is operating in overwrite mode, so should never see this. ABORT_FINALIZE(RS_RET_ERR); } // update perctile specific stats pthread_rwlock_wrlock(&pstat->stats_lock); { if (pstat->bReported) { // reset window values pstat->ctrWindowCount = pstat->ctrWindowSum = 0; pstat->ctrWindowMin = pstat->ctrWindowMax = value; pstat->bReported = 0; } ++(pstat->ctrWindowCount); pstat->ctrWindowSum += value; pstat->ctrWindowMin = min(pstat->ctrWindowMin, value); pstat->ctrWindowMax = max(pstat->ctrWindowMax, value); } pthread_rwlock_unlock(&pstat->stats_lock); #ifdef PERCTILE_STATS_DEBUG PERCTILE_STATS_LOG("perctile_observe - appended value: %lld to ringbuffer\n", value); PERCTILE_STATS_LOG("ringbuffer contents... \n"); for (size_t i = 0; i < pstat->rb_observed_stats->size; ++i) { PERCTILE_STATS_LOG("%lld ", pstat->rb_observed_stats->cb.buf[i]); } PERCTILE_STATS_LOG("\n"); print_perctiles(bkt); #endif finalize_it: if (lock_initialized) { pthread_rwlock_unlock(&bkt->lock); } if (iRet != RS_RET_OK) { // clean up if there was an error if (iRet == RS_RET_OUT_OF_MEMORY) { STATSCOUNTER_INC(bkt->ctrOpsOverflow, bkt->mutCtrOpsOverflow); } } RETiRet; } static int cmp(const void* p1, const void* p2) { return (*(ITEM*)p1) - (*(ITEM*)p2); } static rsRetVal report_perctile_stats(perctile_bucket_t* pbkt) { ITEM *buf = NULL; struct hashtable_itr *itr = NULL; DEFiRet; pthread_rwlock_rdlock(&pbkt->lock); if (hashtable_count(pbkt->htable)) { itr = hashtable_iterator(pbkt->htable); CHKmalloc(buf = malloc(pbkt->window_size*sizeof(ITEM))); do { memset(buf, 0, pbkt->window_size*sizeof(ITEM)); perctile_stat_t *perc_stat = hashtable_iterator_value(itr); // ringbuffer read size_t count = ringbuf_read_to_end(perc_stat->rb_observed_stats, buf, pbkt->window_size); if (!count) { continue; } PERCTILE_STATS_LOG("read %zu values\n", count); // calculate the p95 based on the #ifdef PERCTILE_STATS_DEBUG PERCTILE_STATS_LOG("ringbuffer contents... \n"); for (size_t i = 0; i < perc_stat->rb_observed_stats->size; ++i) { PERCTILE_STATS_LOG("%lld ", perc_stat->rb_observed_stats->cb.buf[i]); } PERCTILE_STATS_LOG("\n"); PERCTILE_STATS_LOG("buffer contents... \n"); for (size_t i = 0; i < perc_stat->rb_observed_stats->size; ++i) { PERCTILE_STATS_LOG("%lld ", buf[i]); } PERCTILE_STATS_LOG("\n"); #endif qsort(buf, count, sizeof(ITEM), cmp); #ifdef PERCTILE_STATS_DEBUG PERCTILE_STATS_LOG("buffer contents after sort... \n"); for (size_t i = 0; i < perc_stat->rb_observed_stats->size; ++i) { PERCTILE_STATS_LOG("%lld ", buf[i]); } PERCTILE_STATS_LOG("\n"); #endif PERCTILE_STATS_LOG("report_perctile_stats() - perctile stat has %zu counters.", perc_stat->perctile_ctrs_count); for (size_t i = 0; i < perc_stat->perctile_ctrs_count; ++i) { perctile_ctr_t *pctr = &perc_stat->ctrs[i]; // get percentile - this can be cached. int index = max(0, ((pctr->percentile/100.0) * count)-1); // look into if we need to lock this. pctr->ctr_perctile_stat = buf[index]; PERCTILE_STATS_LOG("report_perctile_stats() - index: %d, perctile stat [%s, %d, %llu]", index, perc_stat->name, pctr->percentile, pctr->ctr_perctile_stat); } perc_stat->bReported = 1; } while (hashtable_iterator_advance(itr)); } finalize_it: pthread_rwlock_unlock(&pbkt->lock); free(itr); free(buf); RETiRet; } static void perctile_readCallback(statsobj_t __attribute__((unused)) *ignore, void __attribute__((unused)) *b) { perctile_buckets_t *bkts = &runConf->perctile_buckets; pthread_rwlock_rdlock(&bkts->lock); for (perctile_bucket_t *pbkt = bkts->listBuckets; pbkt != NULL; pbkt = pbkt->next) { report_perctile_stats(pbkt); } pthread_rwlock_unlock(&bkts->lock); } static rsRetVal perctileInitNewBucketStats(perctile_bucket_t *b) { DEFiRet; CHKiRet(statsobj.Construct(&b->statsobj)); CHKiRet(statsobj.SetOrigin(b->statsobj, UCHAR_CONSTANT("percentile.bucket"))); CHKiRet(statsobj.SetName(b->statsobj, b->name)); CHKiRet(statsobj.SetReportingNamespace(b->statsobj, UCHAR_CONSTANT("values"))); statsobj.SetReadNotifier(b->statsobj, perctile_readCallback, b); CHKiRet(statsobj.ConstructFinalize(b->statsobj)); finalize_it: RETiRet; } static rsRetVal perctileAddBucketMetrics(perctile_buckets_t *bkts, perctile_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 + PERCTILE_MAX_BUCKET_NS_METRIC_LENGTH + 1) * sizeof(uchar))); strcpy((char*)metric_name_buff, (char*)name); metric_suffix = metric_name_buff + name_len; *metric_suffix = PERCTILE_METRIC_NAME_SEPARATOR; metric_suffix++; suffix_litteral = UCHAR_CONSTANT("new_metric_add"); ustrncpy(metric_suffix, suffix_litteral, PERCTILE_MAX_BUCKET_NS_METRIC_LENGTH); STATSCOUNTER_INIT(b->ctrNewKeyAdd, b->mutCtrNewKeyAdd); CHKiRet(statsobj.AddManagedCounter(bkts->global_stats, metric_name_buff, ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(b->ctrNewKeyAdd), &b->pNewKeyAddCtr, 1)); suffix_litteral = UCHAR_CONSTANT("ops_overflow"); ustrncpy(metric_suffix, suffix_litteral, PERCTILE_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)); finalize_it: free(metric_name_buff); if (iRet != RS_RET_OK) { if (b->pOpsOverflowCtr != NULL) { statsobj.DestructCounter(bkts->global_stats, b->pOpsOverflowCtr); } if (b->pNewKeyAddCtr != NULL) { statsobj.DestructCounter(bkts->global_stats, b->pNewKeyAddCtr); } } RETiRet; } /* Create new perctile bucket, and add it to our list of perctile buckets. */ static rsRetVal perctile_newBucket(const uchar *name, const uchar *delim, uint8_t *perctiles, uint32_t perctilesCount, uint32_t windowSize) { perctile_buckets_t *bkts; perctile_bucket_t* b = NULL; pthread_rwlockattr_t bucket_lock_attr; DEFiRet; bkts = &loadConf->perctile_buckets; if (bkts->initialized) { CHKmalloc(b = calloc(1, sizeof(perctile_bucket_t))); // initialize pthread_rwlockattr_init(&bucket_lock_attr); pthread_rwlock_init(&b->lock, &bucket_lock_attr); CHKmalloc(b->htable = create_hashtable(7, hash_from_string, key_equals_string, NULL)); CHKmalloc(b->name = ustrdup(name)); if (delim) { CHKmalloc(b->delim = ustrdup(delim)); } else { CHKmalloc(b->delim = ustrdup(".")); } CHKmalloc(b->perctile_values = calloc(perctilesCount, sizeof(uint8_t))); b->perctile_values_count = perctilesCount; memcpy(b->perctile_values, perctiles, perctilesCount * sizeof(uint8_t)); b->window_size = windowSize; b->next = NULL; PERCTILE_STATS_LOG("perctile_newBucket: create new bucket for %s," "with windowsize: %d, values_count: %zu\n", b->name, b->window_size, b->perctile_values_count); // add bucket to list of buckets if (!bkts->listBuckets) { // none yet bkts->listBuckets = b; PERCTILE_STATS_LOG("perctile_newBucket: Adding new bucket to empty list \n"); } else { b->next = bkts->listBuckets; bkts->listBuckets = b; PERCTILE_STATS_LOG("perctile_newBucket: prepended new bucket list \n"); } // create the statsobj for this bucket CHKiRet(perctileInitNewBucketStats(b)); CHKiRet(perctileAddBucketMetrics(bkts, b, name)); } else { LogError(0, RS_RET_INTERNAL_ERROR, "perctile: bucket creation failed, as " "global-initialization of buckets was unsuccessful"); ABORT_FINALIZE(RS_RET_INTERNAL_ERROR); } finalize_it: if (iRet != RS_RET_OK) { if (b != NULL) { perctileBucketDestruct(b); } } RETiRet; } // Public functions rsRetVal perctile_processCnf(struct cnfobj *o) { struct cnfparamvals *pvals; uchar *name = NULL; uchar *delim = NULL; uint8_t *perctiles = NULL; uint32_t perctilesCount = 0; uint64_t windowSize = 0; DEFiRet; pvals = nvlstGetParams(o->nvlst, &modpblk, NULL); if(pvals == NULL) { ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); } for(short i = 0 ; i < modpblk.nParams ; ++i) { if(!pvals[i].bUsed) continue; if(!strcmp(modpblk.descr[i].name, PERCTILE_CONF_PARAM_NAME)) { CHKmalloc(name = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL)); } else if(!strcmp(modpblk.descr[i].name, PERCTILE_CONF_PARAM_DELIM)) { CHKmalloc(delim = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL)); } else if (!strcmp(modpblk.descr[i].name, PERCTILE_CONF_PARAM_PERCENTILES)) { /* Only the first instance of this parameter will be accepted */ if (!perctiles) { perctilesCount = pvals[i].val.d.ar->nmemb; if (perctilesCount) { CHKmalloc(perctiles = calloc(perctilesCount, sizeof(uint8_t))); for (int j = 0; j < pvals[i].val.d.ar->nmemb; ++j) { char *cstr = es_str2cstr(pvals[i].val.d.ar->arr[j], NULL); perctiles[j] = atoi(cstr); free(cstr); } } } } else if (!strcmp(modpblk.descr[i].name, PERCTILE_CONF_PARAM_WINDOW_SIZE)) { windowSize = pvals[i].val.d.n; } else { dbgprintf("perctile: program error, non-handled " "param '%s'\n", modpblk.descr[i].name); } } if (name != NULL && perctiles != NULL) { CHKiRet(perctile_newBucket(name, delim, perctiles, perctilesCount, windowSize)); } finalize_it: free(name); free(delim); free(perctiles); cnfparamvalsDestruct(pvals, &modpblk); RETiRet; } rsRetVal perctile_initCnf(perctile_buckets_t *bkts) { DEFiRet; bkts->initialized = 0; bkts->listBuckets = NULL; CHKiRet(statsobj.Construct(&bkts->global_stats)); CHKiRet(statsobj.SetOrigin(bkts->global_stats, UCHAR_CONSTANT("percentile"))); 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; } perctile_bucket_t* perctile_findBucket(const uchar* name) { perctile_bucket_t *b = NULL; perctile_buckets_t *bkts = &loadConf->perctile_buckets; if (bkts->initialized) { pthread_rwlock_rdlock(&bkts->lock); if (bkts->listBuckets) { b = findBucket(bkts->listBuckets, name); } pthread_rwlock_unlock(&bkts->lock); } else { LogError(0, RS_RET_INTERNAL_ERROR, "perctile: bucket lookup failed, as global-initialization " "of buckets was unsuccessful"); } return b; } rsRetVal perctile_obs(perctile_bucket_t *perctile_bkt, uchar* key, int64_t value) { DEFiRet; if (!perctile_bkt) { LogError(0, RS_RET_INTERNAL_ERROR, "perctile() - perctile bkt not available"); FINALIZE; } PERCTILE_STATS_LOG("perctile_obs() - bucket name: %s, key: %s, val: %" PRId64 "\n", perctile_bkt->name, key, value); CHKiRet(perctile_observe(perctile_bkt, key, value)); finalize_it: if (iRet != RS_RET_OK) { LogError(0, RS_RET_INTERNAL_ERROR, "perctile_obs(): name: %s, key: %s, val: %" PRId64 "\n", perctile_bkt->name, key, value); } RETiRet; }