| /* SPDX-License-Identifier: Apache-2.0 |
| * Copyright(c) 2022 Cisco Systems, Inc. |
| */ |
| |
| #include <vlib/vlib.h> |
| #include <vlib/unix/unix.h> |
| #include <vlib/stats/stats.h> |
| |
| static void |
| stat_validate_counter_vector2 (vlib_stats_entry_t *ep, u32 max1, u32 max2) |
| { |
| counter_t **counters = ep->data; |
| int i; |
| vec_validate_aligned (counters, max1, CLIB_CACHE_LINE_BYTES); |
| for (i = 0; i <= max1; i++) |
| vec_validate_aligned (counters[i], max2, CLIB_CACHE_LINE_BYTES); |
| |
| ep->data = counters; |
| } |
| |
| static void |
| stat_validate_counter_vector (vlib_stats_entry_t *ep, u32 max) |
| { |
| vlib_thread_main_t *tm = vlib_get_thread_main (); |
| ASSERT (tm->n_vlib_mains > 0); |
| stat_validate_counter_vector2 (ep, tm->n_vlib_mains, max); |
| } |
| |
| static inline void |
| update_node_counters (vlib_stats_segment_t *sm) |
| { |
| vlib_main_t **stat_vms = 0; |
| vlib_node_t ***node_dups = 0; |
| int i, j; |
| static u32 no_max_nodes = 0; |
| |
| vlib_node_get_nodes (0 /* vm, for barrier sync */, |
| (u32) ~0 /* all threads */, 1 /* include stats */, |
| 0 /* barrier sync */, &node_dups, &stat_vms); |
| |
| u32 l = vec_len (node_dups[0]); |
| u8 *symlink_name = 0; |
| |
| /* |
| * Extend performance nodes if necessary |
| */ |
| if (l > no_max_nodes) |
| { |
| void *oldheap = clib_mem_set_heap (sm->heap); |
| vlib_stats_segment_lock (); |
| |
| stat_validate_counter_vector ( |
| &sm->directory_vector[STAT_COUNTER_NODE_CLOCKS], l - 1); |
| stat_validate_counter_vector ( |
| &sm->directory_vector[STAT_COUNTER_NODE_VECTORS], l - 1); |
| stat_validate_counter_vector ( |
| &sm->directory_vector[STAT_COUNTER_NODE_CALLS], l - 1); |
| stat_validate_counter_vector ( |
| &sm->directory_vector[STAT_COUNTER_NODE_SUSPENDS], l - 1); |
| |
| vec_validate (sm->nodes, l - 1); |
| vlib_stats_entry_t *ep; |
| ep = &sm->directory_vector[STAT_COUNTER_NODE_NAMES]; |
| ep->data = sm->nodes; |
| |
| /* Update names dictionary */ |
| vlib_node_t **nodes = node_dups[0]; |
| int i; |
| for (i = 0; i < vec_len (nodes); i++) |
| { |
| vlib_node_t *n = nodes[i]; |
| u8 *s = format (0, "%v%c", n->name, 0); |
| if (sm->nodes[n->index]) |
| vec_free (sm->nodes[n->index]); |
| sm->nodes[n->index] = s; |
| |
| oldheap = clib_mem_set_heap (oldheap); |
| #define _(E, t, name, p) \ |
| vlib_stats_add_symlink (STAT_COUNTER_##E, n->index, "/nodes/%U/" #name, \ |
| format_vlib_stats_symlink, s); |
| foreach_stat_segment_node_counter_name |
| #undef _ |
| oldheap = clib_mem_set_heap (oldheap); |
| } |
| |
| vlib_stats_segment_unlock (); |
| clib_mem_set_heap (oldheap); |
| no_max_nodes = l; |
| } |
| |
| for (j = 0; j < vec_len (node_dups); j++) |
| { |
| vlib_node_t **nodes = node_dups[j]; |
| |
| for (i = 0; i < vec_len (nodes); i++) |
| { |
| counter_t **counters; |
| counter_t *c; |
| vlib_node_t *n = nodes[i]; |
| |
| if (j == 0) |
| { |
| if (strncmp ((char *) sm->nodes[n->index], (char *) n->name, |
| strlen ((char *) sm->nodes[n->index]))) |
| { |
| u32 vector_index; |
| void *oldheap = clib_mem_set_heap (sm->heap); |
| vlib_stats_segment_lock (); |
| u8 *s = format (0, "%v%c", n->name, 0); |
| clib_mem_set_heap (oldheap); |
| #define _(E, t, name, p) \ |
| vec_reset_length (symlink_name); \ |
| symlink_name = format (symlink_name, "/nodes/%U/" #name, \ |
| format_vlib_stats_symlink, sm->nodes[n->index]); \ |
| vector_index = vlib_stats_find_entry_index ("%v", symlink_name); \ |
| ASSERT (vector_index != -1); \ |
| vlib_stats_rename_symlink (vector_index, "/nodes/%U/" #name, \ |
| format_vlib_stats_symlink, s); |
| foreach_stat_segment_node_counter_name |
| #undef _ |
| vec_free (symlink_name); |
| clib_mem_set_heap (sm->heap); |
| vec_free (sm->nodes[n->index]); |
| sm->nodes[n->index] = s; |
| vlib_stats_segment_unlock (); |
| clib_mem_set_heap (oldheap); |
| } |
| } |
| |
| counters = sm->directory_vector[STAT_COUNTER_NODE_CLOCKS].data; |
| c = counters[j]; |
| c[n->index] = n->stats_total.clocks - n->stats_last_clear.clocks; |
| |
| counters = sm->directory_vector[STAT_COUNTER_NODE_VECTORS].data; |
| c = counters[j]; |
| c[n->index] = n->stats_total.vectors - n->stats_last_clear.vectors; |
| |
| counters = sm->directory_vector[STAT_COUNTER_NODE_CALLS].data; |
| c = counters[j]; |
| c[n->index] = n->stats_total.calls - n->stats_last_clear.calls; |
| |
| counters = sm->directory_vector[STAT_COUNTER_NODE_SUSPENDS].data; |
| c = counters[j]; |
| c[n->index] = n->stats_total.suspends - n->stats_last_clear.suspends; |
| } |
| vec_free (node_dups[j]); |
| } |
| vec_free (node_dups); |
| vec_free (stat_vms); |
| } |
| |
| static void |
| do_stat_segment_updates (vlib_main_t *vm, vlib_stats_segment_t *sm) |
| { |
| if (sm->node_counters_enabled) |
| update_node_counters (sm); |
| |
| vlib_stats_collector_t *c; |
| pool_foreach (c, sm->collectors) |
| { |
| vlib_stats_collector_data_t data = { |
| .entry_index = c->entry_index, |
| .vector_index = c->vector_index, |
| .private_data = c->private_data, |
| .entry = sm->directory_vector + c->entry_index, |
| }; |
| c->fn (&data); |
| } |
| |
| /* Heartbeat, so clients detect we're still here */ |
| sm->directory_vector[STAT_COUNTER_HEARTBEAT].value++; |
| } |
| |
| static uword |
| stat_segment_collector_process (vlib_main_t *vm, vlib_node_runtime_t *rt, |
| vlib_frame_t *f) |
| { |
| vlib_stats_segment_t *sm = vlib_stats_get_segment (); |
| |
| while (1) |
| { |
| do_stat_segment_updates (vm, sm); |
| vlib_process_suspend (vm, sm->update_interval); |
| } |
| return 0; /* or not */ |
| } |
| |
| VLIB_REGISTER_NODE (stat_segment_collector, static) = { |
| .function = stat_segment_collector_process, |
| .name = "statseg-collector-process", |
| .type = VLIB_NODE_TYPE_PROCESS, |
| }; |