2 * Copyright (c) 2018 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
15 #include <vpp/stats/stats.h>
18 vlib_stat_segment_lock (void)
20 stats_main_t *sm = &stats_main;
21 clib_spinlock_lock (sm->stat_segment_lockp);
25 vlib_stat_segment_unlock (void)
27 stats_main_t *sm = &stats_main;
28 clib_spinlock_unlock (sm->stat_segment_lockp);
32 vlib_stats_push_heap (void)
34 stats_main_t *sm = &stats_main;
35 ssvm_private_t *ssvmp = &sm->stat_segment;
36 ssvm_shared_header_t *shared_header;
38 ASSERT (ssvmp && ssvmp->sh);
40 shared_header = ssvmp->sh;
42 return ssvm_push_heap (shared_header);
46 vlib_stats_pop_heap (void *cm_arg, void *oldheap, stat_directory_type_t type)
48 vlib_simple_counter_main_t *cm = (vlib_simple_counter_main_t *) cm_arg;
49 stats_main_t *sm = &stats_main;
50 ssvm_private_t *ssvmp = &sm->stat_segment;
51 ssvm_shared_header_t *shared_header;
52 char *stat_segment_name;
53 stat_segment_directory_entry_t *ep;
56 ASSERT (ssvmp && ssvmp->sh);
58 shared_header = ssvmp->sh;
60 /* Not all counters have names / hash-table entries */
61 if (cm->name || cm->stat_segment_name)
66 stat_segment_name = cm->stat_segment_name ?
67 cm->stat_segment_name : cm->name;
69 clib_spinlock_lock (sm->stat_segment_lockp);
71 /* Update hash table. The name must be copied into the segment */
72 hp = hash_get_pair (sm->counter_vector_by_name, stat_segment_name);
75 name_copy = (u8 *) hp->key;
76 ep = (stat_segment_directory_entry_t *) (hp->value[0]);
77 hash_unset_mem (sm->counter_vector_by_name, stat_segment_name);
81 name_copy = format (0, "%s%c", stat_segment_name, 0);
82 ep = clib_mem_alloc (sizeof (*ep));
84 ep->value = cm->counters;
85 hash_set_mem (sm->counter_vector_by_name, name_copy, ep);
87 /* Reset the client hash table pointer, since it WILL change! */
88 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR]
89 = sm->counter_vector_by_name;
91 /* Warn clients to refresh any pointers they might be holding */
92 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
93 ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
94 clib_spinlock_unlock (sm->stat_segment_lockp);
96 ssvm_pop_heap (oldheap);
100 vlib_stats_register_error_index (u8 * name, u64 index)
102 stats_main_t *sm = &stats_main;
103 ssvm_private_t *ssvmp = &sm->stat_segment;
104 ssvm_shared_header_t *shared_header;
105 stat_segment_directory_entry_t *ep;
110 ASSERT (ssvmp && ssvmp->sh);
112 shared_header = ssvmp->sh;
114 clib_spinlock_lock (sm->stat_segment_lockp);
116 /* Update hash table. The name must be copied into the segment */
117 hp = hash_get_pair (sm->counter_vector_by_name, name);
120 name_copy = (u8 *) hp->key;
121 ep = (stat_segment_directory_entry_t *) (hp->value[0]);
122 hash_unset_mem (sm->counter_vector_by_name, name);
123 vec_free (name_copy);
127 ep = clib_mem_alloc (sizeof (*ep));
128 ep->type = STAT_DIR_TYPE_ERROR_INDEX;
129 ep->value = (void *) index;
131 hash_set_mem (sm->counter_vector_by_name, name, ep);
133 /* Reset the client hash table pointer, since it WILL change! */
134 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
136 /* Warn clients to refresh any pointers they might be holding */
137 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
138 ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
139 clib_spinlock_unlock (sm->stat_segment_lockp);
143 vlib_stats_pop_heap2 (u64 * counter_vector, u32 thread_index, void *oldheap)
145 stats_main_t *sm = &stats_main;
146 ssvm_private_t *ssvmp = &sm->stat_segment;
147 ssvm_shared_header_t *shared_header;
148 stat_segment_directory_entry_t *ep;
150 u8 *error_vector_name;
154 ASSERT (ssvmp && ssvmp->sh);
156 shared_header = ssvmp->sh;
158 clib_spinlock_lock (sm->stat_segment_lockp);
160 error_vector_name = format (0, "/err/%d/counter_vector%c", thread_index, 0);
162 /* Update hash table. The name must be copied into the segment */
163 hp = hash_get_pair (sm->counter_vector_by_name, error_vector_name);
166 name_copy = (u8 *) hp->key;
167 ep = (stat_segment_directory_entry_t *) (hp->value[0]);
168 hash_unset_mem (sm->counter_vector_by_name, error_vector_name);
169 vec_free (name_copy);
173 ep = clib_mem_alloc (sizeof (*ep));
174 ep->type = STAT_DIR_TYPE_VECTOR_POINTER;
175 ep->value = counter_vector;
177 hash_set_mem (sm->counter_vector_by_name, error_vector_name, ep);
179 /* Reset the client hash table pointer, since it WILL change! */
180 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
182 /* Warn clients to refresh any pointers they might be holding */
183 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
184 ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
185 clib_spinlock_unlock (sm->stat_segment_lockp);
186 ssvm_pop_heap (oldheap);
190 vlib_map_stat_segment_init (void)
192 stats_main_t *sm = &stats_main;
193 ssvm_private_t *ssvmp = &sm->stat_segment;
194 ssvm_shared_header_t *shared_header;
195 stat_segment_directory_entry_t *ep;
203 memory_size = sm->memory_size;
204 if (memory_size == 0)
205 memory_size = STAT_SEGMENT_DEFAULT_SIZE;
207 ssvmp->ssvm_size = memory_size;
208 ssvmp->i_am_master = 1;
209 ssvmp->my_pid = getpid ();
210 ssvmp->name = format (0, "/stats%c", 0);
211 ssvmp->requested_va = 0;
213 rv = ssvm_master_init (ssvmp, SSVM_SEGMENT_MEMFD);
216 return clib_error_return (0, "stat segment ssvm init failure");
217 shared_header = ssvmp->sh;
219 oldheap = ssvm_push_heap (shared_header);
221 /* Set up the name to counter-vector hash table */
222 sm->counter_vector_by_name = hash_create_string (0, sizeof (uword));
224 sm->stat_segment_lockp = clib_mem_alloc (sizeof (clib_spinlock_t));
226 /* Save the hash table address in the shared segment, for clients */
227 clib_spinlock_init (sm->stat_segment_lockp);
228 shared_header->opaque[STAT_SEGMENT_OPAQUE_LOCK] = sm->stat_segment_lockp;
229 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *) 1;
231 /* Set up a few scalar stats */
233 scalar_data = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
234 CLIB_CACHE_LINE_BYTES);
235 sm->vector_rate_ptr = (scalar_data + 0);
236 sm->input_rate_ptr = (scalar_data + 1);
237 sm->last_runtime_ptr = (scalar_data + 2);
238 sm->last_runtime_stats_clear_ptr = (scalar_data + 3);
239 sm->heartbeat_ptr = (scalar_data + 4);
241 name = format (0, "/sys/vector_rate%c", 0);
242 ep = clib_mem_alloc (sizeof (*ep));
243 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
244 ep->value = sm->vector_rate_ptr;
246 hash_set_mem (sm->counter_vector_by_name, name, ep);
248 name = format (0, "/sys/input_rate%c", 0);
249 ep = clib_mem_alloc (sizeof (*ep));
250 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
251 ep->value = sm->input_rate_ptr;
253 hash_set_mem (sm->counter_vector_by_name, name, ep);
255 name = format (0, "/sys/last_update%c", 0);
256 ep = clib_mem_alloc (sizeof (*ep));
257 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
258 ep->value = sm->last_runtime_ptr;
260 hash_set_mem (sm->counter_vector_by_name, name, ep);
262 name = format (0, "/sys/last_stats_clear%c", 0);
263 ep = clib_mem_alloc (sizeof (*ep));
264 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
265 ep->value = sm->last_runtime_stats_clear_ptr;
267 hash_set_mem (sm->counter_vector_by_name, name, ep);
269 name = format (0, "/sys/heartbeat%c", 0);
270 ep = clib_mem_alloc (sizeof (*ep));
271 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
272 ep->value = sm->heartbeat_ptr;
274 hash_set_mem (sm->counter_vector_by_name, name, ep);
277 /* Publish the hash table */
278 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
280 ssvm_pop_heap (oldheap);
288 stat_segment_directory_entry_t *dir_entry;
289 } show_stat_segment_t;
292 name_sort_cmp (void *a1, void *a2)
294 show_stat_segment_t *n1 = a1;
295 show_stat_segment_t *n2 = a2;
297 return strcmp ((char *) n1->name, (char *) n2->name);
301 format_stat_dir_entry (u8 * s, va_list * args)
303 stat_segment_directory_entry_t *ep =
304 va_arg (*args, stat_segment_directory_entry_t *);
308 format_string = "%-10s %20llx";
312 case STAT_DIR_TYPE_SCALAR_POINTER:
313 type_name = "ScalarPtr";
316 case STAT_DIR_TYPE_VECTOR_POINTER:
317 type_name = "VectorPtr";
320 case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
321 case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
322 type_name = "CMainPtr";
325 case STAT_DIR_TYPE_SERIALIZED_NODES:
326 type_name = "SerNodesPtr";
329 case STAT_DIR_TYPE_ERROR_INDEX:
330 type_name = "ErrIndex";
331 format_string = "%-10s %20lld";
335 type_name = "illegal!";
339 return format (s, format_string, type_name, ep->value);
342 static clib_error_t *
343 show_stat_segment_command_fn (vlib_main_t * vm,
344 unformat_input_t * input,
345 vlib_cli_command_t * cmd)
347 stats_main_t *sm = &stats_main;
348 ssvm_private_t *ssvmp = &sm->stat_segment;
349 ssvm_shared_header_t *shared_header;
352 show_stat_segment_t *show_data = 0;
353 show_stat_segment_t *this;
359 if (unformat (input, "verbose"))
362 clib_spinlock_lock (sm->stat_segment_lockp);
365 hash_foreach_pair (p, sm->counter_vector_by_name,
367 vec_add2 (show_data, this, 1);
369 this->name = (u8 *) (p->key);
370 this->dir_entry = (stat_segment_directory_entry_t *)(p->value[0]);
374 clib_spinlock_unlock (sm->stat_segment_lockp);
376 vec_sort_with_function (show_data, name_sort_cmp);
378 vlib_cli_output (vm, "%-60s %10s %20s", "Name", "Type", "Value");
380 for (i = 0; i < vec_len (show_data); i++)
382 this = vec_elt_at_index (show_data, i);
384 vlib_cli_output (vm, "%-60s %31U",
385 this->name, format_stat_dir_entry, this->dir_entry);
390 ASSERT (ssvmp && ssvmp->sh);
392 shared_header = ssvmp->sh;
394 vlib_cli_output (vm, "%U", format_mheap,
395 shared_header->heap, 0 /* verbose */ );
402 VLIB_CLI_COMMAND (show_stat_segment_command, static) =
404 .path = "show statistics segment",
405 .short_help = "show statistics segment [verbose]",
406 .function = show_stat_segment_command_fn,
411 update_serialized_nodes (stats_main_t * sm)
414 vlib_main_t *vm = vlib_mains[0];
415 ssvm_private_t *ssvmp = &sm->stat_segment;
416 ssvm_shared_header_t *shared_header;
418 stat_segment_directory_entry_t *ep;
422 ASSERT (ssvmp && ssvmp->sh);
424 vec_reset_length (sm->serialized_nodes);
426 shared_header = ssvmp->sh;
428 oldheap = ssvm_push_heap (shared_header);
430 clib_spinlock_lock (sm->stat_segment_lockp);
432 vlib_node_get_nodes (0 /* vm, for barrier sync */ ,
433 (u32) ~ 0 /* all threads */ ,
434 1 /* include stats */ ,
435 0 /* barrier sync */ ,
436 &sm->node_dups, &sm->stat_vms);
438 sm->serialized_nodes = vlib_node_serialize (vm, sm->node_dups,
439 sm->serialized_nodes,
440 0 /* include nexts */ ,
441 1 /* include stats */ );
443 hp = hash_get_pair (sm->counter_vector_by_name, "serialized_nodes");
446 name_copy = (u8 *) hp->key;
447 ep = (stat_segment_directory_entry_t *) (hp->value[0]);
449 if (ep->value != sm->serialized_nodes)
451 ep->value = sm->serialized_nodes;
452 /* Warn clients to refresh any pointers they might be holding */
453 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
454 ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
459 name_copy = format (0, "%s%c", "serialized_nodes", 0);
460 ep = clib_mem_alloc (sizeof (*ep));
461 ep->type = STAT_DIR_TYPE_SERIALIZED_NODES;
462 ep->value = sm->serialized_nodes;
463 hash_set_mem (sm->counter_vector_by_name, name_copy, ep);
465 /* Reset the client hash table pointer */
466 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR]
467 = sm->counter_vector_by_name;
469 /* Warn clients to refresh any pointers they might be holding */
470 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
471 ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
474 clib_spinlock_unlock (sm->stat_segment_lockp);
475 ssvm_pop_heap (oldheap);
479 * Called by stats_thread_fn, in stats.c, which runs in a
480 * separate pthread, which won't halt the parade
481 * in single-forwarding-core cases.
485 do_stat_segment_updates (stats_main_t * sm)
487 vlib_main_t *vm = vlib_mains[0];
489 u64 input_packets, last_input_packets;
491 vlib_main_t *this_vlib_main;
495 * Compute the average vector rate across all workers
499 start = vec_len (vlib_mains) > 1 ? 1 : 0;
501 for (i = start; i < vec_len (vlib_mains); i++)
503 this_vlib_main = vlib_mains[i];
504 vector_rate += vlib_last_vector_length_per_node (this_vlib_main);
506 vector_rate /= (f64) (i - start);
508 *sm->vector_rate_ptr = vector_rate / ((f64) (vec_len (vlib_mains) - start));
511 * Compute the aggregate input rate
513 now = vlib_time_now (vm);
514 dt = now - sm->last_runtime_ptr[0];
515 input_packets = vnet_get_aggregate_rx_packets ();
516 *sm->input_rate_ptr = (f64) (input_packets - sm->last_input_packets) / dt;
517 sm->last_runtime_ptr[0] = now;
518 sm->last_input_packets = input_packets;
519 sm->last_runtime_stats_clear_ptr[0] =
520 vm->node_main.time_last_runtime_stats_clear;
522 if (sm->serialize_nodes)
523 update_serialized_nodes (sm);
525 /* Heartbeat, so clients detect we're still here */
526 (*sm->heartbeat_ptr)++;
529 static clib_error_t *
530 statseg_config (vlib_main_t * vm, unformat_input_t * input)
532 stats_main_t *sm = &stats_main;
535 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
537 if (unformat (input, "size %U", unformat_memory_size, &sm->memory_size))
539 else if (unformat (input, "serialize-nodes on"))
540 sm->serialize_nodes = 1;
541 else if (unformat (input, "serialize-nodes off"))
542 sm->serialize_nodes = 0;
544 return clib_error_return (0, "unknown input `%U'",
545 format_unformat_error, input);
551 VLIB_EARLY_CONFIG_FUNCTION (statseg_config, "statseg");
554 * fd.io coding-style-patch-verification: ON
557 * eval: (c-set-style "gnu")