Code

Merge branch 'collectd-4.7'
[collectd.git] / src / utils_cache.c
index 170e2c5e41edd3c13c6deff5a7e8e42c69e6010a..956f8269c786a04614b089a088d5640be0b3ce83 100644 (file)
@@ -25,6 +25,7 @@
 #include "utils_avltree.h"
 #include "utils_cache.h"
 #include "utils_threshold.h"
+#include "meta_data.h"
 
 #include <assert.h>
 #include <pthread.h>
@@ -34,7 +35,7 @@ typedef struct cache_entry_s
        char name[6 * DATA_MAX_NAME_LEN];
        int        values_num;
        gauge_t   *values_gauge;
-       counter_t *values_counter;
+       value_t   *values_raw;
        /* Time contained in the package
         * (for calculating rates) */
        time_t last_time;
@@ -45,6 +46,21 @@ 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;
+
+       meta_data_t *meta;
 } cache_entry_t;
 
 static c_avl_tree_t   *cache_tree = NULL;
@@ -69,17 +85,21 @@ static cache_entry_t *cache_alloc (int values_num)
   memset (ce, '\0', sizeof (cache_entry_t));
   ce->values_num = values_num;
 
-  ce->values_gauge = (gauge_t *) calloc (values_num, sizeof (gauge_t));
-  ce->values_counter = (counter_t *) calloc (values_num, sizeof (counter_t));
-  if ((ce->values_gauge == NULL) || (ce->values_counter == NULL))
+  ce->values_gauge = calloc (values_num, sizeof (*ce->values_gauge));
+  ce->values_raw   = calloc (values_num, sizeof (*ce->values_raw));
+  if ((ce->values_gauge == NULL) || (ce->values_raw == NULL))
   {
     sfree (ce->values_gauge);
-    sfree (ce->values_counter);
+    sfree (ce->values_raw);
     sfree (ce);
     ERROR ("utils_cache: cache_alloc: calloc failed.");
     return (NULL);
   }
 
+  ce->history = NULL;
+  ce->history_length = 0;
+  ce->meta = NULL;
+
   return (ce);
 } /* cache_entry_t *cache_alloc */
 
@@ -89,7 +109,13 @@ static void cache_free (cache_entry_t *ce)
     return;
 
   sfree (ce->values_gauge);
-  sfree (ce->values_counter);
+  sfree (ce->values_raw);
+  sfree (ce->history);
+  if (ce->meta != NULL)
+  {
+    meta_data_destroy (ce->meta);
+    ce->meta = NULL;
+  }
   sfree (ce);
 } /* void cache_free */
 
@@ -167,6 +193,21 @@ static int uc_send_notification (const char *name)
   return (0);
 } /* int uc_send_notification */
 
