aboutsummaryrefslogtreecommitdiffstats
path: root/src/vlib/stats/stats.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vlib/stats/stats.c')
-rw-r--r--src/vlib/stats/stats.c574
1 files changed, 574 insertions, 0 deletions
diff --git a/src/vlib/stats/stats.c b/src/vlib/stats/stats.c
new file mode 100644
index 00000000000..b7743ec70f2
--- /dev/null
+++ b/src/vlib/stats/stats.c
@@ -0,0 +1,574 @@
+/* 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;
+
+void
+vlib_stats_segment_lock (void)
+{
+ vlib_main_t *vm = vlib_get_main ();
+ vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+
+ /* already locked by us */
+ if (sm->shared_header->in_progress &&
+ vm->thread_index == sm->locking_thread_index)
+ goto done;
+
+ ASSERT (sm->locking_thread_index == ~0);
+ ASSERT (sm->shared_header->in_progress == 0);
+ ASSERT (sm->n_locks == 0);
+
+ clib_spinlock_lock (sm->stat_segment_lockp);
+
+ sm->shared_header->in_progress = 1;
+ sm->locking_thread_index = vm->thread_index;
+done:
+ sm->n_locks++;
+}
+
+void
+vlib_stats_segment_unlock (void)
+{
+ vlib_main_t *vm = vlib_get_main ();
+ vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+
+ ASSERT (sm->shared_header->in_progress == 1);
+ ASSERT (sm->locking_thread_index == vm->thread_index);
+ ASSERT (sm->n_locks > 0);
+
+ sm->n_locks--;
+
+ if (sm->n_locks > 0)
+ return;
+
+ sm->shared_header->epoch++;
+ __atomic_store_n (&sm->shared_header->in_progress, 0, __ATOMIC_RELEASE);
+ sm->locking_thread_index = ~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 ();
+ u32 index;
+
+ if (sm->dir_vector_first_free_elt != CLIB_U32_MAX)
+ {
+ index = sm->dir_vector_first_free_elt;
+ sm->dir_vector_first_free_elt = sm->directory_vector[index].index;
+ }
+ else
+ {
+ index = vec_len (sm->directory_vector);
+ vec_validate (sm->directory_vector, index);
+ }
+
+ sm->directory_vector[index] = *e;
+
+ 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);
+ counter_t **c;
+ vlib_counter_t **vc;
+ void *oldheap;
+ u32 i;
+
+ if (entry_index >= vec_len (sm->directory_vector))
+ return;
+
+ vlib_stats_segment_lock ();
+
+ 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;
+ oldheap = clib_mem_set_heap (sm->heap);
+ for (i = 0; i < vec_len (c); i++)
+ vec_free (c[i]);
+ vec_free (c);
+ clib_mem_set_heap (oldheap);
+ break;
+
+ case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
+ vc = e->data;
+ e->data = 0;
+ oldheap = clib_mem_set_heap (sm->heap);
+ for (i = 0; i < vec_len (vc); i++)
+ vec_free (vc[i]);
+ vec_free (vc);
+ clib_mem_set_heap (oldheap);
+ break;
+
+ case STAT_DIR_TYPE_SCALAR_INDEX:
+ case STAT_DIR_TYPE_SYMLINK:
+ break;
+ default:
+ ASSERT (0);
+ }
+
+ vlib_stats_segment_unlock ();
+
+ hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
+
+ memset (e, 0, sizeof (*e));
+ e->type = STAT_DIR_TYPE_EMPTY;
+
+ e->value = sm->dir_vector_first_free_elt;
+ sm->dir_vector_first_free_elt = entry_index;
+}
+
+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;
+}
+
+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;
+}
+
+vlib_stats_string_vector_t
+vlib_stats_add_string_vector (char *fmt, ...)
+{
+ vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+ va_list va;
+ vlib_stats_header_t *sh;
+ vlib_stats_string_vector_t sv;
+ u32 index;
+ u8 *name;
+
+ va_start (va, fmt);
+ name = va_format (0, fmt, &va);
+ va_end (va);
+
+ index = vlib_stats_new_entry_internal (STAT_DIR_TYPE_NAME_VECTOR, name);
+ if (index == CLIB_U32_MAX)
+ return 0;
+
+ sv = vec_new_generic (vlib_stats_string_vector_t, 0,
+ sizeof (vlib_stats_header_t), 0, sm->heap);
+ sh = vec_header (sv);
+ sh->entry_index = index;
+ sm->directory_vector[index].string_vector = sv;
+ return sv;
+}
+
+void
+vlib_stats_set_string_vector (vlib_stats_string_vector_t *svp,
+ u32 vector_index, char *fmt, ...)
+{
+ vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+ vlib_stats_header_t *sh = vec_header (*svp);
+ vlib_stats_entry_t *e = vlib_stats_get_entry (sm, sh->entry_index);
+ va_list va;
+ u8 *s;
+
+ if (fmt[0] == 0)
+ {
+ if (vec_len (e->string_vector) <= vector_index)
+ return;
+
+ if (e->string_vector[vector_index] == 0)
+ return;
+
+ vlib_stats_segment_lock ();
+ vec_free (e->string_vector[vector_index]);
+ vlib_stats_segment_unlock ();
+ return;
+ }
+
+ vlib_stats_segment_lock ();
+
+ ASSERT (e->string_vector);
+
+ vec_validate (e->string_vector, vector_index);
+ svp[0] = e->string_vector;
+
+ s = e->string_vector[vector_index];
+
+ if (s == 0)
+ s = vec_new_heap (u8 *, 0, sm->heap);
+
+ vec_reset_length (s);
+
+ va_start (va, fmt);
+ s = va_format (s, fmt, &va);
+ va_end (va);
+ vec_add1 (s, 0);
+
+ e->string_vector[vector_index] = s;
+
+ vlib_stats_segment_unlock ();
+}
+
+void
+vlib_stats_free_string_vector (vlib_stats_string_vector_t *sv)
+{
+ vlib_stats_header_t *sh = vec_header (*sv);
+ vlib_stats_remove_entry (sh->entry_index);
+}
+
+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);
+}
+
+u32
+vlib_stats_add_counter_pair_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_COMBINED,
+ name);
+}
+
+static int
+vlib_stats_validate_will_expand_internal (u32 entry_index, va_list *va)
+{
+ vlib_stats_segment_t *sm = vlib_stats_get_segment ();
+ vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
+ void *oldheap;
+ int rv = 1;
+
+ oldheap = clib_mem_set_heap (sm->heap);
+ if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
+ {
+ u32 idx0 = va_arg (*va, u32);
+ u32 idx1 = va_arg (*va, u32);
+ u64 **data = e->data;
+
+ if (idx0 >= vec_len (data))
+ goto done;
+
+ for (u32 i = 0; i <= idx0; i++)
+ if (idx1 >= vec_max_len (data[i]))
+ goto done;
+ }
+ else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
+ {
+ u32 idx0 = va_arg (*va, u32);
+ u32 idx1 = va_arg (*va, u32);
+ vlib_counter_t **data = e->data;
+
+ va_end (*va);
+
+ if (idx0 >= vec_len (data))
+ goto done;
+
+ for (u32 i = 0; i <= idx0; i++)
+ if (idx1 >= vec_max_len (data[i]))
+ goto done;
+ }
+ else
+ ASSERT (0);
+
+ rv = 0;
+done:
+ clib_mem_set_heap (oldheap);
+ return rv;
+}
+
+int
+vlib_stats_validate_will_expand (u32 entry_index, ...)
+{
+ va_list va;
+ int rv;
+
+ va_start (va, entry_index);
+ rv = vlib_stats_validate_will_expand_internal (entry_index, &va);
+ va_end (va);
+ return rv;
+}
+
+void
+vlib_stats_validate (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;
+ va_list va;
+ int will_expand;
+
+ va_start (va, entry_index);
+ will_expand = vlib_stats_validate_will_expand_internal (entry_index, &va);
+ va_end (va);
+
+ if (will_expand)
+ vlib_stats_segment_lock ();
+
+ oldheap = clib_mem_set_heap (sm->heap);
+
+ va_start (va, entry_index);
+
+ if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
+ {
+ u32 idx0 = va_arg (va, u32);
+ u32 idx1 = va_arg (va, u32);
+ u64 **data = e->data;
+
+ vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
+
+ for (u32 i = 0; i <= idx0; i++)
+ vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
+ e->data = data;
+ }
+ else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
+ {
+ u32 idx0 = va_arg (va, u32);
+ u32 idx1 = va_arg (va, u32);
+ vlib_counter_t **data = e->data;
+
+ vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
+
+ for (u32 i = 0; i <= idx0; i++)
+ vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
+ e->data = data;
+ }
+ else
+ ASSERT (0);
+
+ va_end (va);
+
+ clib_mem_set_heap (oldheap);
+
+ if (will_expand)
+ vlib_stats_segment_unlock ();
+}
+
+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;
+}