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