+static void uc_check_range (const data_set_t *ds, cache_entry_t *ce)
+{
+  int i;
+
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    if (isnan (ce->values_gauge[i]))
+      continue;
+    else if (ce->values_gauge[i] < ds->ds[i].min)
+      ce->values_gauge[i] = NAN;
+    else if (ce->values_gauge[i] > ds->ds[i].max)
+      ce->values_gauge[i] = NAN;
+  }
+} /* void uc_check_range */
+
 static int uc_insert (const data_set_t *ds, const value_list_t *vl,
     const char *key)
 {
@@ -195,17 +236,42 @@ static int uc_insert (const data_set_t *ds, const value_list_t *vl,
 
   for (i = 0; i < ds->ds_num; i++)
   {
-    if (ds->ds[i].type == DS_TYPE_COUNTER)
-    {
-      ce->values_gauge[i] = NAN;
-      ce->values_counter[i] = vl->values[i].counter;
-    }
-    else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
+    switch (ds->ds[i].type)
     {
-      ce->values_gauge[i] = vl->values[i].gauge;
-    }
+      case DS_TYPE_COUNTER:
+       ce->values_gauge[i] = NAN;
+       ce->values_raw[i].counter = vl->values[i].counter;
+       break;
+
+      case DS_TYPE_GAUGE:
+       ce->values_gauge[i] = vl->values[i].gauge;
+       ce->values_raw[i].gauge = vl->values[i].gauge;
+       break;
+
+      case DS_TYPE_DERIVE:
+       ce->values_gauge[i] = NAN;
+       ce->values_raw[i].derive = vl->values[i].derive;
+       break;
+
+      case DS_TYPE_ABSOLUTE:
+       ce->values_gauge[i] = NAN;
+       if (vl->interval > 0)
+         ce->values_gauge[i] = ((double) vl->values[i].absolute)
+           / ((double) vl->interval);
+       ce->values_raw[i].absolute = vl->values[i].absolute;
+       break;
+       
+      default:
+       /* This shouldn't happen. */
+       ERROR ("uc_insert: Don't know how to handle data source type %i.",
+           ds->ds[i].type);
+       return (-1);
+    } /* switch (ds->ds[i].type) */
   } /* for (i) */
 
+  /* Prune invalid gauge data */
+  uc_check_range (ds, ce);
+
   ce->last_time = vl->time;
   ce->last_update = time (NULL);
   ce->interval = vl->interval;
@@ -386,7 +452,7 @@ int uc_update (const data_set_t *ds, const value_list_t *vl)
   int status;
   int i;
 
-  if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
+  if (FORMAT_VL (name, sizeof (name), vl) != 0)
   {
     ERROR ("uc_update: FORMAT_VL failed.");
     return (-1);
@@ -425,36 +491,84 @@ int uc_update (const data_set_t *ds, const value_list_t *vl)
 
   for (i = 0; i < ds->ds_num; i++)
   {
-    if (ds->ds[i].type == DS_TYPE_COUNTER)
+    switch (ds->ds[i].type)
     {
-      counter_t diff;
+      case DS_TYPE_COUNTER:
+       {
+         counter_t diff;
+
+         /* check if the counter has wrapped around */
+         if (vl->values[i].counter < ce->values_raw[i].counter)
+         {
+           if (ce->values_raw[i].counter <= 4294967295U)
+             diff = (4294967295U - ce->values_raw[i].counter)
+               + vl->values[i].counter;
+           else
+             diff = (18446744073709551615ULL - ce->values_raw[i].counter)
+               + vl->values[i].counter;
+         }
+         else /* counter has NOT wrapped around */
+         {
+           diff = vl->values[i].counter - ce->values_raw[i].counter;
+         }
+
+         ce->values_gauge[i] = ((double) diff)
+           / ((double) (vl->time - ce->last_time));
+         ce->values_raw[i].counter = vl->values[i].counter;
+       }
+       break;
 
-      /* check if the counter has wrapped around */
-      if (vl->values[i].counter < ce->values_counter[i])
-      {
-       if (ce->values_counter[i] <= 4294967295U)
-         diff = (4294967295U - ce->values_counter[i])
-           + vl->values[i].counter;
-       else
-         diff = (18446744073709551615ULL - ce->values_counter[i])
-           + vl->values[i].counter;
-      }
-      else /* counter has NOT wrapped around */
-      {
-       diff = vl->values[i].counter - ce->values_counter[i];
-      }
+      case DS_TYPE_GAUGE:
+       ce->values_raw[i].gauge = vl->values[i].gauge;
+       ce->values_gauge[i] = vl->values[i].gauge;
+       break;
+
+      case DS_TYPE_DERIVE:
+       {
+         derive_t diff;
+
+         diff = vl->values[i].derive - ce->values_raw[i].derive;
+
+         ce->values_gauge[i] = ((double) diff)
+           / ((double) (vl->time - ce->last_time));
+         ce->values_raw[i].derive = vl->values[i].derive;
+       }
+       break;
+
+      case DS_TYPE_ABSOLUTE:
+       ce->values_gauge[i] = ((double) vl->values[i].absolute)
+         / ((double) (vl->time - ce->last_time));
+       ce->values_raw[i].absolute = vl->values[i].absolute;
+       break;
+
+      default:
+       /* This shouldn't happen. */
+       pthread_mutex_unlock (&cache_lock);
+       ERROR ("uc_update: Don't know how to handle data source type %i.",
+           ds->ds[i].type);
+       return (-1);
+    } /* switch (ds->ds[i].type) */
 
-      ce->values_gauge[i] = ((double) diff)
-       / ((double) (vl->time - ce->last_time));
-      ce->values_counter[i] = vl->values[i].counter;
-    }
-    else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
-    {
-      ce->values_gauge[i] = vl->values[i].gauge;
-    }
     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);
+
   ce->last_time = vl->time;
   ce->last_update = time (NULL);
   ce->interval = vl->interval;
@@ -543,7 +657,7 @@ gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl)
   size_t ret_num = 0;
   int status;
 
