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);
189 static clib_error_t *
190 map_stat_segment_init (vlib_main_t * vm)
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;
202 ssvmp->ssvm_size = 32 << 20; /*$$$$$ CONFIG PARAM */
203 ssvmp->i_am_master = 1;
204 ssvmp->my_pid = getpid ();
205 ssvmp->name = format (0, "/stats%c", 0);
206 ssvmp->requested_va = 0;
208 rv = ssvm_master_init (ssvmp, SSVM_SEGMENT_MEMFD);
211 return clib_error_return (0, "stat segment ssvm init failure");
212 shared_header = ssvmp->sh;
214 oldheap = ssvm_push_heap (shared_header);
216 /* Set up the name to counter-vector hash table */
217 sm->counter_vector_by_name = hash_create_string (0, sizeof (uword));
219 sm->stat_segment_lockp = clib_mem_alloc (sizeof (clib_spinlock_t));
221 /* Save the hash table address in the shared segment, for clients */
222 clib_spinlock_init (sm->stat_segment_lockp);
223 shared_header->opaque[STAT_SEGMENT_OPAQUE_LOCK] = sm->stat_segment_lockp;
224 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *) 1;
226 /* Set up a few scalar stats */
228 scalar_data = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
229 CLIB_CACHE_LINE_BYTES);
230 sm->vector_rate_ptr = (scalar_data + 0);
231 sm->input_rate_ptr = (scalar_data + 1);
232 sm->last_runtime_ptr = (scalar_data + 2);
233 sm->last_runtime_stats_clear_ptr = (scalar_data + 3);
235 name = format (0, "vector_rate%c", 0);
236 ep = clib_mem_alloc (sizeof (*ep));
237 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
238 ep->value = sm->vector_rate_ptr;
240 hash_set_mem (sm->counter_vector_by_name, name, ep);
242 name = format (0, "input_rate%c", 0);
243 ep = clib_mem_alloc (sizeof (*ep));
244 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
245 ep->value = sm->input_rate_ptr;
247 hash_set_mem (sm->counter_vector_by_name, name, ep);
249 name = format (0, "last_update%c", 0);
250 ep = clib_mem_alloc (sizeof (*ep));
251 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
252 ep->value = sm->last_runtime_ptr;
254 hash_set_mem (sm->counter_vector_by_name, name, ep);
256 name = format (0, "last_stats_clear%c", 0);
257 ep = clib_mem_alloc (sizeof (*ep));
258 ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
259 ep->value = sm->last_runtime_stats_clear_ptr;
261 hash_set_mem (sm->counter_vector_by_name, name, ep);
264 /* Publish the hash table */
265 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
267 ssvm_pop_heap (oldheap);
272 VLIB_INIT_FUNCTION (map_stat_segment_init);
277 stat_segment_directory_entry_t *dir_entry;
278 } show_stat_segment_t;
281 name_sort_cmp (void *a1, void *a2)
283 show_stat_segment_t *n1 = a1;
284 show_stat_segment_t *n2 = a2;
286 return strcmp ((char *) n1->name, (char *) n2->name);
290 format_stat_dir_entry (u8 * s, va_list * args)
292 stat_segment_directory_entry_t *ep =
293 va_arg (*args, stat_segment_directory_entry_t *);
297 format_string = "%-10s %20llx";
301 case STAT_DIR_TYPE_SCALAR_POINTER:
302 type_name = "ScalarPtr";
305 case STAT_DIR_TYPE_VECTOR_POINTER:
306 type_name = "VectorPtr";
309 case STAT_DIR_TYPE_COUNTER_VECTOR:
310 type_name = "CMainPtr";
313 case STAT_DIR_TYPE_SERIALIZED_NODES:
314 type_name = "SerNodesPtr";
317 case STAT_DIR_TYPE_ERROR_INDEX:
318 type_name = "ErrIndex";
319 format_string = "%-10s %20lld";
323 type_name = "illegal!";
327 return format (s, format_string, type_name, ep->value);
330 static clib_error_t *
331 show_stat_segment_command_fn (vlib_main_t * vm,
332 unformat_input_t * input,
333 vlib_cli_command_t * cmd)
335 stats_main_t *sm = &stats_main;
336 ssvm_private_t *ssvmp = &sm->stat_segment;
337 ssvm_shared_header_t *shared_header;
340 show_stat_segment_t *show_data = 0;
341 show_stat_segment_t *this;
347 if (unformat (input, "verbose"))
350 clib_spinlock_lock (sm->stat_segment_lockp);
353 hash_foreach_pair (p, sm->counter_vector_by_name,
355 vec_add2 (show_data, this, 1);
357 this->name = (u8 *) (p->key);
358 this->dir_entry = (stat_segment_directory_entry_t *)(p->value[0]);
362 clib_spinlock_unlock (sm->stat_segment_lockp);
364 vec_sort_with_function (show_data, name_sort_cmp);
366 vlib_cli_output (vm, "%-60s %10s %20s", "Name", "Type", "Value");
368 for (i = 0; i < vec_len (show_data); i++)
370 this = vec_elt_at_index (show_data, i);
372 vlib_cli_output (vm, "%-60s %31U",
373 this->name, format_stat_dir_entry, this->dir_entry);
378 ASSERT (ssvmp && ssvmp->sh);
380 shared_header = ssvmp->sh;
382 vlib_cli_output (vm, "%U", format_mheap,
383 shared_header->heap, 0 /* verbose */ );
390 VLIB_CLI_COMMAND (show_stat_segment_command, static) =
392 .path = "show statistics segment",
393 .short_help = "show statistics segment [verbose]",
394 .function = show_stat_segment_command_fn,
399 update_serialized_nodes (stats_main_t * sm)
402 vlib_main_t *vm = vlib_mains[0];
403 ssvm_private_t *ssvmp = &sm->stat_segment;
404 ssvm_shared_header_t *shared_header;
406 stat_segment_directory_entry_t *ep;
410 ASSERT (ssvmp && ssvmp->sh);
412 vec_reset_length (sm->serialized_nodes);
414 shared_header = ssvmp->sh;
416 oldheap = ssvm_push_heap (shared_header);
418 clib_spinlock_lock (sm->stat_segment_lockp);
420 vlib_node_get_nodes (0 /* vm, for barrier sync */ ,
421 (u32) ~ 0 /* all threads */ ,
422 1 /* include stats */ ,
423 0 /* barrier sync */ ,
424 &sm->node_dups, &sm->stat_vms);
426 sm->serialized_nodes = vlib_node_serialize (vm, sm->node_dups,
427 sm->serialized_nodes,
428 0 /* include nexts */ ,
429 1 /* include stats */ );
431 hp = hash_get_pair (sm->counter_vector_by_name, "serialized_nodes");
434 name_copy = (u8 *) hp->key;
435 ep = (stat_segment_directory_entry_t *) (hp->value[0]);
437 if (ep->value != sm->serialized_nodes)
439 ep->value = sm->serialized_nodes;
440 /* Warn clients to refresh any pointers they might be holding */
441 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
442 ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
447 name_copy = format (0, "%s%c", "serialized_nodes", 0);
448 ep = clib_mem_alloc (sizeof (*ep));
449 ep->type = STAT_DIR_TYPE_SERIALIZED_NODES;
450 ep->value = sm->serialized_nodes;
451 hash_set_mem (sm->counter_vector_by_name, name_copy, ep);
453 /* Reset the client hash table pointer */
454 shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR]
455 = sm->counter_vector_by_name;
457 /* Warn clients to refresh any pointers they might be holding */
458 shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
459 ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
462 clib_spinlock_unlock (sm->stat_segment_lockp);
463 ssvm_pop_heap (oldheap);
467 * Called by stats_thread_fn, in stats.c, which runs in a
468 * separate pthread, which won't halt the parade
469 * in single-forwarding-core cases.
473 do_stat_segment_updates (stats_main_t * sm)
475 vlib_main_t *vm = vlib_mains[0];
477 u64 input_packets, last_input_packets;
479 vlib_main_t *this_vlib_main;
483 * Compute the average vector rate across all workers
487 start = vec_len (vlib_mains) > 1 ? 1 : 0;
489 for (i = start; i < vec_len (vlib_mains); i++)
491 this_vlib_main = vlib_mains[i];
492 vector_rate += vlib_last_vector_length_per_node (this_vlib_main);
494 vector_rate /= (f64) (i - start);
496 *sm->vector_rate_ptr = vector_rate / ((f64) (vec_len (vlib_mains) - start));
499 * Compute the aggregate input rate
501 now = vlib_time_now (vm);
502 dt = now - sm->last_runtime_ptr[0];
503 input_packets = vnet_get_aggregate_rx_packets ();
504 *sm->input_rate_ptr = (f64) (input_packets - sm->last_input_packets) / dt;
505 sm->last_runtime_ptr[0] = now;
506 sm->last_input_packets = input_packets;
507 sm->last_runtime_stats_clear_ptr[0] =
508 vm->node_main.time_last_runtime_stats_clear;
510 update_serialized_nodes (sm);
515 * fd.io coding-style-patch-verification: ON
518 * eval: (c-set-style "gnu")