61dadd74b1c448921c49204d44c664e5df324a7c
[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   u32 i;
136
137   if (entry_index >= vec_len (sm->directory_vector))
138     return;
139
140   vlib_stats_segment_lock ();
141
142   switch (e->type)
143     {
144     case STAT_DIR_TYPE_NAME_VECTOR:
145       for (i = 0; i < vec_len (e->string_vector); i++)
146         vec_free (e->string_vector[i]);
147       vec_free (e->string_vector);
148       break;
149
150     case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
151       c = e->data;
152       e->data = 0;
153       for (i = 0; i < vec_len (c); i++)
154         vec_free (c[i]);
155       vec_free (c);
156       break;
157
158     case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
159       vc = e->data;
160       e->data = 0;
161       for (i = 0; i < vec_len (vc); i++)
162         vec_free (vc[i]);
163       vec_free (vc);
164       break;
165
166     case STAT_DIR_TYPE_SCALAR_INDEX:
167     case STAT_DIR_TYPE_SYMLINK:
168       break;
169     default:
170       ASSERT (0);
171     }
172
173   vlib_stats_segment_unlock ();
174
175   hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
176
177   memset (e, 0, sizeof (*e));
178   e->type = STAT_DIR_TYPE_EMPTY;
179
180   e->value = sm->dir_vector_first_free_elt;
181   sm->dir_vector_first_free_elt = entry_index;
182 }
183
184 static void
185 vlib_stats_set_entry_name (vlib_stats_entry_t *e, char *s)
186 {
187   u32 i, len = VLIB_STATS_MAX_NAME_SZ - 1;
188
189   for (i = 0; i < len; i++)
190     {
191       e->name[i] = s[i];
192       if (s[i] == 0)
193         return;
194     }
195   ASSERT (i < VLIB_STATS_MAX_NAME_SZ - 1);
196   s[i] = 0;
197 }
198
199 static u32
200 vlib_stats_new_entry_internal (stat_directory_type_t t, u8 *name)
201 {
202   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
203   vlib_stats_shared_header_t *shared_header = sm->shared_header;
204   vlib_stats_entry_t e = { .type = t };
205
206   ASSERT (shared_header);
207
208   u32 vector_index = vlib_stats_find_entry_index ("%v", name);
209   if (vector_index != STAT_SEGMENT_INDEX_INVALID) /* Already registered */
210     {
211       vector_index = ~0;
212       goto done;
213     }
214
215   vec_add1 (name, 0);
216   vlib_stats_set_entry_name (&e, (char *) name);
217
218   vlib_stats_segment_lock ();
219   vector_index = vlib_stats_create_counter (&e);
220
221   shared_header->directory_vector = sm->directory_vector;
222
223   vlib_stats_segment_unlock ();
224
225 done:
226   vec_free (name);
227   return vector_index;
228 }
229
230 u32
231 vlib_stats_add_gauge (char *fmt, ...)
232 {
233   va_list va;
234   u8 *name;
235
236   va_start (va, fmt);
237   name = va_format (0, fmt, &va);
238   va_end (va);
239   return vlib_stats_new_entry_internal (STAT_DIR_TYPE_SCALAR_INDEX, name);
240 }
241
242 void
243 vlib_stats_set_gauge (u32 index, u64 value)
244 {
245   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
246
247   ASSERT (index < vec_len (sm->directory_vector));
248   sm->directory_vector[index].value = value;
249 }
250
251 u32
252 vlib_stats_add_timestamp (char *fmt, ...)
253 {
254   va_list va;
255   u8 *name;
256
257   va_start (va, fmt);
258   name = va_format (0, fmt, &va);
259   va_end (va);
260   return vlib_stats_new_entry_internal (STAT_DIR_TYPE_SCALAR_INDEX, name);
261 }
262
263 void
264 vlib_stats_set_timestamp (u32 entry_index, f64 value)
265 {
266   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
267
268   ASSERT (entry_index < vec_len (sm->directory_vector));
269   sm->directory_vector[entry_index].value = value;
270 }
271
272 u32
273 vlib_stats_add_string_vector (char *fmt, ...)
274 {
275   va_list va;
276   u8 *name;
277
278   va_start (va, fmt);
279   name = va_format (0, fmt, &va);
280   va_end (va);
281   return vlib_stats_new_entry_internal (STAT_DIR_TYPE_NAME_VECTOR, name);
282 }
283
284 void
285 vlib_stats_set_string_vector (u32 entry_index, u32 vector_index, char *fmt,
286                               ...)
287 {
288   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
289   vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
290   va_list va;
291   void *oldheap;
292
293   oldheap = clib_mem_set_heap (sm->heap);
294   vlib_stats_segment_lock ();
295
296   vec_validate (e->string_vector, vector_index);
297   vec_reset_length (e->string_vector[vector_index]);
298
299   va_start (va, fmt);
300   e->string_vector[vector_index] =
301     va_format (e->string_vector[vector_index], fmt, &va);
302   va_end (va);
303
304   vlib_stats_segment_unlock ();
305   clib_mem_set_heap (oldheap);
306 }
307
308 u32
309 vlib_stats_add_counter_vector (char *fmt, ...)
310 {
311   va_list va;
312   u8 *name;
313
314   va_start (va, fmt);
315   name = va_format (0, fmt, &va);
316   va_end (va);
317   return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE,
318                                         name);
319 }
320
321 u32
322 vlib_stats_add_counter_pair_vector (char *fmt, ...)
323 {
324   va_list va;
325   u8 *name;
326
327   va_start (va, fmt);
328   name = va_format (0, fmt, &va);
329   va_end (va);
330   return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED,
331                                         name);
332 }
333
334 static int
335 vlib_stats_validate_will_expand_internal (u32 entry_index, va_list *va)
336 {
337   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
338   vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
339   void *oldheap;
340   int rv = 1;
341
342   oldheap = clib_mem_set_heap (sm->heap);
343   if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
344     {
345       u32 idx0 = va_arg (*va, u32);
346       u32 idx1 = va_arg (*va, u32);
347       u64 **data = e->data;
348
349       if (idx0 >= vec_len (data))
350         goto done;
351
352       for (u32 i = 0; i <= idx0; i++)
353         if (idx1 >= vec_max_len (data[i]))
354           goto done;
355     }
356   else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
357     {
358       u32 idx0 = va_arg (*va, u32);
359       u32 idx1 = va_arg (*va, u32);
360       vlib_counter_t **data = e->data;
361
362       va_end (*va);
363
364       if (idx0 >= vec_len (data))
365         goto done;
366
367       for (u32 i = 0; i <= idx0; i++)
368         if (idx1 >= vec_max_len (data[i]))
369           goto done;
370     }
371   else
372     ASSERT (0);
373
374   rv = 0;
375 done:
376   clib_mem_set_heap (oldheap);
377   return rv;
378 }
379
380 int
381 vlib_stats_validate_will_expand (u32 entry_index, ...)
382 {
383   va_list va;
384   int rv;
385
386   va_start (va, entry_index);
387   rv = vlib_stats_validate_will_expand_internal (entry_index, &va);
388   va_end (va);
389   return rv;
390 }
391
392 void
393 vlib_stats_validate (u32 entry_index, ...)
394 {
395   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
396   vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
397   void *oldheap;
398   va_list va;
399   int will_expand;
400
401   va_start (va, entry_index);
402   will_expand = vlib_stats_validate_will_expand_internal (entry_index, &va);
403   va_end (va);
404
405   if (will_expand)
406     vlib_stats_segment_lock ();
407
408   oldheap = clib_mem_set_heap (sm->heap);
409
410   va_start (va, entry_index);
411
412   if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
413     {
414       u32 idx0 = va_arg (va, u32);
415       u32 idx1 = va_arg (va, u32);
416       u64 **data = e->data;
417
418       vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
419
420       for (u32 i = 0; i <= idx0; i++)
421         vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
422       e->data = data;
423     }
424   else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
425     {
426       u32 idx0 = va_arg (va, u32);
427       u32 idx1 = va_arg (va, u32);
428       vlib_counter_t **data = e->data;
429
430       vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
431
432       for (u32 i = 0; i <= idx0; i++)
433         vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
434       e->data = data;
435     }
436   else
437     ASSERT (0);
438
439   va_end (va);
440
441   clib_mem_set_heap (oldheap);
442
443   if (will_expand)
444     vlib_stats_segment_unlock ();
445 }
446
447 u32
448 vlib_stats_add_symlink (u32 entry_index, u32 vector_index, char *fmt, ...)
449 {
450   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
451   vlib_stats_shared_header_t *shared_header = sm->shared_header;
452   vlib_stats_entry_t e;
453   va_list va;
454   u8 *name;
455
456   ASSERT (shared_header);
457   ASSERT (entry_index < vec_len (sm->directory_vector));
458
459   va_start (va, fmt);
460   name = va_format (0, fmt, &va);
461   va_end (va);
462
463   if (vlib_stats_find_entry_index ("%v", name) == STAT_SEGMENT_INDEX_INVALID)
464     {
465       vec_add1 (name, 0);
466       vlib_stats_set_entry_name (&e, (char *) name);
467       e.type = STAT_DIR_TYPE_SYMLINK;
468       e.index1 = entry_index;
469       e.index2 = vector_index;
470       vector_index = vlib_stats_create_counter (&e);
471
472       /* Warn clients to refresh any pointers they might be holding */
473       shared_header->directory_vector = sm->directory_vector;
474     }
475   else
476     vector_index = ~0;
477
478   vec_free (name);
479   return vector_index;
480 }
481
482 void
483 vlib_stats_rename_symlink (u64 entry_index, char *fmt, ...)
484 {
485   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
486   vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
487   va_list va;
488   u8 *new_name;
489
490   hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
491
492   va_start (va, fmt);
493   new_name = va_format (0, fmt, &va);
494   va_end (va);
495
496   vec_add1 (new_name, 0);
497   vlib_stats_set_entry_name (e, (char *) new_name);
498   hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, entry_index);
499   vec_free (new_name);
500 }
501
502 f64
503 vlib_stats_get_segment_update_rate (void)
504 {
505   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
506   return sm->update_interval;
507 }
508
509 void
510 vlib_stats_register_collector_fn (vlib_stats_collector_reg_t *reg)
511 {
512   vlib_stats_segment_t *sm = vlib_stats_get_segment ();
513   vlib_stats_collector_t *c;
514
515   ASSERT (reg->entry_index != ~0);
516
517   pool_get_zero (sm->collectors, c);
518   c->fn = reg->collect_fn;
519   c->entry_index = reg->entry_index;
520   c->vector_index = reg->vector_index;
521   c->private_data = reg->private_data;
522
523   return;
524 }