-  if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
+  if (FORMAT_VL (name, sizeof (name), vl) != 0)
   {
     ERROR ("utils_cache: uc_get_rate: FORMAT_VL failed.");
     return (NULL);
@@ -653,7 +767,7 @@ int uc_get_state (const data_set_t *ds, const value_list_t *vl)
   cache_entry_t *ce = NULL;
   int ret = STATE_ERROR;
 
-  if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
+  if (FORMAT_VL (name, sizeof (name), vl) != 0)
   {
     ERROR ("uc_get_state: FORMAT_VL failed.");
     return (STATE_ERROR);
@@ -678,7 +792,7 @@ int uc_set_state (const data_set_t *ds, const value_list_t *vl, int state)
   cache_entry_t *ce = NULL;
   int ret = -1;
 
-  if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
+  if (FORMAT_VL (name, sizeof (name), vl) != 0)
   {
     ERROR ("uc_get_state: FORMAT_VL failed.");
     return (STATE_ERROR);
@@ -697,4 +811,193 @@ 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) != 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 */
+
+/*
+ * Meta data interface
+ */
+/* XXX: This function will acquire `cache_lock' but will not free it! */
+static meta_data_t *uc_get_meta (const value_list_t *vl) /* {{{ */
+{
+  char name[6 * DATA_MAX_NAME_LEN];
+  cache_entry_t *ce = NULL;
+  int status;
+
+  status = FORMAT_VL (name, sizeof (name), vl);
+  if (status != 0)
+  {
+    ERROR ("utils_cache: uc_get_meta: FORMAT_VL failed.");
+    return (NULL);
+  }
+
+  pthread_mutex_lock (&cache_lock);
+
+  status = c_avl_get (cache_tree, name, (void *) &ce);
+  if (status != 0)
+  {
+    pthread_mutex_unlock (&cache_lock);
+    return (NULL);
+  }
+  assert (ce != NULL);
+
+  if (ce->meta == NULL)
+    ce->meta = meta_data_create ();
+
+  return (ce->meta);
+} /* }}} meta_data_t *uc_get_meta */
+
+/* Sorry about this preprocessor magic, but it really makes this file much
+ * shorter.. */
+#define UC_WRAP(wrap_function) { \
+  meta_data_t *meta; \
+  int status; \
+  meta = uc_get_meta (vl); \
+  if (meta == NULL) return (-1); \
+  status = wrap_function (meta, key); \
+  pthread_mutex_unlock (&cache_lock); \
+  return (status); \
+}
+int uc_meta_data_exists (const value_list_t *vl, const char *key)
+  UC_WRAP (meta_data_exists)
+
+int uc_meta_data_delete (const value_list_t *vl, const char *key)
+  UC_WRAP (meta_data_delete)
+#undef UC_WRAP
+
+/* We need a new version of this macro because the following functions take
+ * two argumetns. */
+#define UC_WRAP(wrap_function) { \
+  meta_data_t *meta; \
+  int status; \
+  meta = uc_get_meta (vl); \
+  if (meta == NULL) return (-1); \
+  status = wrap_function (meta, key, value); \
+  pthread_mutex_unlock (&cache_lock); \
+  return (status); \
+}
+int uc_meta_data_add_string (const value_list_t *vl,
+    const char *key,
+    const char *value)
+  UC_WRAP(meta_data_add_string)
+int uc_meta_data_add_signed_int (const value_list_t *vl,
+    const char *key,
+    int64_t value)
+  UC_WRAP(meta_data_add_signed_int)
+int uc_meta_data_add_unsigned_int (const value_list_t *vl,
+    const char *key,
+    uint64_t value)
+  UC_WRAP(meta_data_add_unsigned_int)
+int uc_meta_data_add_double (const value_list_t *vl,
+    const char *key,
+    double value)
+  UC_WRAP(meta_data_add_double)
+int uc_meta_data_add_boolean (const value_list_t *vl,
+    const char *key,
+    _Bool value)
+  UC_WRAP(meta_data_add_boolean)
+
+int uc_meta_data_get_string (const value_list_t *vl,
+    const char *key,
+    char **value)
+  UC_WRAP(meta_data_get_string)
+int uc_meta_data_get_signed_int (const value_list_t *vl,
+    const char *key,
+    int64_t *value)
+  UC_WRAP(meta_data_get_signed_int)
+int uc_meta_data_get_unsigned_int (const value_list_t *vl,
+    const char *key,
+    uint64_t *value)
+  UC_WRAP(meta_data_get_unsigned_int)
+int uc_meta_data_get_double (const value_list_t *vl,
+    const char *key,
+    double *value)
+  UC_WRAP(meta_data_get_double)
+int uc_meta_data_get_boolean (const value_list_t *vl,
+    const char *key,
+    _Bool *value)
+  UC_WRAP(meta_data_get_boolean)
+#undef UC_WRAP
+
 /* vim: set sw=2 ts=8 sts=2 tw=78 : */