8459138be3455572f566c726e2357e8826f9614d
[vpp.git] / src / vpp / stats / stat_segment.c
1 /*
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:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
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.
14  */
15 #include <vpp/stats/stats.h>
16
17 void
18 vlib_stat_segment_lock (void)
19 {
20   stats_main_t *sm = &stats_main;
21   clib_spinlock_lock (sm->stat_segment_lockp);
22 }
23
24 void
25 vlib_stat_segment_unlock (void)
26 {
27   stats_main_t *sm = &stats_main;
28   clib_spinlock_unlock (sm->stat_segment_lockp);
29 }
30
31 void *
32 vlib_stats_push_heap (void)
33 {
34   stats_main_t *sm = &stats_main;
35   ssvm_private_t *ssvmp = &sm->stat_segment;
36   ssvm_shared_header_t *shared_header;
37
38   ASSERT (ssvmp && ssvmp->sh);
39
40   shared_header = ssvmp->sh;
41
42   return ssvm_push_heap (shared_header);
43 }
44
45 void
46 vlib_stats_pop_heap (void *cm_arg, void *oldheap, stat_directory_type_t type)
47 {
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;
54   uword *p;
55
56   ASSERT (ssvmp && ssvmp->sh);
57
58   shared_header = ssvmp->sh;
59
60   /* Not all counters have names / hash-table entries */
61   if (cm->name || cm->stat_segment_name)
62     {
63       hash_pair_t *hp;
64       u8 *name_copy;
65
66       stat_segment_name = cm->stat_segment_name ?
67         cm->stat_segment_name : cm->name;
68
69       clib_spinlock_lock (sm->stat_segment_lockp);
70
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);
73       if (hp)
74         {
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);
78           vec_free (name_copy);
79           clib_mem_free (ep);
80         }
81       name_copy = format (0, "%s%c", stat_segment_name, 0);
82       ep = clib_mem_alloc (sizeof (*ep));
83       ep->type = type;
84       ep->value = cm->counters;
85       hash_set_mem (sm->counter_vector_by_name, name_copy, ep);
86
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;
90
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);
95     }
96   ssvm_pop_heap (oldheap);
97 }
98
99 void
100 vlib_stats_register_error_index (u8 * name, u64 index)
101 {
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;
106   hash_pair_t *hp;
107   u8 *name_copy;
108   uword *p;
109
110   ASSERT (ssvmp && ssvmp->sh);
111
112   shared_header = ssvmp->sh;
113
114   clib_spinlock_lock (sm->stat_segment_lockp);
115
116   /* Update hash table. The name must be copied into the segment */
117   hp = hash_get_pair (sm->counter_vector_by_name, name);
118   if (hp)
119     {
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);
124       clib_mem_free (ep);
125     }
126
127   ep = clib_mem_alloc (sizeof (*ep));
128   ep->type = STAT_DIR_TYPE_ERROR_INDEX;
129   ep->value = (void *) index;
130
131   hash_set_mem (sm->counter_vector_by_name, name, ep);
132
133   /* Reset the client hash table pointer, since it WILL change! */
134   shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
135
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);
140 }
141
142 void
143 vlib_stats_pop_heap2 (u64 * counter_vector, u32 thread_index, void *oldheap)
144 {
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;
149   hash_pair_t *hp;
150   u8 *error_vector_name;
151   u8 *name_copy;
152   uword *p;
153
154   ASSERT (ssvmp && ssvmp->sh);
155
156   shared_header = ssvmp->sh;
157
158   clib_spinlock_lock (sm->stat_segment_lockp);
159
160   error_vector_name = format (0, "/err/%d/counter_vector%c", thread_index, 0);
161
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);
164   if (hp)
165     {
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);
170       clib_mem_free (ep);
171     }
172
173   ep = clib_mem_alloc (sizeof (*ep));
174   ep->type = STAT_DIR_TYPE_VECTOR_POINTER;
175   ep->value = counter_vector;
176
177   hash_set_mem (sm->counter_vector_by_name, error_vector_name, ep);
178
179   /* Reset the client hash table pointer, since it WILL change! */
180   shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
181
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);
187 }
188
189 clib_error_t *
190 vlib_map_stat_segment_init (void)
191 {
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;
196   f64 *scalar_data;
197   u8 *name;
198   void *oldheap;
199   u32 *lock;
200   int rv;
201   u64 memory_size;
202
203   memory_size = sm->memory_size;
204   if (memory_size == 0)
205     memory_size = STAT_SEGMENT_DEFAULT_SIZE;
206
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;
212
213   rv = ssvm_master_init (ssvmp, SSVM_SEGMENT_MEMFD);
214
215   if (rv)
216     return clib_error_return (0, "stat segment ssvm init failure");
217   shared_header = ssvmp->sh;
218
219   oldheap = ssvm_push_heap (shared_header);
220
221   /* Set up the name to counter-vector hash table */
222   sm->counter_vector_by_name = hash_create_string (0, sizeof (uword));
223
224   sm->stat_segment_lockp = clib_mem_alloc (sizeof (clib_spinlock_t));
225
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;
230
231   /* Set up a few scalar stats */
232
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);
240
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;
245
246   hash_set_mem (sm->counter_vector_by_name, name, ep);
247
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;
252
253   hash_set_mem (sm->counter_vector_by_name, name, ep);
254
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;
259
260   hash_set_mem (sm->counter_vector_by_name, name, ep);
261
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;
266
267   hash_set_mem (sm->counter_vector_by_name, name, ep);
268
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;
273
274   hash_set_mem (sm->counter_vector_by_name, name, ep);
275
276
277   /* Publish the hash table */
278   shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
279
280   ssvm_pop_heap (oldheap);
281
282   return 0;
283 }
284
285 typedef struct
286 {
287   u8 *name;
288   stat_segment_directory_entry_t *dir_entry;
289 } show_stat_segment_t;
290
291 static int
292 name_sort_cmp (void *a1, void *a2)
293 {
294   show_stat_segment_t *n1 = a1;
295   show_stat_segment_t *n2 = a2;
296
297   return strcmp ((char *) n1->name, (char *) n2->name);
298 }
299
300 static u8 *
301 format_stat_dir_entry (u8 * s, va_list * args)
302 {
303   stat_segment_directory_entry_t *ep =
304     va_arg (*args, stat_segment_directory_entry_t *);
305   char *type_name;
306   char *format_string;
307
308   format_string = "%-10s %20llx";
309
310   switch (ep->type)
311     {
312     case STAT_DIR_TYPE_SCALAR_POINTER:
313       type_name = "ScalarPtr";
314       break;
315
316     case STAT_DIR_TYPE_VECTOR_POINTER:
317       type_name = "VectorPtr";
318       break;
319
320     case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
321     case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
322       type_name = "CMainPtr";
323       break;
324
325     case STAT_DIR_TYPE_SERIALIZED_NODES:
326       type_name = "SerNodesPtr";
327       break;
328
329     case STAT_DIR_TYPE_ERROR_INDEX:
330       type_name = "ErrIndex";
331       format_string = "%-10s %20lld";
332       break;
333
334     default:
335       type_name = "illegal!";
336       break;
337     }
338
339   return format (s, format_string, type_name, ep->value);
340 }
341
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)
346 {
347   stats_main_t *sm = &stats_main;
348   ssvm_private_t *ssvmp = &sm->stat_segment;
349   ssvm_shared_header_t *shared_header;
350   counter_t *counter;
351   hash_pair_t *p;
352   show_stat_segment_t *show_data = 0;
353   show_stat_segment_t *this;
354   int i;
355
356   int verbose = 0;
357   u8 *s;
358
359   if (unformat (input, "verbose"))
360     verbose = 1;
361
362   clib_spinlock_lock (sm->stat_segment_lockp);
363
364   /* *INDENT-OFF* */
365   hash_foreach_pair (p, sm->counter_vector_by_name,
366   ({
367     vec_add2 (show_data, this, 1);
368
369     this->name = (u8 *) (p->key);
370     this->dir_entry = (stat_segment_directory_entry_t *)(p->value[0]);
371   }));
372   /* *INDENT-ON* */
373
374   clib_spinlock_unlock (sm->stat_segment_lockp);
375
376   vec_sort_with_function (show_data, name_sort_cmp);
377
378   vlib_cli_output (vm, "%-60s %10s %20s", "Name", "Type", "Value");
379
380   for (i = 0; i < vec_len (show_data); i++)
381     {
382       this = vec_elt_at_index (show_data, i);
383
384       vlib_cli_output (vm, "%-60s %31U",
385                        this->name, format_stat_dir_entry, this->dir_entry);
386     }
387
388   if (verbose)
389     {
390       ASSERT (ssvmp && ssvmp->sh);
391
392       shared_header = ssvmp->sh;
393
394       vlib_cli_output (vm, "%U", format_mheap,
395                        shared_header->heap, 0 /* verbose */ );
396     }
397
398   return 0;
399 }
400
401 /* *INDENT-OFF* */
402 VLIB_CLI_COMMAND (show_stat_segment_command, static) =
403 {
404   .path = "show statistics segment",
405   .short_help = "show statistics segment [verbose]",
406   .function = show_stat_segment_command_fn,
407 };
408 /* *INDENT-ON* */
409
410 static inline void
411 update_serialized_nodes (stats_main_t * sm)
412 {
413   int i;
414   vlib_main_t *vm = vlib_mains[0];
415   ssvm_private_t *ssvmp = &sm->stat_segment;
416   ssvm_shared_header_t *shared_header;
417   void *oldheap;
418   stat_segment_directory_entry_t *ep;
419   hash_pair_t *hp;
420   u8 *name_copy;
421
422   ASSERT (ssvmp && ssvmp->sh);
423
424   vec_reset_length (sm->serialized_nodes);
425
426   shared_header = ssvmp->sh;
427
428   oldheap = ssvm_push_heap (shared_header);
429
430   clib_spinlock_lock (sm->stat_segment_lockp);
431
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);
437
438   sm->serialized_nodes = vlib_node_serialize (vm, sm->node_dups,
439                                               sm->serialized_nodes,
440                                               0 /* include nexts */ ,
441                                               1 /* include stats */ );
442
443   hp = hash_get_pair (sm->counter_vector_by_name, "serialized_nodes");
444   if (hp)
445     {
446       name_copy = (u8 *) hp->key;
447       ep = (stat_segment_directory_entry_t *) (hp->value[0]);
448
449       if (ep->value != sm->serialized_nodes)
450         {
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);
455         }
456     }
457   else
458     {
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);
464
465       /* Reset the client hash table pointer */
466       shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR]
467         = sm->counter_vector_by_name;
468
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);
472     }
473
474   clib_spinlock_unlock (sm->stat_segment_lockp);
475   ssvm_pop_heap (oldheap);
476 }
477
478 /*
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.
482  */
483
484 void
485 do_stat_segment_updates (stats_main_t * sm)
486 {
487   vlib_main_t *vm = vlib_mains[0];
488   f64 vector_rate;
489   u64 input_packets, last_input_packets;
490   f64 dt, now;
491   vlib_main_t *this_vlib_main;
492   int i, start;
493
494   /*
495    * Compute the average vector rate across all workers
496    */
497   vector_rate = 0.0;
498
499   start = vec_len (vlib_mains) > 1 ? 1 : 0;
500
501   for (i = start; i < vec_len (vlib_mains); i++)
502     {
503       this_vlib_main = vlib_mains[i];
504       vector_rate += vlib_last_vector_length_per_node (this_vlib_main);
505     }
506   vector_rate /= (f64) (i - start);
507
508   *sm->vector_rate_ptr = vector_rate / ((f64) (vec_len (vlib_mains) - start));
509
510   /*
511    * Compute the aggregate input rate
512    */
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;
521
522   if (sm->serialize_nodes)
523     update_serialized_nodes (sm);
524
525   /* Heartbeat, so clients detect we're still here */
526   (*sm->heartbeat_ptr)++;
527 }
528
529 static clib_error_t *
530 statseg_config (vlib_main_t * vm, unformat_input_t * input)
531 {
532   stats_main_t *sm = &stats_main;
533   uword ms;
534
535   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
536     {
537       if (unformat (input, "size %U", unformat_memory_size, &sm->memory_size))
538         ;
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;
543       else
544         return clib_error_return (0, "unknown input `%U'",
545                                   format_unformat_error, input);
546     }
547
548   return 0;
549 }
550
551 VLIB_EARLY_CONFIG_FUNCTION (statseg_config, "statseg");
552
553 /*
554  * fd.io coding-style-patch-verification: ON
555  *
556  * Local Variables:
557  * eval: (c-set-style "gnu")
558  * End:
559  */