stats: string vector and node collector improvements
[vpp.git] / src / vlib / stats / stats.c
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright(c) 2022 Cisco Systems, Inc.
3  */
4
5 #include <vlib/vlib.h>
6 #include <vlib/stats/stats.h>
7
8 vlib_stats_main_t vlib_stats_main;
9
10 void
11 vlib_stats_segment_lock (void)
12 {
13   vlib_main_t *vm = vlib_get_main ();
14   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
15
16   /* already locked by us */
17   if (sm->shared_header->in_progress &&
18       vm->thread_index == sm->locking_thread_index)
19     goto done;
20
21   ASSERT (sm->locking_thread_index == ~0);
22   ASSERT (sm->shared_header->in_progress == 0);
23   ASSERT (sm->n_locks == 0);
24
25   clib_spinlock_lock (sm->stat_segment_lockp);
26
27   sm->shared_header->in_progress = 1;
28   sm->locking_thread_index = vm->thread_index;
29 done:
30   sm->n_locks++;
31 }
32
33 void
34 vlib_stats_segment_unlock (void)
35 {
36   vlib_main_t *vm = vlib_get_main ();
37   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
38
39   ASSERT (sm->shared_header->in_progress == 1);
40   ASSERT (sm->locking_thread_index == vm->thread_index);
41   ASSERT (sm->n_locks > 0);
42
43   sm->n_locks--;
44
45   if (sm->n_locks > 0)
46     return;
47
48   sm->shared_header->epoch++;
49   __atomic_store_n (&sm->shared_header->in_progress, 0, __ATOMIC_RELEASE);
50   sm->locking_thread_index = ~0;
51   clib_spinlock_unlock (sm->stat_segment_lockp);
52 }
53
54 /*
55  * Change heap to the stats shared memory segment
56  */
57 void *
58 vlib_stats_set_heap ()
59 {
60   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
61
62   ASSERT (sm && sm->shared_header);
63   return clib_mem_set_heap (sm->heap);
64 }
65
66 u32
67 vlib_stats_find_entry_index (char *fmt, ...)
68 {
69   u8 *name;
70   va_list va;
71
72   va_start (va, fmt);
73   name = va_format (0, fmt, &va);
74   va_end (va);
75   vec_add1 (name, 0);
76
77   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
78   hash_pair_t *hp = hash_get_pair (sm->directory_vector_by_name, name);
79   vec_free (name);
80   return hp ? hp->value[0] : STAT_SEGMENT_INDEX_INVALID;
81 }
82
83 static void
84 hash_set_str_key_alloc (uword **h, const char *key, uword v)
85 {
86   int size = strlen (key) + 1;
87   void *copy = clib_mem_alloc (size);
88   clib_memcpy_fast (copy, key, size);
89   hash_set_mem (*h, copy, v);
90 }
91
92 static void
93 hash_unset_str_key_free (uword **h, const char *key)
94 {
95   hash_pair_t *hp = hash_get_pair_mem (*h, key);
96   if (hp)
97     {
98       void *_k = uword_to_pointer (hp->key, void *);
99       hash_unset_mem (*h, _k);
100       clib_mem_free (_k);
101     }
102 }
103
104 u32
105 vlib_stats_create_counter (vlib_stats_entry_t *e)
106 {
107   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
108   u32 index;
109
110   if (sm->dir_vector_first_free_elt != CLIB_U32_MAX)
111     {
112       index = sm->dir_vector_first_free_elt;
113       sm->dir_vector_first_free_elt = sm->directory_vector[index].index;
114     }
115   else
116     {
117       index = vec_len (sm->directory_vector);
118       vec_validate (sm->directory_vector, index);
119     }
120
121   sm->directory_vector[index] = *e;
122
123   hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, index);
124
125   return index;
126 }
127
128 void
129 vlib_stats_remove_entry (u32 entry_index)
130 {
131   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
132   vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
133   counter_t **c;
134   vlib_counter_t **vc;
135   void *oldheap;
136   u32 i;
137
138   if (entry_index >= vec_len (sm->directory_vector))
139     return;
140
141   vlib_stats_segment_lock ();
142
143   switch (e->type)
144     {
145     case STAT_DIR_TYPE_NAME_VECTOR:
146       for (i = 0; i < vec_len (e->string_vector); i++)
147         vec_free (e->string_vector[i]);
148       vec_free (e->string_vector);
149       break;
150
151     case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
152       c = e->data;
153       e->data = 0;
154       oldheap = clib_mem_set_heap (sm->heap);
155       for (i = 0; i < vec_len (c); i++)
156         vec_free (c[i]);
157       vec_free (c);
158       clib_mem_set_heap (oldheap);
159       break;
160
161     case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
162       vc = e->data;
163       e->data = 0;
164       oldheap = clib_mem_set_heap (sm->heap);
165       for (i = 0; i < vec_len (vc); i++)
166         vec_free (vc[i]);
167       vec_free (vc);
168       clib_mem_set_heap (oldheap);
169       break;
170
171     case STAT_DIR_TYPE_SCALAR_INDEX:
172     case STAT_DIR_TYPE_SYMLINK:
173       break;
174     default:
175       ASSERT (0);
176     }
177
178   vlib_stats_segment_unlock ();
179
180   hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
181
182   memset (e, 0, sizeof (*e));
183   e->type = STAT_DIR_TYPE_EMPTY;
184
185   e->value = sm->dir_vector_first_free_elt;
186   sm->dir_vector_first_free_elt = entry_index;
187 }
188
189 static void
190 vlib_stats_set_entry_name (vlib_stats_entry_t *e, char *s)
191 {
192   u32 i, len = VLIB_STATS_MAX_NAME_SZ - 1;
193
194   for (i = 0; i < len; i++)
195     {
196       e->name[i] = s[i];
197       if (s[i] == 0)
198         return;
199     }
200   ASSERT (i < VLIB_STATS_MAX_NAME_SZ - 1);
201   s[i] = 0;
202 }
203
204 static u32
205 vlib_stats_new_entry_internal (stat_directory_type_t t, u8 *name)
206 {
207   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
208   vlib_stats_shared_header_t *shared_header = sm->shared_header;
209   vlib_stats_entry_t e = { .type = t };
210
211   ASSERT (shared_header);
212
213   u32 vector_index = vlib_stats_find_entry_index ("%v", name);
214   if (vector_index != STAT_SEGMENT_INDEX_INVALID) /* Already registered */
215     {
216       vector_index = ~0;
217       goto done;
218     }
219
220   vec_add1 (name, 0);
221   vlib_stats_set_entry_name (&e, (char *) name);
222
223   vlib_stats_segment_lock ();
224   vector_index = vlib_stats_create_counter (&e);
225
226   shared_header->directory_vector = sm->directory_vector;
227
228   vlib_stats_segment_unlock ();
229
230 done:
231   vec_free (name);
232   return vector_index;
233 }
234
235 u32
236 vlib_stats_add_gauge (char *fmt, ...)
237 {
238   va_list va;
239   u8 *name;
240
241   va_start (va, fmt);
242   name = va_format (0, fmt, &va);
243   va_end (va);
244   return vlib_stats_new_entry_internal (STAT_DIR_TYPE_SCALAR_INDEX, name);
245 }
246
247 void
248 vlib_stats_set_gauge (u32 index, u64 value)
249 {
250   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
251
252   ASSERT (index < vec_len (sm->directory_vector));
253   sm->directory_vector[index].value = value;
254 }
255
256 u32
257 vlib_stats_add_timestamp (char *fmt, ...)
258 {
259   va_list va;
260   u8 *name;
261
262   va_start (va, fmt);
263   name = va_format (0, fmt, &va);
264   va_end (va);
265   return vlib_stats_new_entry_internal (STAT_DIR_TYPE_SCALAR_INDEX, name);
266 }
267
268 void
269 vlib_stats_set_timestamp (u32 entry_index, f64 value)
270 {
271   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
272
273   ASSERT (entry_index < vec_len (sm->directory_vector));
274   sm->directory_vector[entry_index].value = value;
275 }
276
277 vlib_stats_string_vector_t
278 vlib_stats_add_string_vector (char *fmt, ...)
279 {
280   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
281   va_list va;
282   vlib_stats_header_t *sh;
283   vlib_stats_string_vector_t sv;
284   u32 index;
285   u8 *name;
286
287   va_start (va, fmt);
288   name = va_format (0, fmt, &va);
289   va_end (va);
290
291   index = vlib_stats_new_entry_internal (STAT_DIR_TYPE_NAME_VECTOR, name);
292   if (index == CLIB_U32_MAX)
293     return 0;
294
295   sv = vec_new_generic (vlib_stats_string_vector_t, 0,
296                         sizeof (vlib_stats_header_t), 0, sm->heap);
297   sh = vec_header (sv);
298   sh->entry_index = index;
299   sm->directory_vector[index].string_vector = sv;
300   return sv;
301 }
302
303 void
304 vlib_stats_set_string_vector (vlib_stats_string_vector_t *svp,
305                               u32 vector_index, char *fmt, ...)
306 {
307   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
308   vlib_stats_header_t *sh = vec_header (*svp);
309   vlib_stats_entry_t *e = vlib_stats_get_entry (sm, sh->entry_index);
310   va_list va;
311   u8 *s;
312
313   if (fmt[0] == 0)
314     {
315       if (vec_len (e->string_vector) <= vector_index)
316         return;
317
318       if (e->string_vector[vector_index] == 0)
319         return;
320
321       vlib_stats_segment_lock ();
322       vec_free (e->string_vector[vector_index]);
323       vlib_stats_segment_unlock ();
324       return;
325     }
326
327   vlib_stats_segment_lock ();
328
329   ASSERT (e->string_vector);
330
331   vec_validate (e->string_vector, vector_index);
332   svp[0] = e->string_vector;
333
334   s = e->string_vector[vector_index];
335
336   if (s == 0)
337     s = vec_new_heap (u8 *, 0, sm->heap);
338
339   vec_reset_length (s);
340
341   va_start (va, fmt);
342   s = va_format (s, fmt, &va);
343   va_end (va);
344
345   e->string_vector[vector_index] = s;
346
347   vlib_stats_segment_unlock ();
348 }
349
350 u32
351 vlib_stats_add_counter_vector (char *fmt, ...)
352 {
353   va_list va;
354   u8 *name;
355
356   va_start (va, fmt);
357   name = va_format (0, fmt, &va);
358   va_end (va);
359   return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE,
360                                         name);
361 }
362
363 u32
364 vlib_stats_add_counter_pair_vector (char *fmt, ...)
365 {
366   va_list va;
367   u8 *name;
368
369   va_start (va, fmt);
370   name = va_format (0, fmt, &va);
371   va_end (va);
372   return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED,
373                                         name);
374 }
375
376 static int
377 vlib_stats_validate_will_expand_internal (u32 entry_index, va_list *va)
378 {
379   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
380   vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
381   void *oldheap;
382   int rv = 1;
383
384   oldheap = clib_mem_set_heap (sm->heap);
385   if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
386     {
387       u32 idx0 = va_arg (*va, u32);
388       u32 idx1 = va_arg (*va, u32);
389       u64 **data = e->data;
390
391       if (idx0 >= vec_len (data))
392         goto done;
393
394       for (u32 i = 0; i <= idx0; i++)
395         if (idx1 >= vec_max_len (data[i]))
396           goto done;
397     }
398   else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
399     {
400       u32 idx0 = va_arg (*va, u32);
401       u32 idx1 = va_arg (*va, u32);
402       vlib_counter_t **data = e->data;
403
404       va_end (*va);
405
406       if (idx0 >= vec_len (data))
407         goto done;
408
409       for (u32 i = 0; i <= idx0; i++)
410         if (idx1 >= vec_max_len (data[i]))
411           goto done;
412     }
413   else
414     ASSERT (0);
415
416   rv = 0;
417 done:
418   clib_mem_set_heap (oldheap);
419   return rv;
420 }
421
422 int
423 vlib_stats_validate_will_expand (u32 entry_index, ...)
424 {
425   va_list va;
426   int rv;
427
428   va_start (va, entry_index);
429   rv = vlib_stats_validate_will_expand_internal (entry_index, &va);
430   va_end (va);
431   return rv;
432 }
433
434 void
435 vlib_stats_validate (u32 entry_index, ...)
436 {
437   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
438   vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
439   void *oldheap;
440   va_list va;
441   int will_expand;
442
443   va_start (va, entry_index);
444   will_expand = vlib_stats_validate_will_expand_internal (entry_index, &va);
445   va_end (va);
446
447   if (will_expand)
448     vlib_stats_segment_lock ();
449
450   oldheap = clib_mem_set_heap (sm->heap);
451
452   va_start (va, entry_index);
453
454   if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
455     {
456       u32 idx0 = va_arg (va, u32);
457       u32 idx1 = va_arg (va, u32);
458       u64 **data = e->data;
459
460       vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
461
462       for (u32 i = 0; i <= idx0; i++)
463         vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
464       e->data = data;
465     }
466   else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
467     {
468       u32 idx0 = va_arg (va, u32);
469       u32 idx1 = va_arg (va, u32);
470       vlib_counter_t **data = e->data;
471
472       vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
473
474       for (u32 i = 0; i <= idx0; i++)
475         vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
476       e->data = data;
477     }
478   else
479     ASSERT (0);
480
481   va_end (va);
482
483   clib_mem_set_heap (oldheap);
484
485   if (will_expand)
486     vlib_stats_segment_unlock ();
487 }
488
489 u32
490 vlib_stats_add_symlink (u32 entry_index, u32 vector_index, char *fmt, ...)
491 {
492   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
493   vlib_stats_shared_header_t *shared_header = sm->shared_header;
494   vlib_stats_entry_t e;
495   va_list va;
496   u8 *name;
497
498   ASSERT (shared_header);
499   ASSERT (entry_index < vec_len (sm->directory_vector));
500
501   va_start (va, fmt);
502   name = va_format (0, fmt, &va);
503   va_end (va);
504
505   if (vlib_stats_find_entry_index ("%v", name) == STAT_SEGMENT_INDEX_INVALID)
506     {
507       vec_add1 (name, 0);
508       vlib_stats_set_entry_name (&e, (char *) name);
509       e.type = STAT_DIR_TYPE_SYMLINK;
510       e.index1 = entry_index;
511       e.index2 = vector_index;
512       vector_index = vlib_stats_create_counter (&e);
513
514       /* Warn clients to refresh any pointers they might be holding */
515       shared_header->directory_vector = sm->directory_vector;
516     }
517   else
518     vector_index = ~0;
519
520   vec_free (name);
521   return vector_index;
522 }
523
524 void
525 vlib_stats_rename_symlink (u64 entry_index, char *fmt, ...)
526 {
527   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
528   vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
529   va_list va;
530   u8 *new_name;
531
532   hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
533
534   va_start (va, fmt);
535   new_name = va_format (0, fmt, &va);
536   va_end (va);
537
538   vec_add1 (new_name, 0);
539   vlib_stats_set_entry_name (e, (char *) new_name);
540   hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, entry_index);
541   vec_free (new_name);
542 }
543
544 f64
545 vlib_stats_get_segment_update_rate (void)
546 {
547   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
548   return sm->update_interval;
549 }
550
551 void
552 vlib_stats_register_collector_fn (vlib_stats_collector_reg_t *reg)
553 {
554   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
555   vlib_stats_collector_t *c;
556
557   ASSERT (reg->entry_index != ~0);
558
559   pool_get_zero (sm->collectors, c);
560   c->fn = reg->collect_fn;
561   c->entry_index = reg->entry_index;
562   c->vector_index = reg->vector_index;
563   c->private_data = reg->private_data;
564
565   return;
566 }