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)
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));
83 ep->type = STAT_DIR_TYPE_COUNTER_VECTOR;
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);
240 name = format (0, "/sys/vector_rate%c", 0);
241 ep = clib_mem_alloc (sizeof (*ep));
242 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
243 ep->value = sm->vector_rate_ptr;
245 hash_set_mem (sm->counter_vector_by_name, name, ep);
247 name = format (0, "/sys/input_rate%c", 0);
248 ep = clib_mem_alloc (sizeof (*ep));
249 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
250 ep->value = sm->input_rate_ptr;
252 hash_set_mem (sm->counter_vector_by_name, name, ep);
254 name = format (0, "/sys/last_update%c", 0);
255 ep = clib_mem_alloc (sizeof (*ep));
256 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
257 ep->value = sm->last_runtime_ptr;
259 hash_set_mem (sm->counter_vector_by_name, name, ep);
261 name = format (0, "/sys/last_stats_clear%c", 0);
262 ep = clib_mem_alloc (sizeof (*ep));
263 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
264 ep->value = sm->last_runtime_stats_clear_ptr;
266 hash_set_mem (sm->counter_vector_by_name, name, ep);
269 /* Publish the hash table */
270 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
272 ssvm_pop_heap (oldheap);
280 stat_segment_directory_entry_t *dir_entry;
281 } show_stat_segment_t;
284 name_sort_cmp (void *a1, void *a2)
286 show_stat_segment_t *n1 = a1;
287 show_stat_segment_t *n2 = a2;
289 return strcmp ((char *) n1->name, (char *) n2->name);
293 format_stat_dir_entry (u8 * s, va_list * args)
295 stat_segment_directory_entry_t *ep =
296 va_arg (*args, stat_segment_directory_entry_t *);
300 format_string = "%-10s %20llx";
304 case STAT_DIR_TYPE_SCALAR_POINTER:
305 type_name = "ScalarPtr";
308 case STAT_DIR_TYPE_VECTOR_POINTER:
309 type_name = "VectorPtr";
312 case STAT_DIR_TYPE_COUNTER_VECTOR:
313 type_name = "CMainPtr";
316 case STAT_DIR_TYPE_SERIALIZED_NODES:
317 type_name = "SerNodesPtr";
320 case STAT_DIR_TYPE_ERROR_INDEX:
321 type_name = "ErrIndex";
322 format_string = "%-10s %20lld";
326 type_name = "illegal!";
330 return format (s, format_string, type_name, ep->value);
333 static clib_error_t *
334 show_stat_segment_command_fn (vlib_main_t * vm,
335 unformat_input_t * input,
336 vlib_cli_command_t * cmd)
338 stats_main_t *sm = &stats_main;
339 ssvm_private_t *ssvmp = &sm->stat_segment;
340 ssvm_shared_header_t *shared_header;
343 show_stat_segment_t *show_data = 0;
344 show_stat_segment_t *this;
350 if (unformat (input, "verbose"))
353 clib_spinlock_lock (sm->stat_segment_lockp);
356 hash_foreach_pair (p, sm->counter_vector_by_name,
358 vec_add2 (show_data, this, 1);
360 this->name = (u8 *) (p->key);
361 this->dir_entry = (stat_segment_directory_entry_t *)(p->value[0]);
365 clib_spinlock_unlock (sm->stat_segment_lockp);
367 vec_sort_with_function (show_data, name_sort_cmp);
369 vlib_cli_output (vm, "%-60s %10s %20s", "Name", "Type", "Value");
371 for (i = 0; i < vec_len (show_data); i++)
373 this = vec_elt_at_index (show_data, i);
375 vlib_cli_output (vm, "%-60s %31U",
376 this->name, format_stat_dir_entry, this->dir_entry);
381 ASSERT (ssvmp && ssvmp->sh);
383 shared_header = ssvmp->sh;
385 vlib_cli_output (vm, "%U", format_mheap,
386 shared_header->heap, 0 /* verbose */ );
393 VLIB_CLI_COMMAND (show_stat_segment_command, static) =
395 .path = "show statistics segment",
396 .short_help = "show statistics segment [verbose]",
397 .function = show_stat_segment_command_fn,
402 update_serialized_nodes (stats_main_t * sm)
405 vlib_main_t *vm = vlib_mains[0];
406 ssvm_private_t *ssvmp = &sm->stat_segment;
407 ssvm_shared_header_t *shared_header;
409 stat_segment_directory_entry_t *ep;
413 ASSERT (ssvmp && ssvmp->sh);
415 vec_reset_length (sm->serialized_nodes);
417 shared_header = ssvmp->sh;
419 oldheap = ssvm_push_heap (shared_header);
421 clib_spinlock_lock (sm->stat_segment_lockp);
423 vlib_node_get_nodes (0 /* vm, for barrier sync */ ,
424 (u32) ~ 0 /* all threads */ ,
425 1 /* include stats */ ,
426 0 /* barrier sync */ ,
427 &sm->node_dups, &sm->stat_vms);
429 sm->serialized_nodes = vlib_node_serialize (vm, sm->node_dups,
430 sm->serialized_nodes,
431 0 /* include nexts */ ,
432 1 /* include stats */ );
434 hp = hash_get_pair (sm->counter_vector_by_name, "serialized_nodes");
437 name_copy = (u8 *) hp->key;
438 ep = (stat_segment_directory_entry_t *) (hp->value[0]);
440 if (ep->value != sm->serialized_nodes)
442 ep->value = sm->serialized_nodes;
443 /* Warn clients to refresh any pointers they might be holding */
444 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
445 ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
450 name_copy = format (0, "%s%c", "serialized_nodes", 0);
451 ep = clib_mem_alloc (sizeof (*ep));
452 ep->type = STAT_DIR_TYPE_SERIALIZED_NODES;
453 ep->value = sm->serialized_nodes;
454 hash_set_mem (sm->counter_vector_by_name, name_copy, ep);
456 /* Reset the client hash table pointer */
457 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR]
458 = sm->counter_vector_by_name;
460 /* Warn clients to refresh any pointers they might be holding */
461 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
462 ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
465 clib_spinlock_unlock (sm->stat_segment_lockp);
466 ssvm_pop_heap (oldheap);
470 * Called by stats_thread_fn, in stats.c, which runs in a
471 * separate pthread, which won't halt the parade
472 * in single-forwarding-core cases.
476 do_stat_segment_updates (stats_main_t * sm)
478 vlib_main_t *vm = vlib_mains[0];
480 u64 input_packets, last_input_packets;
482 vlib_main_t *this_vlib_main;
486 * Compute the average vector rate across all workers
490 start = vec_len (vlib_mains) > 1 ? 1 : 0;
492 for (i = start; i < vec_len (vlib_mains); i++)
494 this_vlib_main = vlib_mains[i];
495 vector_rate += vlib_last_vector_length_per_node (this_vlib_main);
497 vector_rate /= (f64) (i - start);
499 *sm->vector_rate_ptr = vector_rate / ((f64) (vec_len (vlib_mains) - start));
502 * Compute the aggregate input rate
504 now = vlib_time_now (vm);
505 dt = now - sm->last_runtime_ptr[0];
506 input_packets = vnet_get_aggregate_rx_packets ();
507 *sm->input_rate_ptr = (f64) (input_packets - sm->last_input_packets) / dt;
508 sm->last_runtime_ptr[0] = now;
509 sm->last_input_packets = input_packets;
510 sm->last_runtime_stats_clear_ptr[0] =
511 vm->node_main.time_last_runtime_stats_clear;
513 if (sm->serialize_nodes)
514 update_serialized_nodes (sm);
517 static clib_error_t *
518 statseg_config (vlib_main_t * vm, unformat_input_t * input)
520 stats_main_t *sm = &stats_main;
523 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
525 if (unformat (input, "size %U", unformat_memory_size, &sm->memory_size))
527 else if (unformat (input, "serialize-nodes on"))
528 sm->serialize_nodes = 1;
529 else if (unformat (input, "serialize-nodes off"))
530 sm->serialize_nodes = 0;
532 return clib_error_return (0, "unknown input `%U'",
533 format_unformat_error, input);
539 VLIB_EARLY_CONFIG_FUNCTION (statseg_config, "statseg");
542 * fd.io coding-style-patch-verification: ON
545 * eval: (c-set-style "gnu")