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 vlib_main_t *vm = vlib_get_main ();
24 /* 3ms is WAY long enough to be reasonably sure something is wrong */
25 deadman = vlib_time_now (vm) + 3e-3;
27 while (__sync_lock_test_and_set (&((*sm->stat_segment_lockp)->lock), 1))
29 if (vlib_time_now (vm) >= deadman)
31 clib_warning ("BUG: stat segment lock held too long...");
38 vlib_stat_segment_unlock (void)
40 stats_main_t *sm = &stats_main;
41 clib_spinlock_unlock (sm->stat_segment_lockp);
45 vlib_stats_push_heap (void)
47 stats_main_t *sm = &stats_main;
48 ssvm_private_t *ssvmp = &sm->stat_segment;
49 ssvm_shared_header_t *shared_header;
51 ASSERT (ssvmp && ssvmp->sh);
53 shared_header = ssvmp->sh;
55 return ssvm_push_heap (shared_header);
59 vlib_stats_pop_heap (void *cm_arg, void *oldheap, stat_directory_type_t type)
61 vlib_simple_counter_main_t *cm = (vlib_simple_counter_main_t *) cm_arg;
62 stats_main_t *sm = &stats_main;
63 ssvm_private_t *ssvmp = &sm->stat_segment;
64 ssvm_shared_header_t *shared_header;
65 char *stat_segment_name;
66 stat_segment_directory_entry_t *ep;
69 ASSERT (ssvmp && ssvmp->sh);
71 shared_header = ssvmp->sh;
73 /* Not all counters have names / hash-table entries */
74 if (cm->name || cm->stat_segment_name)
79 stat_segment_name = cm->stat_segment_name ?
80 cm->stat_segment_name : cm->name;
82 vlib_stat_segment_lock ();
84 /* Update hash table. The name must be copied into the segment */
85 hp = hash_get_pair (sm->counter_vector_by_name, stat_segment_name);
88 name_copy = (u8 *) hp->key;
89 ep = (stat_segment_directory_entry_t *) (hp->value[0]);
90 hash_unset_mem (sm->counter_vector_by_name, stat_segment_name);
94 name_copy = format (0, "%s%c", stat_segment_name, 0);
95 ep = clib_mem_alloc (sizeof (*ep));
97 ep->value = cm->counters;
98 hash_set_mem (sm->counter_vector_by_name, name_copy, ep);
100 /* Reset the client hash table pointer, since it WILL change! */
101 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR]
102 = sm->counter_vector_by_name;
104 /* Warn clients to refresh any pointers they might be holding */
105 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
106 ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
107 vlib_stat_segment_unlock ();
109 ssvm_pop_heap (oldheap);
113 vlib_stats_register_error_index (u8 * name, u64 index)
115 stats_main_t *sm = &stats_main;
116 ssvm_private_t *ssvmp = &sm->stat_segment;
117 ssvm_shared_header_t *shared_header;
118 stat_segment_directory_entry_t *ep;
123 ASSERT (ssvmp && ssvmp->sh);
125 shared_header = ssvmp->sh;
127 vlib_stat_segment_lock ();
128 /* Update hash table. The name must be copied into the segment */
129 hp = hash_get_pair (sm->counter_vector_by_name, name);
132 name_copy = (u8 *) hp->key;
133 ep = (stat_segment_directory_entry_t *) (hp->value[0]);
134 hash_unset_mem (sm->counter_vector_by_name, name);
135 vec_free (name_copy);
139 ep = clib_mem_alloc (sizeof (*ep));
140 ep->type = STAT_DIR_TYPE_ERROR_INDEX;
141 ep->value = (void *) index;
143 hash_set_mem (sm->counter_vector_by_name, name, ep);
145 /* Reset the client hash table pointer, since it WILL change! */
146 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
148 /* Warn clients to refresh any pointers they might be holding */
149 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
150 ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
151 vlib_stat_segment_unlock ();
155 vlib_stats_pop_heap2 (u64 * counter_vector, u32 thread_index, void *oldheap)
157 stats_main_t *sm = &stats_main;
158 ssvm_private_t *ssvmp = &sm->stat_segment;
159 ssvm_shared_header_t *shared_header;
160 stat_segment_directory_entry_t *ep;
162 u8 *error_vector_name;
166 ASSERT (ssvmp && ssvmp->sh);
168 shared_header = ssvmp->sh;
170 vlib_stat_segment_lock ();
171 error_vector_name = format (0, "/err/%d/counter_vector%c", thread_index, 0);
173 /* Update hash table. The name must be copied into the segment */
174 hp = hash_get_pair (sm->counter_vector_by_name, error_vector_name);
177 name_copy = (u8 *) hp->key;
178 ep = (stat_segment_directory_entry_t *) (hp->value[0]);
179 hash_unset_mem (sm->counter_vector_by_name, error_vector_name);
180 vec_free (name_copy);
184 ep = clib_mem_alloc (sizeof (*ep));
185 ep->type = STAT_DIR_TYPE_VECTOR_POINTER;
186 ep->value = counter_vector;
188 hash_set_mem (sm->counter_vector_by_name, error_vector_name, ep);
190 /* Reset the client hash table pointer, since it WILL change! */
191 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
193 /* Warn clients to refresh any pointers they might be holding */
194 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
195 ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
196 vlib_stat_segment_unlock ();
197 ssvm_pop_heap (oldheap);
201 vlib_map_stat_segment_init (void)
203 stats_main_t *sm = &stats_main;
204 ssvm_private_t *ssvmp = &sm->stat_segment;
205 ssvm_shared_header_t *shared_header;
206 stat_segment_directory_entry_t *ep;
214 memory_size = sm->memory_size;
215 if (memory_size == 0)
216 memory_size = STAT_SEGMENT_DEFAULT_SIZE;
218 ssvmp->ssvm_size = memory_size;
219 ssvmp->i_am_master = 1;
220 ssvmp->my_pid = getpid ();
221 ssvmp->name = format (0, "/stats%c", 0);
222 ssvmp->requested_va = 0;
224 rv = ssvm_master_init (ssvmp, SSVM_SEGMENT_MEMFD);
227 return clib_error_return (0, "stat segment ssvm init failure");
228 shared_header = ssvmp->sh;
230 oldheap = ssvm_push_heap (shared_header);
232 /* Set up the name to counter-vector hash table */
233 sm->counter_vector_by_name = hash_create_string (0, sizeof (uword));
235 sm->stat_segment_lockp = clib_mem_alloc (sizeof (clib_spinlock_t));
237 /* Save the hash table address in the shared segment, for clients */
238 clib_spinlock_init (sm->stat_segment_lockp);
239 shared_header->opaque[STAT_SEGMENT_OPAQUE_LOCK] = sm->stat_segment_lockp;
240 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *) 1;
242 /* Set up a few scalar stats */
244 scalar_data = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
245 CLIB_CACHE_LINE_BYTES);
246 sm->vector_rate_ptr = (scalar_data + 0);
247 sm->input_rate_ptr = (scalar_data + 1);
248 sm->last_runtime_ptr = (scalar_data + 2);
249 sm->last_runtime_stats_clear_ptr = (scalar_data + 3);
250 sm->heartbeat_ptr = (scalar_data + 4);
252 name = format (0, "/sys/vector_rate%c", 0);
253 ep = clib_mem_alloc (sizeof (*ep));
254 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
255 ep->value = sm->vector_rate_ptr;
257 hash_set_mem (sm->counter_vector_by_name, name, ep);
259 name = format (0, "/sys/input_rate%c", 0);
260 ep = clib_mem_alloc (sizeof (*ep));
261 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
262 ep->value = sm->input_rate_ptr;
264 hash_set_mem (sm->counter_vector_by_name, name, ep);
266 name = format (0, "/sys/last_update%c", 0);
267 ep = clib_mem_alloc (sizeof (*ep));
268 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
269 ep->value = sm->last_runtime_ptr;
271 hash_set_mem (sm->counter_vector_by_name, name, ep);
273 name = format (0, "/sys/last_stats_clear%c", 0);
274 ep = clib_mem_alloc (sizeof (*ep));
275 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
276 ep->value = sm->last_runtime_stats_clear_ptr;
278 hash_set_mem (sm->counter_vector_by_name, name, ep);
280 name = format (0, "/sys/heartbeat%c", 0);
281 ep = clib_mem_alloc (sizeof (*ep));
282 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
283 ep->value = sm->heartbeat_ptr;
285 hash_set_mem (sm->counter_vector_by_name, name, ep);
288 /* Publish the hash table */
289 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
291 ssvm_pop_heap (oldheap);
299 stat_segment_directory_entry_t *dir_entry;
300 } show_stat_segment_t;
303 name_sort_cmp (void *a1, void *a2)
305 show_stat_segment_t *n1 = a1;
306 show_stat_segment_t *n2 = a2;
308 return strcmp ((char *) n1->name, (char *) n2->name);
312 format_stat_dir_entry (u8 * s, va_list * args)
314 stat_segment_directory_entry_t *ep =
315 va_arg (*args, stat_segment_directory_entry_t *);
319 format_string = "%-10s %20llx";
323 case STAT_DIR_TYPE_SCALAR_POINTER:
324 type_name = "ScalarPtr";
327 case STAT_DIR_TYPE_VECTOR_POINTER:
328 type_name = "VectorPtr";
331 case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
332 case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
333 type_name = "CMainPtr";
336 case STAT_DIR_TYPE_SERIALIZED_NODES:
337 type_name = "SerNodesPtr";
340 case STAT_DIR_TYPE_ERROR_INDEX:
341 type_name = "ErrIndex";
342 format_string = "%-10s %20lld";
346 type_name = "illegal!";
350 return format (s, format_string, type_name, ep->value);
353 static clib_error_t *
354 show_stat_segment_command_fn (vlib_main_t * vm,
355 unformat_input_t * input,
356 vlib_cli_command_t * cmd)
358 stats_main_t *sm = &stats_main;
359 ssvm_private_t *ssvmp = &sm->stat_segment;
360 ssvm_shared_header_t *shared_header;
363 show_stat_segment_t *show_data = 0;
364 show_stat_segment_t *this;
370 if (unformat (input, "verbose"))
373 vlib_stat_segment_lock ();
376 hash_foreach_pair (p, sm->counter_vector_by_name,
378 vec_add2 (show_data, this, 1);
380 this->name = (u8 *) (p->key);
381 this->dir_entry = (stat_segment_directory_entry_t *)(p->value[0]);
385 vlib_stat_segment_unlock ();
387 vec_sort_with_function (show_data, name_sort_cmp);
389 vlib_cli_output (vm, "%-60s %10s %20s", "Name", "Type", "Value");
391 for (i = 0; i < vec_len (show_data); i++)
393 this = vec_elt_at_index (show_data, i);
395 vlib_cli_output (vm, "%-60s %31U",
396 this->name, format_stat_dir_entry, this->dir_entry);
401 ASSERT (ssvmp && ssvmp->sh);
403 shared_header = ssvmp->sh;
405 vlib_cli_output (vm, "%U", format_mheap,
406 shared_header->heap, 0 /* verbose */ );
413 VLIB_CLI_COMMAND (show_stat_segment_command, static) =
415 .path = "show statistics segment",
416 .short_help = "show statistics segment [verbose]",
417 .function = show_stat_segment_command_fn,
422 update_serialized_nodes (stats_main_t * sm)
425 vlib_main_t *vm = vlib_mains[0];
426 ssvm_private_t *ssvmp = &sm->stat_segment;
427 ssvm_shared_header_t *shared_header;
429 stat_segment_directory_entry_t *ep;
433 ASSERT (ssvmp && ssvmp->sh);
435 vec_reset_length (sm->serialized_nodes);
437 shared_header = ssvmp->sh;
439 oldheap = ssvm_push_heap (shared_header);
441 vlib_stat_segment_lock ();
442 vlib_node_get_nodes (0 /* vm, for barrier sync */ ,
443 (u32) ~ 0 /* all threads */ ,
444 1 /* include stats */ ,
445 0 /* barrier sync */ ,
446 &sm->node_dups, &sm->stat_vms);
448 sm->serialized_nodes = vlib_node_serialize (vm, sm->node_dups,
449 sm->serialized_nodes,
450 0 /* include nexts */ ,
451 1 /* include stats */ );
453 hp = hash_get_pair (sm->counter_vector_by_name, "serialized_nodes");
456 name_copy = (u8 *) hp->key;
457 ep = (stat_segment_directory_entry_t *) (hp->value[0]);
459 if (ep->value != sm->serialized_nodes)
461 ep->value = sm->serialized_nodes;
462 /* Warn clients to refresh any pointers they might be holding */
463 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
464 ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
469 name_copy = format (0, "%s%c", "serialized_nodes", 0);
470 ep = clib_mem_alloc (sizeof (*ep));
471 ep->type = STAT_DIR_TYPE_SERIALIZED_NODES;
472 ep->value = sm->serialized_nodes;
473 hash_set_mem (sm->counter_vector_by_name, name_copy, ep);
475 /* Reset the client hash table pointer */
476 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR]
477 = sm->counter_vector_by_name;
479 /* Warn clients to refresh any pointers they might be holding */
480 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
481 ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
484 vlib_stat_segment_unlock ();
485 ssvm_pop_heap (oldheap);
489 * Called by stats_thread_fn, in stats.c, which runs in a
490 * separate pthread, which won't halt the parade
491 * in single-forwarding-core cases.
495 do_stat_segment_updates (stats_main_t * sm)
497 vlib_main_t *vm = vlib_mains[0];
499 u64 input_packets, last_input_packets;
501 vlib_main_t *this_vlib_main;
505 * Compute the average vector rate across all workers
509 start = vec_len (vlib_mains) > 1 ? 1 : 0;
511 for (i = start; i < vec_len (vlib_mains); i++)
513 this_vlib_main = vlib_mains[i];
514 vector_rate += vlib_last_vector_length_per_node (this_vlib_main);
516 vector_rate /= (f64) (i - start);
518 *sm->vector_rate_ptr = vector_rate / ((f64) (vec_len (vlib_mains) - start));
521 * Compute the aggregate input rate
523 now = vlib_time_now (vm);
524 dt = now - sm->last_runtime_ptr[0];
525 input_packets = vnet_get_aggregate_rx_packets ();
526 *sm->input_rate_ptr = (f64) (input_packets - sm->last_input_packets) / dt;
527 sm->last_runtime_ptr[0] = now;
528 sm->last_input_packets = input_packets;
529 sm->last_runtime_stats_clear_ptr[0] =
530 vm->node_main.time_last_runtime_stats_clear;
532 if (sm->serialize_nodes)
533 update_serialized_nodes (sm);
535 /* Heartbeat, so clients detect we're still here */
536 (*sm->heartbeat_ptr)++;
539 static clib_error_t *
540 statseg_config (vlib_main_t * vm, unformat_input_t * input)
542 stats_main_t *sm = &stats_main;
545 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
547 if (unformat (input, "size %U", unformat_memory_size, &sm->memory_size))
549 else if (unformat (input, "serialize-nodes on"))
550 sm->serialize_nodes = 1;
551 else if (unformat (input, "serialize-nodes off"))
552 sm->serialize_nodes = 0;
554 return clib_error_return (0, "unknown input `%U'",
555 format_unformat_error, input);
561 VLIB_EARLY_CONFIG_FUNCTION (statseg_config, "statseg");
564 * fd.io coding-style-patch-verification: ON
567 * eval: (c-set-style "gnu")