perfmon plugin: 2-way parallel stat collection
[vpp.git] / src / vlib / node_cli.c
1 /*
2  * Copyright (c) 2015 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 /*
16  * node_cli.c: node CLI
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 #include <vlib/vlib.h>
41 #include <vlib/threads.h>
42
43 static int
44 node_cmp (void *a1, void *a2)
45 {
46   vlib_node_t **n1 = a1;
47   vlib_node_t **n2 = a2;
48
49   return vec_cmp (n1[0]->name, n2[0]->name);
50 }
51
52 static clib_error_t *
53 show_node_graph (vlib_main_t * vm,
54                  unformat_input_t * input, vlib_cli_command_t * cmd)
55 {
56   vlib_node_main_t *nm = &vm->node_main;
57   vlib_node_t *n;
58   u32 node_index;
59
60   vlib_cli_output (vm, "%U\n", format_vlib_node_graph, nm, 0);
61
62   if (unformat (input, "%U", unformat_vlib_node, vm, &node_index))
63     {
64       n = vlib_get_node (vm, node_index);
65       vlib_cli_output (vm, "%U\n", format_vlib_node_graph, nm, n);
66     }
67   else
68     {
69       vlib_node_t **nodes = vec_dup (nm->nodes);
70       uword i;
71
72       vec_sort_with_function (nodes, node_cmp);
73
74       for (i = 0; i < vec_len (nodes); i++)
75         vlib_cli_output (vm, "%U\n\n", format_vlib_node_graph, nm, nodes[i]);
76
77       vec_free (nodes);
78     }
79
80   return 0;
81 }
82
83 /* *INDENT-OFF* */
84 VLIB_CLI_COMMAND (show_node_graph_command, static) = {
85   .path = "show vlib graph",
86   .short_help = "Show packet processing node graph",
87   .function = show_node_graph,
88 };
89 /* *INDENT-ON* */
90
91 static u8 *
92 format_vlib_node_state (u8 * s, va_list * va)
93 {
94   vlib_main_t *vm = va_arg (*va, vlib_main_t *);
95   vlib_node_t *n = va_arg (*va, vlib_node_t *);
96   char *state;
97
98   state = "active";
99   if (n->type == VLIB_NODE_TYPE_PROCESS)
100     {
101       vlib_process_t *p = vlib_get_process_from_node (vm, n);
102
103       switch (p->flags & (VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_CLOCK
104                           | VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_EVENT))
105         {
106         default:
107           if (!(p->flags & VLIB_PROCESS_IS_RUNNING))
108             state = "done";
109           break;
110
111         case VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_CLOCK:
112           state = "time wait";
113           break;
114
115         case VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_EVENT:
116           state = "event wait";
117           break;
118
119         case (VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_EVENT | VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_CLOCK):
120           state =
121             "any wait";
122           break;
123         }
124     }
125   else if (n->type != VLIB_NODE_TYPE_INTERNAL)
126     {
127       state = "polling";
128       if (n->state == VLIB_NODE_STATE_DISABLED)
129         state = "disabled";
130       else if (n->state == VLIB_NODE_STATE_INTERRUPT)
131         state = "interrupt wait";
132     }
133
134   return format (s, "%s", state);
135 }
136
137 static u8 *
138 format_vlib_node_stats (u8 * s, va_list * va)
139 {
140   vlib_main_t *vm = va_arg (*va, vlib_main_t *);
141   vlib_node_t *n = va_arg (*va, vlib_node_t *);
142   int max = va_arg (*va, int);
143   f64 v;
144   u8 *ns;
145   u8 *misc_info = 0;
146   u64 c, p, l, d;
147   f64 x;
148   f64 maxc, maxcn;
149   u32 maxn;
150   u32 indent;
151
152   if (!n)
153     {
154       if (max)
155         s = format (s,
156                     "%=30s%=17s%=16s%=16s%=16s%=16s",
157                     "Name", "Max Node Clocks", "Vectors at Max",
158                     "Max Clocks", "Avg Clocks", "Avg Vectors/Call");
159       else
160         s = format (s,
161                     "%=30s%=12s%=16s%=16s%=16s%=16s%=16s",
162                     "Name", "State", "Calls", "Vectors", "Suspends",
163                     "Clocks", "Vectors/Call");
164       return s;
165     }
166
167   indent = format_get_indent (s);
168
169   l = n->stats_total.clocks - n->stats_last_clear.clocks;
170   c = n->stats_total.calls - n->stats_last_clear.calls;
171   p = n->stats_total.vectors - n->stats_last_clear.vectors;
172   d = n->stats_total.suspends - n->stats_last_clear.suspends;
173   maxc = (f64) n->stats_total.max_clock;
174   maxn = n->stats_total.max_clock_n;
175   if (n->stats_total.max_clock_n)
176     maxcn = (f64) n->stats_total.max_clock / (f64) maxn;
177   else
178     maxcn = 0.0;
179
180   /* Clocks per packet, per call or per suspend. */
181   x = 0;
182   if (p > 0)
183     x = (f64) l / (f64) p;
184   else if (c > 0)
185     x = (f64) l / (f64) c;
186   else if (d > 0)
187     x = (f64) l / (f64) d;
188
189   if (c > 0)
190     v = (double) p / (double) c;
191   else
192     v = 0;
193
194   if (n->type == VLIB_NODE_TYPE_PROCESS)
195     {
196       vlib_process_t *p = vlib_get_process_from_node (vm, n);
197
198       /* Show processes with events pending.  This helps spot bugs where events are not
199          being handled. */
200       if (!clib_bitmap_is_zero (p->non_empty_event_type_bitmap))
201         misc_info = format (misc_info, "events pending, ");
202     }
203   ns = n->name;
204
205   if (max)
206     s = format (s, "%-30v%=17.2e%=16d%=16.2e%=16.2e%=16.2e",
207                 ns, maxc, maxn, maxcn, x, v);
208   else
209     s = format (s, "%-30v%=12U%16Ld%16Ld%16Ld%16.2e%16.2f", ns,
210                 format_vlib_node_state, vm, n, c, p, d, x, v);
211
212   if (ns != n->name)
213     vec_free (ns);
214
215   if (misc_info)
216     {
217       s = format (s, "\n%U%v", format_white_space, indent + 4, misc_info);
218       vec_free (misc_info);
219     }
220
221   return s;
222 }
223
224 static clib_error_t *
225 show_node_runtime (vlib_main_t * vm,
226                    unformat_input_t * input, vlib_cli_command_t * cmd)
227 {
228   vlib_node_main_t *nm = &vm->node_main;
229   vlib_node_t *n;
230   f64 time_now;
231   u32 node_index;
232   vlib_node_t ***node_dups = 0;
233   f64 *vectors_per_main_loop = 0;
234   f64 *last_vector_length_per_node = 0;
235
236   time_now = vlib_time_now (vm);
237
238   if (unformat (input, "%U", unformat_vlib_node, vm, &node_index))
239     {
240       n = vlib_get_node (vm, node_index);
241       vlib_node_sync_stats (vm, n);
242       vlib_cli_output (vm, "%U\n", format_vlib_node_stats, vm, 0, 0);
243       vlib_cli_output (vm, "%U\n", format_vlib_node_stats, vm, n, 0);
244     }
245   else
246     {
247       vlib_node_t **nodes;
248       uword i, j;
249       f64 dt;
250       u64 n_input, n_output, n_drop, n_punt;
251       u64 n_internal_vectors, n_internal_calls;
252       u64 n_clocks, l, v, c, d;
253       int brief = 1;
254       int max = 0;
255       vlib_main_t **stat_vms = 0, *stat_vm;
256
257       /* Suppress nodes with zero calls since last clear */
258       if (unformat (input, "brief") || unformat (input, "b"))
259         brief = 1;
260       if (unformat (input, "verbose") || unformat (input, "v"))
261         brief = 0;
262       if (unformat (input, "max") || unformat (input, "m"))
263         max = 1;
264
265       for (i = 0; i < vec_len (vlib_mains); i++)
266         {
267           stat_vm = vlib_mains[i];
268           if (stat_vm)
269             vec_add1 (stat_vms, stat_vm);
270         }
271
272       /*
273        * Barrier sync across stats scraping.
274        * Otherwise, the counts will be grossly inaccurate.
275        */
276       vlib_worker_thread_barrier_sync (vm);
277
278       for (j = 0; j < vec_len (stat_vms); j++)
279         {
280           stat_vm = stat_vms[j];
281           nm = &stat_vm->node_main;
282
283           for (i = 0; i < vec_len (nm->nodes); i++)
284             {
285               n = nm->nodes[i];
286               vlib_node_sync_stats (stat_vm, n);
287             }
288
289           nodes = vec_dup (nm->nodes);
290
291           vec_add1 (node_dups, nodes);
292           vec_add1 (vectors_per_main_loop,
293                     vlib_last_vectors_per_main_loop_as_f64 (stat_vm));
294           vec_add1 (last_vector_length_per_node,
295                     vlib_last_vector_length_per_node (stat_vm));
296         }
297       vlib_worker_thread_barrier_release (vm);
298
299
300       for (j = 0; j < vec_len (stat_vms); j++)
301         {
302           stat_vm = stat_vms[j];
303           nodes = node_dups[j];
304
305           vec_sort_with_function (nodes, node_cmp);
306
307           n_input = n_output = n_drop = n_punt = n_clocks = 0;
308           n_internal_vectors = n_internal_calls = 0;
309           for (i = 0; i < vec_len (nodes); i++)
310             {
311               n = nodes[i];
312
313               l = n->stats_total.clocks - n->stats_last_clear.clocks;
314               n_clocks += l;
315
316               v = n->stats_total.vectors - n->stats_last_clear.vectors;
317               c = n->stats_total.calls - n->stats_last_clear.calls;
318
319               switch (n->type)
320                 {
321                 default:
322                   continue;
323
324                 case VLIB_NODE_TYPE_INTERNAL:
325                   n_output += (n->flags & VLIB_NODE_FLAG_IS_OUTPUT) ? v : 0;
326                   n_drop += (n->flags & VLIB_NODE_FLAG_IS_DROP) ? v : 0;
327                   n_punt += (n->flags & VLIB_NODE_FLAG_IS_PUNT) ? v : 0;
328                   if (!(n->flags & VLIB_NODE_FLAG_IS_OUTPUT))
329                     {
330                       n_internal_vectors += v;
331                       n_internal_calls += c;
332                     }
333                   if (n->flags & VLIB_NODE_FLAG_IS_HANDOFF)
334                     n_input += v;
335                   break;
336
337                 case VLIB_NODE_TYPE_INPUT:
338                   n_input += v;
339                   break;
340                 }
341             }
342
343           if (vec_len (vlib_mains) > 1)
344             {
345               vlib_worker_thread_t *w = vlib_worker_threads + j;
346               if (j > 0)
347                 vlib_cli_output (vm, "---------------");
348
349               if (w->cpu_id > -1)
350                 vlib_cli_output (vm, "Thread %d %s (lcore %u)", j, w->name,
351                                  w->cpu_id);
352               else
353                 vlib_cli_output (vm, "Thread %d %s", j, w->name);
354             }
355
356           dt = time_now - nm->time_last_runtime_stats_clear;
357           vlib_cli_output
358             (vm,
359              "Time %.1f, average vectors/node %.2f, last %d main loops %.2f per node %.2f"
360              "\n  vector rates in %.4e, out %.4e, drop %.4e, punt %.4e",
361              dt,
362              (n_internal_calls > 0
363               ? (f64) n_internal_vectors / (f64) n_internal_calls
364               : 0),
365              1 << VLIB_LOG2_MAIN_LOOPS_PER_STATS_UPDATE,
366              vectors_per_main_loop[j],
367              last_vector_length_per_node[j],
368              (f64) n_input / dt,
369              (f64) n_output / dt, (f64) n_drop / dt, (f64) n_punt / dt);
370
371           vlib_cli_output (vm, "%U", format_vlib_node_stats, stat_vm, 0, max);
372           for (i = 0; i < vec_len (nodes); i++)
373             {
374               c =
375                 nodes[i]->stats_total.calls -
376                 nodes[i]->stats_last_clear.calls;
377               d =
378                 nodes[i]->stats_total.suspends -
379                 nodes[i]->stats_last_clear.suspends;
380               if (c || d || !brief)
381                 {
382                   vlib_cli_output (vm, "%U", format_vlib_node_stats, stat_vm,
383                                    nodes[i], max);
384                 }
385             }
386           vec_free (nodes);
387         }
388       vec_free (stat_vms);
389       vec_free (node_dups);
390       vec_free (vectors_per_main_loop);
391       vec_free (last_vector_length_per_node);
392     }
393
394   return 0;
395 }
396
397 /* *INDENT-OFF* */
398 VLIB_CLI_COMMAND (show_node_runtime_command, static) = {
399   .path = "show runtime",
400   .short_help = "Show packet processing runtime",
401   .function = show_node_runtime,
402   .is_mp_safe = 1,
403 };
404 /* *INDENT-ON* */
405
406 static clib_error_t *
407 clear_node_runtime (vlib_main_t * vm,
408                     unformat_input_t * input, vlib_cli_command_t * cmd)
409 {
410   vlib_node_main_t *nm;
411   vlib_node_t *n;
412   int i, j;
413   vlib_main_t **stat_vms = 0, *stat_vm;
414   vlib_node_runtime_t *r;
415
416   for (i = 0; i < vec_len (vlib_mains); i++)
417     {
418       stat_vm = vlib_mains[i];
419       if (stat_vm)
420         vec_add1 (stat_vms, stat_vm);
421     }
422
423   vlib_worker_thread_barrier_sync (vm);
424
425   for (j = 0; j < vec_len (stat_vms); j++)
426     {
427       stat_vm = stat_vms[j];
428       nm = &stat_vm->node_main;
429
430       for (i = 0; i < vec_len (nm->nodes); i++)
431         {
432           n = nm->nodes[i];
433           vlib_node_sync_stats (stat_vm, n);
434           n->stats_last_clear = n->stats_total;
435
436           r = vlib_node_get_runtime (stat_vm, n->index);
437           r->max_clock = 0;
438         }
439       /* Note: input/output rates computed using vlib_global_main */
440       nm->time_last_runtime_stats_clear = vlib_time_now (vm);
441     }
442
443   vlib_worker_thread_barrier_release (vm);
444
445   vec_free (stat_vms);
446
447   return 0;
448 }
449
450 /* *INDENT-OFF* */
451 VLIB_CLI_COMMAND (clear_node_runtime_command, static) = {
452   .path = "clear runtime",
453   .short_help = "Clear packet processing runtime statistics",
454   .function = clear_node_runtime,
455 };
456 /* *INDENT-ON* */
457
458 static clib_error_t *
459 show_node (vlib_main_t * vm, unformat_input_t * input,
460            vlib_cli_command_t * cmd)
461 {
462   unformat_input_t _line_input, *line_input = &_line_input;
463   clib_error_t *error = 0;
464   vlib_node_main_t *nm = &vm->node_main;
465   vlib_node_t *n;
466   u8 *s = 0, *s2 = 0;
467   u32 i, node_index = ~0;
468   char *type_str;
469   u8 valid_node_name = 0;
470
471   if (!unformat_user (input, unformat_line_input, line_input))
472     return 0;
473
474   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
475     {
476       if (unformat (line_input, "index %u", &node_index))
477         ;
478       else
479         if (unformat (line_input, "%U", unformat_vlib_node, vm, &node_index))
480         valid_node_name = 1;
481       else if (!valid_node_name)
482         error = clib_error_return (0, "unknown node name: '%U'",
483                                    format_unformat_error, line_input);
484       else
485         error = clib_error_return (0, "unknown input '%U'",
486                                    format_unformat_error, line_input);
487     }
488
489   unformat_free (line_input);
490
491   if (error)
492     return error;
493
494   if (node_index >= vec_len (vm->node_main.nodes))
495     return clib_error_return (0, "please specify valid node");
496
497   n = vlib_get_node (vm, node_index);
498   vlib_node_sync_stats (vm, n);
499
500   switch (n->type)
501     {
502     case VLIB_NODE_TYPE_INTERNAL:
503       type_str = "internal";
504       break;
505     case VLIB_NODE_TYPE_INPUT:
506       type_str = "input";
507       break;
508     case VLIB_NODE_TYPE_PRE_INPUT:
509       type_str = "pre-input";
510       break;
511     case VLIB_NODE_TYPE_PROCESS:
512       type_str = "process";
513       break;
514     default:
515       type_str = "unknown";
516     }
517
518   if (n->sibling_of)
519     s = format (s, ", sibling-of %s", n->sibling_of);
520
521   vlib_cli_output (vm, "node %s, type %s, state %U, index %d%v\n",
522                    n->name, type_str, format_vlib_node_state, vm, n,
523                    n->index, s);
524   vec_reset_length (s);
525
526   if (n->node_fn_registrations)
527     {
528       vlib_node_fn_registration_t *fnr = n->node_fn_registrations;
529       while (fnr)
530         {
531           if (vec_len (s) == 0)
532             s = format (s, "\n    %-15s  %=8s  %6s",
533                         "Name", "Priority", "Active");
534           s = format (s, "\n    %-15s  %=8u  %=6s", fnr->name, fnr->priority,
535                       fnr->function == n->function ? "yes" : "");
536           fnr = fnr->next_registration;
537         }
538     }
539   else
540     s = format (s, "\n    default only");
541   vlib_cli_output (vm, "  node function variants:%v\n", s);
542   vec_reset_length (s);
543
544   for (i = 0; i < vec_len (n->next_nodes); i++)
545     {
546       vlib_node_t *pn;
547       if (n->next_nodes[i] == VLIB_INVALID_NODE_INDEX)
548         continue;
549
550       pn = vec_elt (nm->nodes, n->next_nodes[i]);
551
552       if (vec_len (s) == 0)
553         s = format (s, "\n    %10s  %10s  %=30s %8s",
554                     "next-index", "node-index", "Node", "Vectors");
555
556       s = format (s, "\n    %=10u  %=10u  %-30v %=8llu", i, n->next_nodes[i],
557                   pn->name, vec_elt (n->n_vectors_by_next_node, i));
558     }
559
560   if (vec_len (s) == 0)
561     s = format (s, "\n    none");
562   vlib_cli_output (vm, "\n  next nodes:%v\n", s);
563   vec_reset_length (s);
564
565   if (n->type == VLIB_NODE_TYPE_INTERNAL)
566     {
567       int j = 0;
568       /* *INDENT-OFF* */
569       clib_bitmap_foreach (i, n->prev_node_bitmap, ({
570             vlib_node_t *pn = vlib_get_node (vm, i);
571             if (j++ % 3 == 0)
572               s = format (s, "\n    ");
573             s2 = format (s2, "%v (%u)", pn->name, i);
574             s = format (s, "%-35v", s2);
575             vec_reset_length (s2);
576           }));
577       /* *INDENT-ON* */
578
579       if (vec_len (s) == 0)
580         s = format (s, "\n    none");
581       vlib_cli_output (vm, "\n  known previous nodes:%v\n", s);
582       vec_reset_length (s);
583     }
584
585   vec_free (s);
586   vec_free (s2);
587   return 0;
588 }
589
590 /* *INDENT-OFF* */
591 VLIB_CLI_COMMAND (show_node_command, static) = {
592   .path = "show node",
593   .short_help = "show node [index] <node-name | node-index>",
594   .function = show_node,
595 };
596
597 static clib_error_t *
598 set_node_fn(vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
599 {
600   unformat_input_t _line_input, *line_input = &_line_input;
601   u32 node_index;
602   vlib_node_t *n;
603   clib_error_t *err = 0;
604   vlib_node_fn_registration_t *fnr;
605   u8 *variant = 0;
606
607   if (!unformat_user (input, unformat_line_input, line_input))
608     return 0;
609
610   if (!unformat (line_input, "%U", unformat_vlib_node, vm, &node_index))
611     {
612       err = clib_error_return (0, "please specify valid node name");
613       goto done;
614     }
615
616   if (!unformat (line_input, "%s", &variant))
617     {
618       err = clib_error_return (0, "please specify node functional variant");
619       goto done;
620     }
621
622   n = vlib_get_node (vm, node_index);
623
624   if (n->node_fn_registrations == 0)
625     {
626       err = clib_error_return (0, "node doesn't have functional variants");
627       goto done;
628     }
629
630   fnr = n->node_fn_registrations;
631   vec_add1 (variant, 0);
632
633   while (fnr)
634     {
635       if (!strncmp (fnr->name, (char *) variant, vec_len (variant) - 1))
636         {
637           int i;
638
639           n->function = fnr->function;
640
641           for (i = 0; i < vec_len (vlib_mains); i++)
642             {
643               vlib_node_runtime_t *nrt;
644               nrt = vlib_node_get_runtime (vlib_mains[i], n->index);
645               nrt->function = fnr->function;
646             }
647           goto done;
648         }
649       fnr = fnr->next_registration;
650     }
651
652   err = clib_error_return (0, "node functional variant '%s' not found", variant);
653
654 done:
655   vec_free (variant);
656   unformat_free (line_input);
657   return err;
658 }
659
660 /* *INDENT-OFF* */
661 VLIB_CLI_COMMAND (set_node_fn_command, static) = {
662   .path = "set node function",
663   .short_help = "set node function <node-name> <variant-name>",
664   .function = set_node_fn,
665 };
666 /* *INDENT-ON* */
667
668 /* Dummy function to get us linked in. */
669 void
670 vlib_node_cli_reference (void)
671 {
672 }
673
674 /*
675  * fd.io coding-style-patch-verification: ON
676  *
677  * Local Variables:
678  * eval: (c-set-style "gnu")
679  * End:
680  */