From: Florian Forster Date: Wed, 22 Jul 2009 07:55:21 +0000 (+0200) Subject: src/utils_cache.[ch]: Add uc_get_history[_by_name]. X-Git-Tag: collectd-4.8.0~72 X-Git-Url: https://git.octo.it/?p=collectd.git;a=commitdiff_plain;h=5472038917e3fbf19e4706e488c1522018c51749 src/utils_cache.[ch]: Add uc_get_history[_by_name]. These two new functions can be used to get historical data of values in the cache. This can be used to calculate floating averages, hysteresis and a shipload of other aggregation and consolidation functions. The current implementation is probably not yet perfect: - If not enough values are available to satisfy the request, the buffer will be enlarged and NaNs will be returned in the newly allocated cells. The caller has no way to recognize this case. - If a value is missing, no NaNs will be added to the cache. It's unclear if this was desirable. - The returned values are reversed, i. e. val[0] will be the newest value, val[n-1] will be the oldest. Here, too, I'm unsure which way is easier to comprehend / use. I went for this implementation because it was easier to write. --- diff --git a/src/utils_cache.c b/src/utils_cache.c index d4ffe10f..39a14687 100644 --- a/src/utils_cache.c +++ b/src/utils_cache.c @@ -45,6 +45,19 @@ typedef struct cache_entry_s * (for purding old entries) */ int interval; int state; + + /* + * +-----+-----+-----+-----+-----+-----+-----+-----+-----+---- + * ! 0 ! 1 ! 2 ! 3 ! 4 ! 5 ! 6 ! 7 ! 8 ! ... + * +-----+-----+-----+-----+-----+-----+-----+-----+-----+---- + * ! ds0 ! ds1 ! ds2 ! ds0 ! ds1 ! ds2 ! ds0 ! ds1 ! ds2 ! ... + * +-----+-----+-----+-----+-----+-----+-----+-----+-----+---- + * ! t = 0 ! t = 1 ! t = 2 ! ... + * +-----------------+-----------------+-----------------+---- + */ + gauge_t *history; + size_t history_index; /* points to the next position to write to. */ + size_t history_length; } cache_entry_t; static c_avl_tree_t *cache_tree = NULL; @@ -80,6 +93,9 @@ static cache_entry_t *cache_alloc (int values_num) return (NULL); } + ce->history = NULL; + ce->history_length = 0; + return (ce); } /* cache_entry_t *cache_alloc */ @@ -90,6 +106,7 @@ static void cache_free (cache_entry_t *ce) sfree (ce->values_gauge); sfree (ce->values_raw); + sfree (ce->history); sfree (ce); } /* void cache_free */ @@ -524,6 +541,20 @@ int uc_update (const data_set_t *ds, const value_list_t *vl) DEBUG ("uc_update: %s: ds[%i] = %lf", name, i, ce->values_gauge[i]); } /* for (i) */ + /* Update the history if it exists. */ + if (ce->history != NULL) + { + assert (ce->history_index < ce->history_length); + for (i = 0; i < ce->values_num; i++) + { + size_t hist_idx = (ce->values_num * ce->history_index) + i; + ce->history[hist_idx] = ce->values_gauge[i]; + } + + assert (ce->history_length > 0); + ce->history_index = (ce->history_index + 1) % ce->history_length; + } + /* Prune invalid gauge data */ uc_check_range (ds, ce); @@ -757,4 +788,88 @@ int uc_set_state (const data_set_t *ds, const value_list_t *vl, int state) return (ret); } /* int uc_set_state */ + +int uc_get_history_by_name (const char *name, + gauge_t *ret_history, size_t num_steps, size_t num_ds) +{ + cache_entry_t *ce = NULL; + size_t i; + int status = 0; + + pthread_mutex_lock (&cache_lock); + + status = c_avl_get (cache_tree, name, (void *) &ce); + if (status != 0) + { + pthread_mutex_unlock (&cache_lock); + return (-ENOENT); + } + + if (((size_t) ce->values_num) != num_ds) + { + pthread_mutex_unlock (&cache_lock); + return (-EINVAL); + } + + /* Check if there are enough values available. If not, increase the buffer + * size. */ + if (ce->history_length < num_steps) + { + gauge_t *tmp; + size_t i; + + tmp = realloc (ce->history, sizeof (*ce->history) + * num_steps * ce->values_num); + if (tmp == NULL) + { + pthread_mutex_unlock (&cache_lock); + return (-ENOMEM); + } + + for (i = ce->history_length * ce->values_num; + i < (num_steps * ce->values_num); + i++) + tmp[i] = NAN; + + ce->history = tmp; + ce->history_length = num_steps; + } /* if (ce->history_length < num_steps) */ + + /* Copy the values to the output buffer. */ + for (i = 0; i < num_steps; i++) + { + size_t src_index; + size_t dst_index; + + if (i < ce->history_index) + src_index = ce->history_index - (i + 1); + else + src_index = ce->history_length + ce->history_index - (i + 1); + src_index = src_index * num_ds; + + dst_index = i * num_ds; + + memcpy (ret_history + dst_index, ce->history + src_index, + sizeof (*ret_history) * num_ds); + } + + pthread_mutex_unlock (&cache_lock); + + return (0); +} /* int uc_get_history_by_name */ + +int uc_get_history (const data_set_t *ds, const value_list_t *vl, + gauge_t *ret_history, size_t num_steps, size_t num_ds) +{ + char name[6 * DATA_MAX_NAME_LEN]; + + if (FORMAT_VL (name, sizeof (name), vl, ds) != 0) + { + ERROR ("utils_cache: uc_get_history: FORMAT_VL failed."); + return (-1); + } + + return (uc_get_history_by_name (name, ret_history, num_steps, num_ds)); +} /* int uc_get_history */ + /* vim: set sw=2 ts=8 sts=2 tw=78 : */ diff --git a/src/utils_cache.h b/src/utils_cache.h index a965febe..d754a32c 100644 --- a/src/utils_cache.h +++ b/src/utils_cache.h @@ -40,5 +40,10 @@ int uc_get_names (char ***ret_names, time_t **ret_times, size_t *ret_number); int uc_get_state (const data_set_t *ds, const value_list_t *vl); int uc_set_state (const data_set_t *ds, const value_list_t *vl, int state); +int uc_get_history (const data_set_t *ds, const value_list_t *vl, + gauge_t *ret_history, size_t num_steps, size_t num_ds); +int uc_get_history_by_name (const char *name, + gauge_t *ret_history, size_t num_steps, size_t num_ds); + /* vim: set shiftwidth=2 softtabstop=2 tabstop=8 : */ #endif /* !UTILS_CACHE_H */