/**
* collectd - src/utils_cache.c
* Copyright (C) 2007-2010 Florian octo Forster
+ * Copyright (C) 2016 Sebastian tokkee Harl
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
*
* Authors:
* Florian octo Forster <octo at collectd.org>
+ * Sebastian tokkee Harl <sh at tokkee.org>
**/
#include "collectd.h"
+
#include "common.h"
#include "plugin.h"
#include "utils_avltree.h"
#include "meta_data.h"
#include <assert.h>
-#include <pthread.h>
typedef struct cache_entry_s
{
* (for purging old entries) */
cdtime_t last_update;
/* Interval in which the data is collected
- * (for purding old entries) */
+ * (for purging old entries) */
cdtime_t interval;
int state;
int hits;
meta_data_t *meta;
} cache_entry_t;
+struct uc_iter_s {
+ c_avl_iterator_t *iter;
+
+ char *name;
+ cache_entry_t *entry;
+};
+
static c_avl_tree_t *cache_tree = NULL;
static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
static void uc_check_range (const data_set_t *ds, cache_entry_t *ce)
{
- size_t i;
-
- for (i = 0; i < ds->ds_num; i++)
+ for (size_t i = 0; i < ds->ds_num; i++)
{
if (isnan (ce->values_gauge[i]))
continue;
{
char *key_copy;
cache_entry_t *ce;
- size_t i;
/* `cache_lock' has been locked by `uc_update' */
sstrncpy (ce->name, key, sizeof (ce->name));
- for (i = 0; i < ds->ds_num; i++)
+ for (size_t i = 0; i < ds->ds_num; i++)
{
switch (ds->ds[i].type)
{
int uc_check_timeout (void)
{
- cdtime_t now;
- cache_entry_t *ce;
+ cdtime_t now = cdtime();
- char **keys = NULL;
- cdtime_t *keys_time = NULL;
- cdtime_t *keys_interval = NULL;
- int keys_len = 0;
+ struct {
+ char *key;
+ cdtime_t time;
+ cdtime_t interval;
+ } *expired = NULL;
+ size_t expired_num = 0;
- char *key;
- c_avl_iterator_t *iter;
-
- int status;
- int i;
-
pthread_mutex_lock (&cache_lock);
- now = cdtime ();
-
/* Build a list of entries to be flushed */
- iter = c_avl_get_iterator (cache_tree);
+ c_avl_iterator_t *iter = c_avl_get_iterator (cache_tree);
+ char *key = NULL;
+ cache_entry_t *ce = NULL;
while (c_avl_iterator_next (iter, (void *) &key, (void *) &ce) == 0)
{
- char **tmp;
- cdtime_t *tmp_time;
-
/* If the entry is fresh enough, continue. */
if ((now - ce->last_update) < (ce->interval * timeout_g))
continue;
- /* If entry has not been updated, add to `keys' array */
- tmp = (char **) realloc ((void *) keys,
- (keys_len + 1) * sizeof (char *));
+ void *tmp = realloc (expired, (expired_num + 1) * sizeof (*expired));
if (tmp == NULL)
{
ERROR ("uc_check_timeout: realloc failed.");
continue;
}
- keys = tmp;
+ expired = tmp;
- tmp_time = realloc (keys_time, (keys_len + 1) * sizeof (*keys_time));
- if (tmp_time == NULL)
- {
- ERROR ("uc_check_timeout: realloc failed.");
- continue;
- }
- keys_time = tmp_time;
+ expired[expired_num].key = strdup (key);
+ expired[expired_num].time = ce->last_time;
+ expired[expired_num].interval = ce->interval;
- tmp_time = realloc (keys_interval, (keys_len + 1) * sizeof (*keys_interval));
- if (tmp_time == NULL)
- {
- ERROR ("uc_check_timeout: realloc failed.");
- continue;
- }
- keys_interval = tmp_time;
-
- keys[keys_len] = strdup (key);
- if (keys[keys_len] == NULL)
+ if (expired[expired_num].key == NULL)
{
ERROR ("uc_check_timeout: strdup failed.");
continue;
}
- keys_time[keys_len] = ce->last_time;
- keys_interval[keys_len] = ce->interval;
- keys_len++;
+ expired_num++;
} /* while (c_avl_iterator_next) */
c_avl_iterator_destroy (iter);
pthread_mutex_unlock (&cache_lock);
- if (keys_len == 0)
+ if (expired_num == 0)
{
- /* realloc() may have been called for these. */
- sfree (keys);
- sfree (keys_time);
- sfree (keys_interval);
+ sfree (expired);
return (0);
}
* including plugin specific meta data, rates, history, …. This must be done
* without holding the lock, otherwise we will run into a deadlock if a
* plugin calls the cache interface. */
- for (i = 0; i < keys_len; i++)
+ for (size_t i = 0; i < expired_num; i++)
{
- value_list_t vl = VALUE_LIST_INIT;
-
- vl.values = NULL;
- vl.values_len = 0;
- vl.meta = NULL;
+ value_list_t vl = {
+ .time = expired[i].time,
+ .interval = expired[i].interval,
+ };
- status = parse_identifier_vl (keys[i], &vl);
- if (status != 0)
+ if (parse_identifier_vl (expired[i].key, &vl) != 0)
{
- ERROR ("uc_check_timeout: parse_identifier_vl (\"%s\") failed.", keys[i]);
+ ERROR ("uc_check_timeout: parse_identifier_vl (\"%s\") failed.", expired[i].key);
continue;
}
- vl.time = keys_time[i];
- vl.interval = keys_interval[i];
-
plugin_dispatch_missing (&vl);
- } /* for (i = 0; i < keys_len; i++) */
+ } /* for (i = 0; i < expired_num; i++) */
/* Now actually remove all the values from the cache. We don't re-evaluate
* the timestamp again, so in theory it is possible we remove a value after
* it is updated here. */
pthread_mutex_lock (&cache_lock);
- for (i = 0; i < keys_len; i++)
+ for (size_t i = 0; i < expired_num; i++)
{
- key = NULL;
- ce = NULL;
+ char *key = NULL;
+ cache_entry_t *value = NULL;
- status = c_avl_remove (cache_tree, keys[i],
- (void *) &key, (void *) &ce);
- if (status != 0)
+ if (c_avl_remove (cache_tree, expired[i].key, (void *) &key, (void *) &value) != 0)
{
- ERROR ("uc_check_timeout: c_avl_remove (\"%s\") failed.", keys[i]);
- sfree (keys[i]);
+ ERROR ("uc_check_timeout: c_avl_remove (\"%s\") failed.", expired[i].key);
+ sfree (expired[i].key);
continue;
}
-
- sfree (keys[i]);
sfree (key);
- cache_free (ce);
- } /* for (i = 0; i < keys_len; i++) */
- pthread_mutex_unlock (&cache_lock);
+ cache_free (value);
- sfree (keys);
- sfree (keys_time);
- sfree (keys_interval);
+ sfree (expired[i].key);
+ } /* for (i = 0; i < expired_num; i++) */
+ pthread_mutex_unlock (&cache_lock);
+ sfree (expired);
return (0);
} /* int uc_check_timeout */
char name[6 * DATA_MAX_NAME_LEN];
cache_entry_t *ce = NULL;
int status;
- size_t i;
if (FORMAT_VL (name, sizeof (name), vl) != 0)
{
return (-1);
}
- for (i = 0; i < ds->ds_num; i++)
+ for (size_t i = 0; i < ds->ds_num; i++)
{
switch (ds->ds[i].type)
{
if (ce->history != NULL)
{
assert (ce->history_index < ce->history_length);
- for (i = 0; i < ce->values_num; i++)
+ for (size_t 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];
if (status != 0)
{
- size_t i;
-
- for (i = 0; i < number; i++)
+ for (size_t i = 0; i < number; i++)
{
sfree (names[i]);
}
if (FORMAT_VL (name, sizeof (name), vl) != 0)
{
- ERROR ("uc_get_state: FORMAT_VL failed.");
+ ERROR ("uc_set_state: FORMAT_VL failed.");
return (STATE_ERROR);
}
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);
return (-ENOMEM);
}
- for (i = ce->history_length * ce->values_num;
+ for (size_t i = ce->history_length * ce->values_num;
i < (num_steps * ce->values_num);
i++)
tmp[i] = NAN;
} /* if (ce->history_length < num_steps) */
/* Copy the values to the output buffer. */
- for (i = 0; i < num_steps; i++)
+ for (size_t i = 0; i < num_steps; i++)
{
size_t src_index;
size_t dst_index;
if (FORMAT_VL (name, sizeof (name), vl) != 0)
{
- ERROR ("uc_get_state: FORMAT_VL failed.");
+ ERROR ("uc_get_hits: FORMAT_VL failed.");
return (STATE_ERROR);
}
if (FORMAT_VL (name, sizeof (name), vl) != 0)
{
- ERROR ("uc_get_state: FORMAT_VL failed.");
+ ERROR ("uc_set_hits: FORMAT_VL failed.");
return (STATE_ERROR);
}
if (FORMAT_VL (name, sizeof (name), vl) != 0)
{
- ERROR ("uc_get_state: FORMAT_VL failed.");
+ ERROR ("uc_inc_hits: FORMAT_VL failed.");
return (STATE_ERROR);
}
} /* int uc_inc_hits */
/*
+ * Iterator interface
+ */
+uc_iter_t *uc_get_iterator (void)
+{
+ uc_iter_t *iter;
+
+ iter = (uc_iter_t *) calloc(1, sizeof (*iter));
+ if (iter == NULL)
+ return (NULL);
+
+ pthread_mutex_lock (&cache_lock);
+
+ iter->iter = c_avl_get_iterator (cache_tree);
+ if (iter->iter == NULL)
+ {
+ free (iter);
+ return (NULL);
+ }
+
+ return (iter);
+} /* uc_iter_t *uc_get_iterator */
+
+int uc_iterator_next (uc_iter_t *iter, char **ret_name)
+{
+ int status;
+
+ if (iter == NULL)
+ return (-1);
+
+ while ((status = c_avl_iterator_next (iter->iter,
+ (void *) &iter->name, (void *) &iter->entry)) == 0)
+ {
+ if (iter->entry->state == STATE_MISSING)
+ continue;
+
+ break;
+ }
+ if (status != 0) {
+ iter->name = NULL;
+ iter->entry = NULL;
+ return (-1);
+ }
+
+ if (ret_name != NULL)
+ *ret_name = iter->name;
+
+ return (0);
+} /* int uc_iterator_next */
+
+void uc_iterator_destroy (uc_iter_t *iter)
+{
+ if (iter == NULL)
+ return;
+
+ c_avl_iterator_destroy (iter->iter);
+ pthread_mutex_unlock (&cache_lock);
+
+ free (iter);
+} /* void uc_iterator_destroy */
+
+int uc_iterator_get_time (uc_iter_t *iter, cdtime_t *ret_time)
+{
+ if ((iter == NULL) || (iter->entry == NULL) || (ret_time == NULL))
+ return (-1);
+
+ *ret_time = iter->entry->last_time;
+ return (0);
+} /* int uc_iterator_get_name */
+
+int uc_iterator_get_values (uc_iter_t *iter, value_t **ret_values, size_t *ret_num)
+{
+ if ((iter == NULL) || (iter->entry == NULL)
+ || (ret_values == NULL) || (ret_num == NULL))
+ return (-1);
+
+ *ret_values = calloc (iter->entry->values_num, sizeof(*iter->entry->values_raw));
+ if (*ret_values == NULL)
+ return (-1);
+ for (size_t i = 0; i < iter->entry->values_num; ++i)
+ *ret_values[i] = iter->entry->values_raw[i];
+
+ *ret_num = iter->entry->values_num;
+
+ return (0);
+} /* int uc_iterator_get_values */
+
+int uc_iterator_get_interval (uc_iter_t *iter, cdtime_t *ret_interval)
+{
+ if ((iter == NULL) || (iter->entry == NULL) || (ret_interval == NULL))
+ return (-1);
+
+ *ret_interval = iter->entry->interval;
+ return (0);
+} /* int uc_iterator_get_name */
+
+/*
* Meta data interface
*/
/* XXX: This function will acquire `cache_lock' but will not free it! */