stats: refactor

Type: refactor
Change-Id: Ifd533a095d979dc55bfbe5fac7e0b7510a4d900c
Signed-off-by: Damjan Marion <damarion@cisco.com>
diff --git a/src/vlib/stats/stats.c b/src/vlib/stats/stats.c
new file mode 100644
index 0000000..19edaf0
--- /dev/null
+++ b/src/vlib/stats/stats.c
@@ -0,0 +1,514 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2022 Cisco Systems, Inc.
+ */
+
+#include <vlib/vlib.h>
+#include <vlib/stats/stats.h>
+
+vlib_stats_main_t vlib_stats_main;
+
+/*
+ *  Used only by VPP writers
+ */
+
+void
+vlib_stats_segment_lock (void)
+{
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+  clib_spinlock_lock (sm->stat_segment_lockp);
+  sm->shared_header->in_progress = 1;
+}
+
+void
+vlib_stats_segment_unlock (void)
+{
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+  sm->shared_header->epoch++;
+  sm->shared_header->in_progress = 0;
+  clib_spinlock_unlock (sm->stat_segment_lockp);
+}
+
+/*
+ * Change heap to the stats shared memory segment
+ */
+void *
+vlib_stats_set_heap ()
+{
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+
+  ASSERT (sm && sm->shared_header);
+  return clib_mem_set_heap (sm->heap);
+}
+
+u32
+vlib_stats_find_entry_index (char *fmt, ...)
+{
+  u8 *name;
+  va_list va;
+
+  va_start (va, fmt);
+  name = va_format (0, fmt, &va);
+  va_end (va);
+  vec_add1 (name, 0);
+
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+  hash_pair_t *hp = hash_get_pair (sm->directory_vector_by_name, name);
+  vec_free (name);
+  return hp ? hp->value[0] : STAT_SEGMENT_INDEX_INVALID;
+}
+
+static void
+hash_set_str_key_alloc (uword **h, const char *key, uword v)
+{
+  int size = strlen (key) + 1;
+  void *copy = clib_mem_alloc (size);
+  clib_memcpy_fast (copy, key, size);
+  hash_set_mem (*h, copy, v);
+}
+
+static void
+hash_unset_str_key_free (uword **h, const char *key)
+{
+  hash_pair_t *hp = hash_get_pair_mem (*h, key);
+  if (hp)
+    {
+      void *_k = uword_to_pointer (hp->key, void *);
+      hash_unset_mem (*h, _k);
+      clib_mem_free (_k);
+    }
+}
+
+u32
+vlib_stats_create_counter (vlib_stats_entry_t *e)
+{
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+  void *oldheap;
+  u32 index = ~0;
+  int i;
+
+  oldheap = clib_mem_set_heap (sm->heap);
+  vec_foreach_index_backwards (i, sm->directory_vector)
+    if (sm->directory_vector[i].type == STAT_DIR_TYPE_EMPTY)
+      {
+	index = i;
+	break;
+      }
+
+  index = index == ~0 ? vec_len (sm->directory_vector) : index;
+
+  vec_validate (sm->directory_vector, index);
+  sm->directory_vector[index] = *e;
+
+  clib_mem_set_heap (oldheap);
+  hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, index);
+
+  return index;
+}
+
+void
+vlib_stats_remove_entry (u32 entry_index)
+{
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+  vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
+  void *oldheap;
+  counter_t **c;
+  u32 i;
+
+  if (entry_index >= vec_len (sm->directory_vector))
+    return;
+
+  oldheap = clib_mem_set_heap (sm->heap);
+
+  switch (e->type)
+    {
+    case STAT_DIR_TYPE_NAME_VECTOR:
+      for (i = 0; i < vec_len (e->string_vector); i++)
+	vec_free (e->string_vector[i]);
+      vec_free (e->string_vector);
+      break;
+
+    case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
+      c = e->data;
+      e->data = 0;
+      for (i = 0; i < vec_len (c); i++)
+	vec_free (c[i]);
+      vec_free (c);
+      break;
+
+    case STAT_DIR_TYPE_SCALAR_INDEX:
+    case STAT_DIR_TYPE_SYMLINK:
+      break;
+    default:
+      ASSERT (0);
+    }
+
+  clib_mem_set_heap (oldheap);
+  hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
+
+  memset (e, 0, sizeof (*e));
+  e->type = STAT_DIR_TYPE_EMPTY;
+}
+
+static void
+vlib_stats_set_entry_name (vlib_stats_entry_t *e, char *s)
+{
+  u32 i, len = VLIB_STATS_MAX_NAME_SZ - 1;
+
+  for (i = 0; i < len; i++)
+    {
+      e->name[i] = s[i];
+      if (s[i] == 0)
+	return;
+    }
+  ASSERT (i < VLIB_STATS_MAX_NAME_SZ - 1);
+  s[i] = 0;
+}
+
+void
+vlib_stats_update_counter (void *cm_arg, u32 cindex,
+			   stat_directory_type_t type)
+{
+  vlib_simple_counter_main_t *cm = (vlib_simple_counter_main_t *) cm_arg;
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+  vlib_stats_shared_header_t *shared_header = sm->shared_header;
+  char *stat_segment_name;
+  vlib_stats_entry_t e = { 0 };
+
+  /* Not all counters have names / hash-table entries */
+  if (!cm->name && !cm->stat_segment_name)
+    return;
+
+  ASSERT (shared_header);
+
+  vlib_stats_segment_lock ();
+
+  /* Lookup hash-table is on the main heap */
+  stat_segment_name = cm->stat_segment_name ? cm->stat_segment_name : cm->name;
+
+  u32 vector_index = vlib_stats_find_entry_index ("%s", stat_segment_name);
+
+  /* Update the vector */
+  if (vector_index == STAT_SEGMENT_INDEX_INVALID)
+    {
+      vlib_stats_set_entry_name (&e, stat_segment_name);
+      e.type = type;
+      vector_index = vlib_stats_create_counter (&e);
+    }
+
+  vlib_stats_entry_t *ep = &sm->directory_vector[vector_index];
+  ep->data = cm->counters;
+
+  /* Reset the client hash table pointer, since it WILL change! */
+  shared_header->directory_vector = sm->directory_vector;
+
+  vlib_stats_segment_unlock ();
+}
+
+void
+vlib_stats_register_error_index (u64 *em_vec, u64 index, char *fmt, ...)
+{
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+  vlib_stats_shared_header_t *shared_header = sm->shared_header;
+  vlib_stats_entry_t e = {};
+  va_list va;
+  u8 *name;
+
+  va_start (va, fmt);
+  name = va_format (0, fmt, &va);
+  va_end (va);
+
+  ASSERT (shared_header);
+
+  vlib_stats_segment_lock ();
+  u32 vector_index = vlib_stats_find_entry_index ("%v", name);
+
+  if (vector_index == STAT_SEGMENT_INDEX_INVALID)
+    {
+      vec_add1 (name, 0);
+      vlib_stats_set_entry_name (&e, (char *) name);
+      e.type = STAT_DIR_TYPE_ERROR_INDEX;
+      e.index = index;
+      vector_index = vlib_stats_create_counter (&e);
+
+      /* Warn clients to refresh any pointers they might be holding */
+      shared_header->directory_vector = sm->directory_vector;
+    }
+
+  vlib_stats_segment_unlock ();
+  vec_free (name);
+}
+
+void
+vlib_stats_update_error_vector (u64 *error_vector, u32 thread_index, int lock)
+{
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+  vlib_stats_shared_header_t *shared_header = sm->shared_header;
+  void *oldheap = clib_mem_set_heap (sm->heap);
+
+  ASSERT (shared_header);
+
+  if (lock)
+    vlib_stats_segment_lock ();
+
+  /* Reset the client hash table pointer, since it WILL change! */
+  vec_validate (sm->error_vector, thread_index);
+  sm->error_vector[thread_index] = error_vector;
+
+  shared_header->error_vector = sm->error_vector;
+  shared_header->directory_vector = sm->directory_vector;
+
+  if (lock)
+    vlib_stats_segment_unlock ();
+  clib_mem_set_heap (oldheap);
+}
+
+void
+vlib_stats_delete_cm (void *cm_arg)
+{
+  vlib_simple_counter_main_t *cm = (vlib_simple_counter_main_t *) cm_arg;
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+  vlib_stats_entry_t *e;
+
+  /* Not all counters have names / hash-table entries */
+  if (!cm->name && !cm->stat_segment_name)
+    return;
+
+  vlib_stats_segment_lock ();
+
+  /* Lookup hash-table is on the main heap */
+  char *stat_segment_name =
+    cm->stat_segment_name ? cm->stat_segment_name : cm->name;
+  u32 index = vlib_stats_find_entry_index ("%s", stat_segment_name);
+
+  e = &sm->directory_vector[index];
+
+  hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
+
+  memset (e, 0, sizeof (*e));
+  e->type = STAT_DIR_TYPE_EMPTY;
+
+  vlib_stats_segment_unlock ();
+}
+
+static u32
+vlib_stats_new_entry_internal (stat_directory_type_t t, u8 *name)
+{
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+  vlib_stats_shared_header_t *shared_header = sm->shared_header;
+  vlib_stats_entry_t e = { .type = t };
+
+  ASSERT (shared_header);
+
+  u32 vector_index = vlib_stats_find_entry_index ("%v", name);
+  if (vector_index != STAT_SEGMENT_INDEX_INVALID) /* Already registered */
+    {
+      vector_index = ~0;
+      goto done;
+    }
+
+  vec_add1 (name, 0);
+  vlib_stats_set_entry_name (&e, (char *) name);
+
+  vlib_stats_segment_lock ();
+  vector_index = vlib_stats_create_counter (&e);
+
+  shared_header->directory_vector = sm->directory_vector;
+
+  vlib_stats_segment_unlock ();
+
+done:
+  vec_free (name);
+  return vector_index;
+}
+
+u32
+vlib_stats_add_gauge (char *fmt, ...)
+{
+  va_list va;
+  u8 *name;
+
+  va_start (va, fmt);
+  name = va_format (0, fmt, &va);
+  va_end (va);
+  return vlib_stats_new_entry_internal (STAT_DIR_TYPE_SCALAR_INDEX, name);
+}
+
+void
+vlib_stats_set_gauge (u32 index, u64 value)
+{
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+
+  ASSERT (index < vec_len (sm->directory_vector));
+  sm->directory_vector[index].value = value;
+}
+
+u32
+vlib_stats_add_timestamp (char *fmt, ...)
+{
+  va_list va;
+  u8 *name;
+
+  va_start (va, fmt);
+  name = va_format (0, fmt, &va);
+  va_end (va);
+  return vlib_stats_new_entry_internal (STAT_DIR_TYPE_SCALAR_INDEX, name);
+}
+
+void
+vlib_stats_set_timestamp (u32 entry_index, f64 value)
+{
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+
+  ASSERT (entry_index < vec_len (sm->directory_vector));
+  sm->directory_vector[entry_index].value = value;
+}
+
+u32
+vlib_stats_add_string_vector (char *fmt, ...)
+{
+  va_list va;
+  u8 *name;
+
+  va_start (va, fmt);
+  name = va_format (0, fmt, &va);
+  va_end (va);
+  return vlib_stats_new_entry_internal (STAT_DIR_TYPE_NAME_VECTOR, name);
+}
+
+void
+vlib_stats_set_string_vector (u32 entry_index, u32 vector_index, char *fmt,
+			      ...)
+{
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+  vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
+  va_list va;
+  void *oldheap;
+
+  oldheap = clib_mem_set_heap (sm->heap);
+  vlib_stats_segment_lock ();
+
+  vec_validate (e->string_vector, vector_index);
+  vec_reset_length (e->string_vector[vector_index]);
+
+  va_start (va, fmt);
+  e->string_vector[vector_index] =
+    va_format (e->string_vector[vector_index], fmt, &va);
+  va_end (va);
+
+  vlib_stats_segment_unlock ();
+  clib_mem_set_heap (oldheap);
+}
+
+u32
+vlib_stats_add_counter_vector (char *fmt, ...)
+{
+  va_list va;
+  u8 *name;
+
+  va_start (va, fmt);
+  name = va_format (0, fmt, &va);
+  va_end (va);
+  return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE,
+					name);
+}
+
+void
+vlib_stats_validate_counter_vector (u32 entry_index, u32 vector_index)
+{
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+  vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
+  void *oldheap;
+  counter_t **c = e->data;
+
+  if (vec_len (c) > 0 && vec_len (c[0]) > vector_index)
+    return;
+
+  oldheap = clib_mem_set_heap (sm->heap);
+  vlib_stats_segment_lock ();
+
+  vec_validate_aligned (c, 0, CLIB_CACHE_LINE_BYTES);
+  vec_validate_aligned (c[0], vector_index, CLIB_CACHE_LINE_BYTES);
+  e->data = c;
+
+  vlib_stats_segment_unlock ();
+  clib_mem_set_heap (oldheap);
+}
+
+u32
+vlib_stats_add_symlink (u32 entry_index, u32 vector_index, char *fmt, ...)
+{
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+  vlib_stats_shared_header_t *shared_header = sm->shared_header;
+  vlib_stats_entry_t e;
+  va_list va;
+  u8 *name;
+
+  ASSERT (shared_header);
+  ASSERT (entry_index < vec_len (sm->directory_vector));
+
+  va_start (va, fmt);
+  name = va_format (0, fmt, &va);
+  va_end (va);
+
+  if (vlib_stats_find_entry_index ("%v", name) == STAT_SEGMENT_INDEX_INVALID)
+    {
+      vec_add1 (name, 0);
+      vlib_stats_set_entry_name (&e, (char *) name);
+      e.type = STAT_DIR_TYPE_SYMLINK;
+      e.index1 = entry_index;
+      e.index2 = vector_index;
+      vector_index = vlib_stats_create_counter (&e);
+
+      /* Warn clients to refresh any pointers they might be holding */
+      shared_header->directory_vector = sm->directory_vector;
+    }
+  else
+    vector_index = ~0;
+
+  vec_free (name);
+  return vector_index;
+}
+
+void
+vlib_stats_rename_symlink (u64 entry_index, char *fmt, ...)
+{
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+  vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
+  va_list va;
+  u8 *new_name;
+
+  hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
+
+  va_start (va, fmt);
+  new_name = va_format (0, fmt, &va);
+  va_end (va);
+
+  vec_add1 (new_name, 0);
+  vlib_stats_set_entry_name (e, (char *) new_name);
+  hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, entry_index);
+  vec_free (new_name);
+}
+
+f64
+vlib_stats_get_segment_update_rate (void)
+{
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+  return sm->update_interval;
+}
+
+void
+vlib_stats_register_collector_fn (vlib_stats_collector_reg_t *reg)
+{
+  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+  vlib_stats_collector_t *c;
+
+  ASSERT (reg->entry_index != ~0);
+
+  pool_get_zero (sm->collectors, c);
+  c->fn = reg->collect_fn;
+  c->entry_index = reg->entry_index;
+  c->vector_index = reg->vector_index;
+  c->private_data = reg->private_data;
+
+  return;
+}