blob: e3a1a8a04d692c969af5bd0e274b6f2209c39d89 [file] [log] [blame]
Damjan Marion8973b072022-03-01 15:51:18 +01001/* 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
8vlib_stats_main_t vlib_stats_main;
9
Damjan Marion8973b072022-03-01 15:51:18 +010010void
11vlib_stats_segment_lock (void)
12{
Damjan Mariond1bd9af2022-03-14 13:04:38 +010013 vlib_main_t *vm = vlib_get_main ();
Damjan Marion8973b072022-03-01 15:51:18 +010014 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
Damjan Mariond1bd9af2022-03-14 13:04:38 +010015
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
Damjan Marion8973b072022-03-01 15:51:18 +010025 clib_spinlock_lock (sm->stat_segment_lockp);
Damjan Mariond1bd9af2022-03-14 13:04:38 +010026
Damjan Marion8973b072022-03-01 15:51:18 +010027 sm->shared_header->in_progress = 1;
Damjan Mariond1bd9af2022-03-14 13:04:38 +010028 sm->locking_thread_index = vm->thread_index;
29done:
30 sm->n_locks++;
Damjan Marion8973b072022-03-01 15:51:18 +010031}
32
33void
34vlib_stats_segment_unlock (void)
35{
Damjan Mariond1bd9af2022-03-14 13:04:38 +010036 vlib_main_t *vm = vlib_get_main ();
Damjan Marion8973b072022-03-01 15:51:18 +010037 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
Damjan Mariond1bd9af2022-03-14 13:04:38 +010038
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
Damjan Marion8973b072022-03-01 15:51:18 +010048 sm->shared_header->epoch++;
Damjan Mariond1bd9af2022-03-14 13:04:38 +010049 __atomic_store_n (&sm->shared_header->in_progress, 0, __ATOMIC_RELEASE);
50 sm->locking_thread_index = ~0;
Damjan Marion8973b072022-03-01 15:51:18 +010051 clib_spinlock_unlock (sm->stat_segment_lockp);
52}
53
54/*
55 * Change heap to the stats shared memory segment
56 */
57void *
58vlib_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
66u32
67vlib_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
83static void
84hash_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
92static void
93hash_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
104u32
105vlib_stats_create_counter (vlib_stats_entry_t *e)
106{
107 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
Damjan Marioneecec8c2022-04-06 12:06:41 +0200108 u32 index;
Damjan Marion8973b072022-03-01 15:51:18 +0100109
Damjan Marioneecec8c2022-04-06 12:06:41 +0200110 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 }
Damjan Marion8973b072022-03-01 15:51:18 +0100120
Damjan Marion8973b072022-03-01 15:51:18 +0100121 sm->directory_vector[index] = *e;
122
Damjan Marion8973b072022-03-01 15:51:18 +0100123 hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, index);
124
125 return index;
126}
127
128void
129vlib_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);
Damjan Marion8973b072022-03-01 15:51:18 +0100133 counter_t **c;
Damjan Marion58fd4812022-03-14 13:04:38 +0100134 vlib_counter_t **vc;
Damjan Marionc5d81b92022-04-15 15:53:17 +0200135 void *oldheap;
Damjan Marion8973b072022-03-01 15:51:18 +0100136 u32 i;
137
138 if (entry_index >= vec_len (sm->directory_vector))
139 return;
140
Damjan Marion58fd4812022-03-14 13:04:38 +0100141 vlib_stats_segment_lock ();
142
Damjan Marion8973b072022-03-01 15:51:18 +0100143 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;
Damjan Marionc5d81b92022-04-15 15:53:17 +0200154 oldheap = clib_mem_set_heap (sm->heap);
Damjan Marion8973b072022-03-01 15:51:18 +0100155 for (i = 0; i < vec_len (c); i++)
156 vec_free (c[i]);
157 vec_free (c);
Damjan Marionc5d81b92022-04-15 15:53:17 +0200158 clib_mem_set_heap (oldheap);
Damjan Marion8973b072022-03-01 15:51:18 +0100159 break;
160
Damjan Marion58fd4812022-03-14 13:04:38 +0100161 case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
162 vc = e->data;
163 e->data = 0;
Damjan Marionc5d81b92022-04-15 15:53:17 +0200164 oldheap = clib_mem_set_heap (sm->heap);
Damjan Marion58fd4812022-03-14 13:04:38 +0100165 for (i = 0; i < vec_len (vc); i++)
166 vec_free (vc[i]);
167 vec_free (vc);
Damjan Marionc5d81b92022-04-15 15:53:17 +0200168 clib_mem_set_heap (oldheap);
Damjan Marion58fd4812022-03-14 13:04:38 +0100169 break;
170
Damjan Marion8973b072022-03-01 15:51:18 +0100171 case STAT_DIR_TYPE_SCALAR_INDEX:
172 case STAT_DIR_TYPE_SYMLINK:
173 break;
174 default:
175 ASSERT (0);
176 }
177
Damjan Marion58fd4812022-03-14 13:04:38 +0100178 vlib_stats_segment_unlock ();
179
Damjan Marion8973b072022-03-01 15:51:18 +0100180 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;
Damjan Marioneecec8c2022-04-06 12:06:41 +0200184
185 e->value = sm->dir_vector_first_free_elt;
186 sm->dir_vector_first_free_elt = entry_index;
Damjan Marion8973b072022-03-01 15:51:18 +0100187}
188
189static void
190vlib_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
Damjan Marion58fd4812022-03-14 13:04:38 +0100204static u32
205vlib_stats_new_entry_internal (stat_directory_type_t t, u8 *name)
Damjan Marion8973b072022-03-01 15:51:18 +0100206{
Damjan Marion8973b072022-03-01 15:51:18 +0100207 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
208 vlib_stats_shared_header_t *shared_header = sm->shared_header;
Damjan Marion58fd4812022-03-14 13:04:38 +0100209 vlib_stats_entry_t e = { .type = t };
Damjan Marion8973b072022-03-01 15:51:18 +0100210
211 ASSERT (shared_header);
212
Damjan Marion58fd4812022-03-14 13:04:38 +0100213 u32 vector_index = vlib_stats_find_entry_index ("%v", name);
214 if (vector_index != STAT_SEGMENT_INDEX_INVALID) /* Already registered */
Damjan Marion8973b072022-03-01 15:51:18 +0100215 {
Damjan Marion58fd4812022-03-14 13:04:38 +0100216 vector_index = ~0;
217 goto done;
Damjan Marion8973b072022-03-01 15:51:18 +0100218 }
219
Damjan Marion58fd4812022-03-14 13:04:38 +0100220 vec_add1 (name, 0);
221 vlib_stats_set_entry_name (&e, (char *) name);
Damjan Marion8973b072022-03-01 15:51:18 +0100222
Damjan Marion58fd4812022-03-14 13:04:38 +0100223 vlib_stats_segment_lock ();
224 vector_index = vlib_stats_create_counter (&e);
225
Damjan Marion8973b072022-03-01 15:51:18 +0100226 shared_header->directory_vector = sm->directory_vector;
227
228 vlib_stats_segment_unlock ();
Damjan Marion58fd4812022-03-14 13:04:38 +0100229
230done:
231 vec_free (name);
232 return vector_index;
233}
234
235u32
236vlib_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);
Damjan Marion8973b072022-03-01 15:51:18 +0100245}
246
247void
Damjan Marion8973b072022-03-01 15:51:18 +0100248vlib_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
256u32
257vlib_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
268void
269vlib_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
Damjan Marionc5d81b92022-04-15 15:53:17 +0200277vlib_stats_string_vector_t
Damjan Marion8973b072022-03-01 15:51:18 +0100278vlib_stats_add_string_vector (char *fmt, ...)
279{
Damjan Marionc5d81b92022-04-15 15:53:17 +0200280 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
Damjan Marion8973b072022-03-01 15:51:18 +0100281 va_list va;
Damjan Marionc5d81b92022-04-15 15:53:17 +0200282 vlib_stats_header_t *sh;
283 vlib_stats_string_vector_t sv;
284 u32 index;
Damjan Marion8973b072022-03-01 15:51:18 +0100285 u8 *name;
286
287 va_start (va, fmt);
288 name = va_format (0, fmt, &va);
289 va_end (va);
Damjan Marionc5d81b92022-04-15 15:53:17 +0200290
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;
Damjan Marion8973b072022-03-01 15:51:18 +0100301}
302
303void
Damjan Marionc5d81b92022-04-15 15:53:17 +0200304vlib_stats_set_string_vector (vlib_stats_string_vector_t *svp,
305 u32 vector_index, char *fmt, ...)
Damjan Marion8973b072022-03-01 15:51:18 +0100306{
307 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
Damjan Marionc5d81b92022-04-15 15:53:17 +0200308 vlib_stats_header_t *sh = vec_header (*svp);
309 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, sh->entry_index);
Damjan Marion8973b072022-03-01 15:51:18 +0100310 va_list va;
Damjan Marionc5d81b92022-04-15 15:53:17 +0200311 u8 *s;
Damjan Marion8973b072022-03-01 15:51:18 +0100312
Damjan Marionc5d81b92022-04-15 15:53:17 +0200313 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
Damjan Marion8973b072022-03-01 15:51:18 +0100327 vlib_stats_segment_lock ();
328
Damjan Marionc5d81b92022-04-15 15:53:17 +0200329 ASSERT (e->string_vector);
330
Damjan Marion8973b072022-03-01 15:51:18 +0100331 vec_validate (e->string_vector, vector_index);
Damjan Marionc5d81b92022-04-15 15:53:17 +0200332 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);
Damjan Marion8973b072022-03-01 15:51:18 +0100340
341 va_start (va, fmt);
Damjan Marionc5d81b92022-04-15 15:53:17 +0200342 s = va_format (s, fmt, &va);
Damjan Marion8973b072022-03-01 15:51:18 +0100343 va_end (va);
Damjan Marion4cfb46d2022-05-06 12:38:19 +0200344 vec_add1 (s, 0);
Damjan Marion8973b072022-03-01 15:51:18 +0100345
Damjan Marionc5d81b92022-04-15 15:53:17 +0200346 e->string_vector[vector_index] = s;
347
Damjan Marion8973b072022-03-01 15:51:18 +0100348 vlib_stats_segment_unlock ();
Damjan Marion8973b072022-03-01 15:51:18 +0100349}
350
351u32
352vlib_stats_add_counter_vector (char *fmt, ...)
353{
354 va_list va;
355 u8 *name;
356
357 va_start (va, fmt);
358 name = va_format (0, fmt, &va);
359 va_end (va);
360 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE,
361 name);
362}
363
Damjan Marion58fd4812022-03-14 13:04:38 +0100364u32
365vlib_stats_add_counter_pair_vector (char *fmt, ...)
366{
367 va_list va;
368 u8 *name;
369
370 va_start (va, fmt);
371 name = va_format (0, fmt, &va);
372 va_end (va);
373 return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED,
374 name);
375}
376
377static int
378vlib_stats_validate_will_expand_internal (u32 entry_index, va_list *va)
Damjan Marion8973b072022-03-01 15:51:18 +0100379{
380 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
381 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
382 void *oldheap;
Damjan Marion58fd4812022-03-14 13:04:38 +0100383 int rv = 1;
Damjan Marion8973b072022-03-01 15:51:18 +0100384
385 oldheap = clib_mem_set_heap (sm->heap);
Damjan Marion58fd4812022-03-14 13:04:38 +0100386 if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
387 {
388 u32 idx0 = va_arg (*va, u32);
389 u32 idx1 = va_arg (*va, u32);
390 u64 **data = e->data;
Damjan Marion8973b072022-03-01 15:51:18 +0100391
Dmitry Valter1c8c6d32022-04-08 10:08:32 +0000392 if (idx0 >= vec_len (data))
Damjan Marion58fd4812022-03-14 13:04:38 +0100393 goto done;
Damjan Marion8973b072022-03-01 15:51:18 +0100394
Damjan Marion58fd4812022-03-14 13:04:38 +0100395 for (u32 i = 0; i <= idx0; i++)
Damjan Marionf0574da2022-03-21 21:08:00 +0100396 if (idx1 >= vec_max_len (data[i]))
Damjan Marion58fd4812022-03-14 13:04:38 +0100397 goto done;
398 }
399 else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
400 {
401 u32 idx0 = va_arg (*va, u32);
402 u32 idx1 = va_arg (*va, u32);
403 vlib_counter_t **data = e->data;
404
405 va_end (*va);
406
Dmitry Valter1c8c6d32022-04-08 10:08:32 +0000407 if (idx0 >= vec_len (data))
Damjan Marion58fd4812022-03-14 13:04:38 +0100408 goto done;
409
410 for (u32 i = 0; i <= idx0; i++)
Damjan Marionf0574da2022-03-21 21:08:00 +0100411 if (idx1 >= vec_max_len (data[i]))
Damjan Marion58fd4812022-03-14 13:04:38 +0100412 goto done;
413 }
414 else
415 ASSERT (0);
416
417 rv = 0;
418done:
Damjan Marion8973b072022-03-01 15:51:18 +0100419 clib_mem_set_heap (oldheap);
Damjan Marion58fd4812022-03-14 13:04:38 +0100420 return rv;
421}
422
423int
424vlib_stats_validate_will_expand (u32 entry_index, ...)
425{
426 va_list va;
427 int rv;
428
429 va_start (va, entry_index);
430 rv = vlib_stats_validate_will_expand_internal (entry_index, &va);
431 va_end (va);
432 return rv;
433}
434
435void
436vlib_stats_validate (u32 entry_index, ...)
437{
438 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
439 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
440 void *oldheap;
441 va_list va;
442 int will_expand;
443
444 va_start (va, entry_index);
445 will_expand = vlib_stats_validate_will_expand_internal (entry_index, &va);
446 va_end (va);
447
448 if (will_expand)
449 vlib_stats_segment_lock ();
450
451 oldheap = clib_mem_set_heap (sm->heap);
452
453 va_start (va, entry_index);
454
455 if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE)
456 {
457 u32 idx0 = va_arg (va, u32);
458 u32 idx1 = va_arg (va, u32);
459 u64 **data = e->data;
460
461 vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
462
463 for (u32 i = 0; i <= idx0; i++)
464 vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
465 e->data = data;
466 }
467 else if (e->type == STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED)
468 {
469 u32 idx0 = va_arg (va, u32);
470 u32 idx1 = va_arg (va, u32);
471 vlib_counter_t **data = e->data;
472
473 vec_validate_aligned (data, idx0, CLIB_CACHE_LINE_BYTES);
474
475 for (u32 i = 0; i <= idx0; i++)
476 vec_validate_aligned (data[i], idx1, CLIB_CACHE_LINE_BYTES);
477 e->data = data;
478 }
479 else
480 ASSERT (0);
481
482 va_end (va);
483
484 clib_mem_set_heap (oldheap);
485
486 if (will_expand)
487 vlib_stats_segment_unlock ();
Damjan Marion8973b072022-03-01 15:51:18 +0100488}
489
490u32
491vlib_stats_add_symlink (u32 entry_index, u32 vector_index, char *fmt, ...)
492{
493 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
494 vlib_stats_shared_header_t *shared_header = sm->shared_header;
495 vlib_stats_entry_t e;
496 va_list va;
497 u8 *name;
498
499 ASSERT (shared_header);
500 ASSERT (entry_index < vec_len (sm->directory_vector));
501
502 va_start (va, fmt);
503 name = va_format (0, fmt, &va);
504 va_end (va);
505
506 if (vlib_stats_find_entry_index ("%v", name) == STAT_SEGMENT_INDEX_INVALID)
507 {
508 vec_add1 (name, 0);
509 vlib_stats_set_entry_name (&e, (char *) name);
510 e.type = STAT_DIR_TYPE_SYMLINK;
511 e.index1 = entry_index;
512 e.index2 = vector_index;
513 vector_index = vlib_stats_create_counter (&e);
514
515 /* Warn clients to refresh any pointers they might be holding */
516 shared_header->directory_vector = sm->directory_vector;
517 }
518 else
519 vector_index = ~0;
520
521 vec_free (name);
522 return vector_index;
523}
524
525void
526vlib_stats_rename_symlink (u64 entry_index, char *fmt, ...)
527{
528 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
529 vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index);
530 va_list va;
531 u8 *new_name;
532
533 hash_unset_str_key_free (&sm->directory_vector_by_name, e->name);
534
535 va_start (va, fmt);
536 new_name = va_format (0, fmt, &va);
537 va_end (va);
538
539 vec_add1 (new_name, 0);
540 vlib_stats_set_entry_name (e, (char *) new_name);
541 hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, entry_index);
542 vec_free (new_name);
543}
544
545f64
546vlib_stats_get_segment_update_rate (void)
547{
548 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
549 return sm->update_interval;
550}
551
552void
553vlib_stats_register_collector_fn (vlib_stats_collector_reg_t *reg)
554{
555 vlib_stats_segment_t *sm = vlib_stats_get_segment ();
556 vlib_stats_collector_t *c;
557
558 ASSERT (reg->entry_index != ~0);
559
560 pool_get_zero (sm->collectors, c);
561 c->fn = reg->collect_fn;
562 c->entry_index = reg->entry_index;
563 c->vector_index = reg->vector_index;
564 c->private_data = reg->private_data;
565
566 return;
567}