stats: expose symlink to stats client
[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   vec_add1 (s, 0);
345
346   e->string_vector[vector_index] = s;
347
348   vlib_stats_segment_unlock ();
349 }
350
351 void
352 vlib_stats_free_string_vector (vlib_stats_string_vector_t *sv)
353 {
354   vlib_stats_header_t *sh = vec_header (*sv);
355   vlib_stats_remove_entry (sh->entry_index);
356 }
357
358 u32
359 vlib_stats_add_counter_vector (char *fmt, ...)
360 {
361   va_list va;
362   u8 *name;
363
364   va_start (va, fmt);
365   name = va_format (0, fmt, &va);
366   va_end (va);
367   return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE,
368                                         name);
369 }
370
371 u32
372 vlib_stats_add_counter_pair_vector (char *fmt, ...)
373 {
374   va_list va;
375   u8 *name;
376
377   va_start (va, fmt);
378   name = va_format (0, fmt, &va);
379   va_end (va);
380   return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED,
381                                         name);
382 }
383
384 static int
385 vlib_stats_validate_will_expand_internal (u32 entry_index, va_list *va)
386 {
387   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
388   vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
389   void *oldheap;
390   int rv = 1;
391
392   oldheap = clib_mem_set_heap (sm->heap);
393   if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
394     {
395       u32 idx0 = va_arg (*va, u32);
396       u32 idx1 = va_arg (*va, u32);
397       u64 **data = e->data;
398
399       if (idx0 >= vec_len (data))
400         goto done;
401
402       for (u32 i = 0; i <= idx0; i++)
403         if (idx1 >= vec_max_len (data[i]))
404           goto done;
405     }
406   else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
407     {
408       u32 idx0 = va_arg (*va, u32);
409       u32 idx1 = va_arg (*va, u32);
410       vlib_counter_t **data = e->data;
411
412       va_end (*va);
413
414       if (idx0 >= vec_len (data))
415         goto done;
416
417       for (u32 i = 0; i <= idx0; i++)
418         if (idx1 >= vec_max_len (data[i]))
419           goto done;
420     }
421   else
422     ASSERT (0);
423
424   rv = 0;
425 done:
426   clib_mem_set_heap (oldheap);
427   return rv;
428 }
429
430 int
431 vlib_stats_validate_will_expand (u32 entry_index, ...)
432 {
433   va_list va;
434   int rv;
435
436   va_start (va, entry_index);
437   rv = vlib_stats_validate_will_expand_internal (entry_index, &va);
438   va_end (va);
439   return rv;
440 }
441
442 void
443 vlib_stats_validate (u32 entry_index, ...)
444 {
445   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
446   vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
447   void *oldheap;
448   va_list va;
449   int will_expand;
450
451   va_start (va, entry_index);
452   will_expand = vlib_stats_validate_will_expand_internal (entry_index, &va);
453   va_end (va);
454
455   if (will_expand)
456     vlib_stats_segment_lock ();
457
458   oldheap = clib_mem_set_heap (sm->heap);
459
460   va_start (va, entry_index);
461
462   if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
463     {
464       u32 idx0 = va_arg (va, u32);
465       u32 idx1 = va_arg (va, u32);
466       u64 **data = e->data;
467
468       vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
469
470       for (u32 i = 0; i <= idx0; i++)
471         vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
472       e->data = data;
473     }
474   else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
475     {
476       u32 idx0 = va_arg (va, u32);
477       u32 idx1 = va_arg (va, u32);
478       vlib_counter_t **data = e->data;
479
480       vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
481
482       for (u32 i = 0; i <= idx0; i++)
483         vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
484       e->data = data;
485     }
486   else
487     ASSERT (0);
488
489   va_end (va);
490
491   clib_mem_set_heap (oldheap);
492
493   if (will_expand)
494     vlib_stats_segment_unlock ();
495 }
496
497 u32
498 vlib_stats_add_symlink (u32 entry_index, u32 vector_index, char *fmt, ...)
499 {
500   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
501   vlib_stats_shared_header_t *shared_header = sm->shared_header;
502   vlib_stats_entry_t e;
503   va_list va;
504   u8 *name;
505
506   ASSERT (shared_header);
507   ASSERT (entry_index < vec_len (sm->directory_vector));
508
509   va_start (va, fmt);
510   name = va_format (0, fmt, &va);
511   va_end (va);
512
513   if (vlib_stats_find_entry_index ("%v", name) == STAT_SEGMENT_INDEX_INVALID)
514     {
515       vec_add1 (name, 0);
516       vlib_stats_set_entry_name (&e, (char *) name);
517       e.type = STAT_DIR_TYPE_SYMLINK;
518       e.index1 = entry_index;
519       e.index2 = vector_index;
520       vector_index = vlib_stats_create_counter (&e);
521
522       /* Warn clients to refresh any pointers they might be holding */
523       shared_header->directory_vector = sm->directory_vector;
524     }
525   else
526     vector_index = ~0;
527
528   vec_free (name);
529   return vector_index;
530 }
531
532 void
533 vlib_stats_rename_symlink (u64 entry_index, char *fmt, ...)
534 {
535   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
536   vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
537   va_list va;
538   u8 *new_name;
539
540   hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
541
542   va_start (va, fmt);
543   new_name = va_format (0, fmt, &va);
544   va_end (va);
545
546   vec_add1 (new_name, 0);
547   vlib_stats_set_entry_name (e, (char *) new_name);
548   hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, entry_index);
549   vec_free (new_name);
550 }
551
552 f64
553 vlib_stats_get_segment_update_rate (void)
554 {
555   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
556   return sm->update_interval;
557 }
558
559 void
560 vlib_stats_register_collector_fn (vlib_stats_collector_reg_t *reg)
561 {
562   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
563   vlib_stats_collector_t *c;
564
565   ASSERT (reg->entry_index != ~0);
566
567   pool_get_zero (sm->collectors, c);
568   c->fn = reg->collect_fn;
569   c->entry_index = reg->entry_index;
570   c->vector_index = reg->vector_index;
571   c->private_data = reg->private_data;
572
573   return;
574 }