f1db684fecd2a58ec0f7821609f5b35b1316b75e
[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_stats_push_heap (void)
19 {
20   stats_main_t *sm = &stats_main;
21   ssvm_private_t *ssvmp = &sm->stat_segment;
22   ssvm_shared_header_t *shared_header;
23
24   ASSERT (ssvmp && ssvmp->sh);
25
26   shared_header = ssvmp->sh;
27
28   return ssvm_push_heap (shared_header);
29 }
30
31 void
32 vlib_stats_pop_heap (void *cm_arg, void *oldheap)
33 {
34   vlib_simple_counter_main_t *cm = (vlib_simple_counter_main_t *) cm_arg;
35   stats_main_t *sm = &stats_main;
36   ssvm_private_t *ssvmp = &sm->stat_segment;
37   ssvm_shared_header_t *shared_header;
38   char *stat_segment_name;
39   stat_segment_directory_entry_t *ep;
40   uword *p;
41
42   ASSERT (ssvmp && ssvmp->sh);
43
44   shared_header = ssvmp->sh;
45
46   /* Not all counters have names / hash-table entries */
47   if (cm->name || cm->stat_segment_name)
48     {
49       hash_pair_t *hp;
50       u8 *name_copy;
51
52       stat_segment_name = cm->stat_segment_name ?
53         cm->stat_segment_name : cm->name;
54
55       clib_spinlock_lock (sm->stat_segment_lockp);
56
57       /* Update hash table. The name must be copied into the segment */
58       hp = hash_get_pair (sm->counter_vector_by_name, stat_segment_name);
59       if (hp)
60         {
61           name_copy = (u8 *) hp->key;
62           ep = (stat_segment_directory_entry_t *) (hp->value[0]);
63           hash_unset_mem (sm->counter_vector_by_name, stat_segment_name);
64           vec_free (name_copy);
65           clib_mem_free (ep);
66         }
67       name_copy = format (0, "%s%c", stat_segment_name, 0);
68       ep = clib_mem_alloc (sizeof (*ep));
69       ep->type = STAT_DIR_TYPE_COUNTER_VECTOR;
70       ep->value = cm->counters;
71       hash_set_mem (sm->counter_vector_by_name, name_copy, ep);
72
73       /* Reset the client hash table pointer, since it WILL change! */
74       shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR]
75         = sm->counter_vector_by_name;
76
77       /* Warn clients to refresh any pointers they might be holding */
78       shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
79         ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
80       clib_spinlock_unlock (sm->stat_segment_lockp);
81     }
82   ssvm_pop_heap (oldheap);
83 }
84
85 void
86 vlib_stats_register_error_index (u8 * name, u64 index)
87 {
88   stats_main_t *sm = &stats_main;
89   ssvm_private_t *ssvmp = &sm->stat_segment;
90   ssvm_shared_header_t *shared_header;
91   stat_segment_directory_entry_t *ep;
92   hash_pair_t *hp;
93   u8 *name_copy;
94   uword *p;
95
96   ASSERT (ssvmp && ssvmp->sh);
97
98   shared_header = ssvmp->sh;
99
100   clib_spinlock_lock (sm->stat_segment_lockp);
101
102   /* Update hash table. The name must be copied into the segment */
103   hp = hash_get_pair (sm->counter_vector_by_name, name);
104   if (hp)
105     {
106       name_copy = (u8 *) hp->key;
107       ep = (stat_segment_directory_entry_t *) (hp->value[0]);
108       hash_unset_mem (sm->counter_vector_by_name, name);
109       vec_free (name_copy);
110       clib_mem_free (ep);
111     }
112
113   ep = clib_mem_alloc (sizeof (*ep));
114   ep->type = STAT_DIR_TYPE_ERROR_INDEX;
115   ep->value = (void *) index;
116
117   hash_set_mem (sm->counter_vector_by_name, name, ep);
118
119   /* Reset the client hash table pointer, since it WILL change! */
120   shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
121
122   /* Warn clients to refresh any pointers they might be holding */
123   shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
124     ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
125   clib_spinlock_unlock (sm->stat_segment_lockp);
126 }
127
128 void
129 vlib_stats_pop_heap2 (u64 * counter_vector, u32 thread_index, void *oldheap)
130 {
131   stats_main_t *sm = &stats_main;
132   ssvm_private_t *ssvmp = &sm->stat_segment;
133   ssvm_shared_header_t *shared_header;
134   stat_segment_directory_entry_t *ep;
135   hash_pair_t *hp;
136   u8 *error_vector_name;
137   u8 *name_copy;
138   uword *p;
139
140   ASSERT (ssvmp && ssvmp->sh);
141
142   shared_header = ssvmp->sh;
143
144   clib_spinlock_lock (sm->stat_segment_lockp);
145
146   error_vector_name = format (0, "/err/%d/counter_vector%c", thread_index, 0);
147
148   /* Update hash table. The name must be copied into the segment */
149   hp = hash_get_pair (sm->counter_vector_by_name, error_vector_name);
150   if (hp)
151     {
152       name_copy = (u8 *) hp->key;
153       ep = (stat_segment_directory_entry_t *) (hp->value[0]);
154       hash_unset_mem (sm->counter_vector_by_name, error_vector_name);
155       vec_free (name_copy);
156       clib_mem_free (ep);
157     }
158
159   ep = clib_mem_alloc (sizeof (*ep));
160   ep->type = STAT_DIR_TYPE_VECTOR_POINTER;
161   ep->value = counter_vector;
162
163   hash_set_mem (sm->counter_vector_by_name, error_vector_name, ep);
164
165   /* Reset the client hash table pointer, since it WILL change! */
166   shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
167
168   /* Warn clients to refresh any pointers they might be holding */
169   shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
170     ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
171   clib_spinlock_unlock (sm->stat_segment_lockp);
172   ssvm_pop_heap (oldheap);
173 }
174
175 static clib_error_t *
176 map_stat_segment_init (vlib_main_t * vm)
177 {
178   stats_main_t *sm = &stats_main;
179   ssvm_private_t *ssvmp = &sm->stat_segment;
180   ssvm_shared_header_t *shared_header;
181   stat_segment_directory_entry_t *ep;
182   f64 *scalar_data;
183   u8 *name;
184   void *oldheap;
185   u32 *lock;
186   int rv;
187
188   ssvmp->ssvm_size = 32 << 20;  /*$$$$$ CONFIG PARAM */
189   ssvmp->i_am_master = 1;
190   ssvmp->my_pid = getpid ();
191   ssvmp->name = format (0, "/stats%c", 0);
192   ssvmp->requested_va = 0;
193
194   rv = ssvm_master_init (ssvmp, SSVM_SEGMENT_MEMFD);
195
196   if (rv)
197     return clib_error_return (0, "stat segment ssvm init failure");
198   shared_header = ssvmp->sh;
199
200   oldheap = ssvm_push_heap (shared_header);
201
202   /* Set up the name to counter-vector hash table */
203   sm->counter_vector_by_name = hash_create_string (0, sizeof (uword));
204
205   sm->stat_segment_lockp = clib_mem_alloc (sizeof (clib_spinlock_t));
206
207   /* Save the hash table address in the shared segment, for clients */
208   clib_spinlock_init (sm->stat_segment_lockp);
209   shared_header->opaque[STAT_SEGMENT_OPAQUE_LOCK] = sm->stat_segment_lockp;
210   shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *) 1;
211
212   /* Set up a few scalar stats */
213
214   scalar_data = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
215                                         CLIB_CACHE_LINE_BYTES);
216   sm->vector_rate_ptr = (scalar_data + 0);
217   sm->input_rate_ptr = (scalar_data + 1);
218
219   name = format (0, "vector_rate%c", 0);
220   ep = clib_mem_alloc (sizeof (*ep));
221   ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
222   ep->value = sm->vector_rate_ptr;
223
224   hash_set_mem (sm->counter_vector_by_name, name, ep);
225
226   name = format (0, "input_rate%c", 0);
227   ep = clib_mem_alloc (sizeof (*ep));
228   ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
229   ep->value = sm->input_rate_ptr;
230
231   hash_set_mem (sm->counter_vector_by_name, name, ep);
232
233   /* Publish the hash table */
234   shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
235
236   ssvm_pop_heap (oldheap);
237
238   return 0;
239 }
240
241 VLIB_INIT_FUNCTION (map_stat_segment_init);
242
243 typedef struct
244 {
245   u8 *name;
246   stat_segment_directory_entry_t *dir_entry;
247 } show_stat_segment_t;
248
249 static int
250 name_sort_cmp (void *a1, void *a2)
251 {
252   show_stat_segment_t *n1 = a1;
253   show_stat_segment_t *n2 = a2;
254
255   return strcmp ((char *) n1->name, (char *) n2->name);
256 }
257
258 static u8 *
259 format_stat_dir_entry (u8 * s, va_list * args)
260 {
261   stat_segment_directory_entry_t *ep =
262     va_arg (*args, stat_segment_directory_entry_t *);
263   char *type_name;
264   char *format_string;
265
266   format_string = "%-10s %20llx";
267
268   switch (ep->type)
269     {
270     case STAT_DIR_TYPE_SCALAR_POINTER:
271       type_name = "ScalarPtr";
272       break;
273
274     case STAT_DIR_TYPE_VECTOR_POINTER:
275       type_name = "VectorPtr";
276       break;
277
278     case STAT_DIR_TYPE_COUNTER_VECTOR:
279       type_name = "CMainPtr";
280       break;
281
282     case STAT_DIR_TYPE_ERROR_INDEX:
283       type_name = "ErrIndex";
284       format_string = "%-10s %20lld";
285       break;
286
287     default:
288       type_name = "illegal!";
289       break;
290     }
291
292   return format (s, format_string, type_name, ep->value);
293 }
294
295
296
297 static clib_error_t *
298 show_stat_segment_command_fn (vlib_main_t * vm,
299                               unformat_input_t * input,
300                               vlib_cli_command_t * cmd)
301 {
302   stats_main_t *sm = &stats_main;
303   ssvm_private_t *ssvmp = &sm->stat_segment;
304   ssvm_shared_header_t *shared_header;
305   counter_t *counter;
306   hash_pair_t *p;
307   show_stat_segment_t *show_data = 0;
308   show_stat_segment_t *this;
309   int i;
310
311   int verbose = 0;
312   u8 *s;
313
314   if (unformat (input, "verbose"))
315     verbose = 1;
316
317   clib_spinlock_lock (sm->stat_segment_lockp);
318
319   /* *INDENT-OFF* */
320   hash_foreach_pair (p, sm->counter_vector_by_name,
321   ({
322     vec_add2 (show_data, this, 1);
323
324     this->name = (u8 *) (p->key);
325     this->dir_entry = (stat_segment_directory_entry_t *)(p->value[0]);
326   }));
327   /* *INDENT-ON* */
328
329   clib_spinlock_unlock (sm->stat_segment_lockp);
330
331   vec_sort_with_function (show_data, name_sort_cmp);
332
333   vlib_cli_output (vm, "%-60s %10s %20s", "Name", "Type", "Value");
334
335   for (i = 0; i < vec_len (show_data); i++)
336     {
337       this = vec_elt_at_index (show_data, i);
338
339       vlib_cli_output (vm, "%-60s %31U",
340                        this->name, format_stat_dir_entry, this->dir_entry);
341     }
342
343   if (verbose)
344     {
345       ASSERT (ssvmp && ssvmp->sh);
346
347       shared_header = ssvmp->sh;
348
349       vlib_cli_output (vm, "%U", format_mheap,
350                        shared_header->heap, 0 /* verbose */ );
351     }
352
353   return 0;
354 }
355
356 /* *INDENT-OFF* */
357 VLIB_CLI_COMMAND (show_stat_segment_command, static) =
358 {
359   .path = "show statistics segment",
360   .short_help = "show statistics segment [verbose]",
361   .function = show_stat_segment_command_fn,
362 };
363 /* *INDENT-ON* */
364
365 static uword
366 stat_segment_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
367                       vlib_frame_t * f)
368 {
369   f64 vector_rate;
370   u64 input_packets, last_input_packets;
371   f64 last_runtime, dt, now;
372   vlib_main_t *this_vlib_main;
373   stats_main_t *sm = &stats_main;
374   int i;
375
376   last_runtime = 0.0;
377   last_input_packets = 0;
378
379   last_runtime = 0.0;
380   last_input_packets = 0;
381
382   while (1)
383     {
384       vlib_process_suspend (vm, 5.0);
385
386       /*
387        * Compute the average vector rate across all workers
388        */
389       vector_rate = 0.0;
390
391       /* *INDENT-OFF* */
392       for (i = 0; i < vec_len (vlib_mains); i++)
393         {
394           this_vlib_main = vlib_mains[i];
395           vector_rate += vlib_last_vector_length_per_node (vm);
396         }
397       vector_rate /= (f64) i;
398
399       /* *INDENT-ON* */
400
401       *sm->vector_rate_ptr = vector_rate / ((f64) vec_len (vlib_mains));
402       now = vlib_time_now (vm);
403       dt = now - last_runtime;
404       input_packets = vnet_get_aggregate_rx_packets ();
405       *sm->input_rate_ptr = (f64) (input_packets - last_input_packets) / dt;
406       last_runtime = now;
407       last_input_packets = input_packets;
408     }
409
410   return 0;                     /* not so much */
411 }
412
413 /* *INDENT-OFF* */
414 VLIB_REGISTER_NODE (stat_segment_node,static) =
415 {
416   .function = stat_segment_process,
417   .type = VLIB_NODE_TYPE_PROCESS,
418   .name = "stat-segment-process",
419 };
420 /* *INDENT-ON* */
421
422
423 /*
424  * fd.io coding-style-patch-verification: ON
425  *
426  * Local Variables:
427  * eval: (c-set-style "gnu")
428  * End:
429  */