X86_64 perf counter plugin
[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   u64 pmc_ticks;
152   f64 pmc_ticks_per_packet;
153
154   if (!n)
155     {
156       if (max)
157         s = format (s,
158                     "%=30s%=17s%=16s%=16s%=16s%=16s",
159                     "Name", "Max Node Clocks", "Vectors at Max",
160                     "Max Clocks", "Avg Clocks", "Avg Vectors/Call");
161       else
162         s = format (s,
163                     "%=30s%=12s%=16s%=16s%=16s%=16s%=16s",
164                     "Name", "State", "Calls", "Vectors", "Suspends",
165                     "Clocks", "Vectors/Call");
166       if (vm->perf_counter_id)
167         s = format (s, "%=16s", "Perf Ticks");
168
169       return s;
170     }
171
172   indent = format_get_indent (s);
173
174   l = n->stats_total.clocks - n->stats_last_clear.clocks;
175   c = n->stats_total.calls - n->stats_last_clear.calls;
176   p = n->stats_total.vectors - n->stats_last_clear.vectors;
177   d = n->stats_total.suspends - n->stats_last_clear.suspends;
178   maxc = (f64) n->stats_total.max_clock;
179   maxn = n->stats_total.max_clock_n;
180   if (n->stats_total.max_clock_n)
181     maxcn = (f64) n->stats_total.max_clock / (f64) maxn;
182   else
183     maxcn = 0.0;
184
185   pmc_ticks = n->stats_total.perf_counter_ticks -
186     n->stats_last_clear.perf_counter_ticks;
187   if (p > 0)
188     pmc_ticks_per_packet = (f64) pmc_ticks / (f64) p;
189   else
190     pmc_ticks_per_packet = 0.0;
191
192   /* Clocks per packet, per call or per suspend. */
193   x = 0;
194   if (p > 0)
195     x = (f64) l / (f64) p;
196   else if (c > 0)
197     x = (f64) l / (f64) c;
198   else if (d > 0)
199     x = (f64) l / (f64) d;
200
201   if (c > 0)
202     v = (double) p / (double) c;
203   else
204     v = 0;
205
206   if (n->type == VLIB_NODE_TYPE_PROCESS)
207     {
208       vlib_process_t *p = vlib_get_process_from_node (vm, n);
209
210       /* Show processes with events pending.  This helps spot bugs where events are not
211          being handled. */
212       if (!clib_bitmap_is_zero (p->non_empty_event_type_bitmap))
213         misc_info = format (misc_info, "events pending, ");
214     }
215   ns = n->name;
216
217   if (max)
218     s = format (s, "%-30v%=17.2e%=16d%=16.2e%=16.2e%=16.2e",
219                 ns, maxc, maxn, maxcn, x, v);
220   else
221     s = format (s, "%-30v%=12U%16Ld%16Ld%16Ld%16.2e%16.2f", ns,
222                 format_vlib_node_state, vm, n, c, p, d, x, v);
223
224   if (pmc_ticks_per_packet > 0.0)
225     s = format (s, "%16.2e", pmc_ticks_per_packet);
226
227   if (ns != n->name)
228     vec_free (ns);
229
230   if (misc_info)
231     {
232       s = format (s, "\n%U%v", format_white_space, indent + 4, misc_info);
233       vec_free (misc_info);
234     }
235
236   return s;
237 }
238
239 static clib_error_t *
240 show_node_runtime (vlib_main_t * vm,
241                    unformat_input_t * input, vlib_cli_command_t * cmd)
242 {
243   vlib_node_main_t *nm = &vm->node_main;
244   vlib_node_t *n;
245   f64 time_now;
246   u32 node_index;
247   vlib_node_t ***node_dups = 0;
248   f64 *vectors_per_main_loop = 0;
249   f64 *last_vector_length_per_node = 0;
250
251   time_now = vlib_time_now (vm);
252
253   if (unformat (input, "%U", unformat_vlib_node, vm, &node_index))
254     {
255       n = vlib_get_node (vm, node_index);
256       vlib_node_sync_stats (vm, n);
257       vlib_cli_output (vm, "%U\n", format_vlib_node_stats, vm, 0, 0);
258       vlib_cli_output (vm, "%U\n", format_vlib_node_stats, vm, n, 0);
259     }
260   else
261     {
262       vlib_node_t **nodes;
263       uword i, j;
264       f64 dt;
265       u64 n_input, n_output, n_drop, n_punt;
266       u64 n_internal_vectors, n_internal_calls;
267       u64 n_clocks, l, v, c, d;
268       int brief = 1;
269       int max = 0;
270       vlib_main_t **stat_vms = 0, *stat_vm;
271
272       /* Suppress nodes with zero calls since last clear */
273       if (unformat (input, "brief") || unformat (input, "b"))
274         brief = 1;
275       if (unformat (input, "verbose") || unformat (input, "v"))
276         brief = 0;
277       if (unformat (input, "max") || unformat (input, "m"))
278         max = 1;
279
280       for (i = 0; i < vec_len (vlib_mains); i++)
281         {
282           stat_vm = vlib_mains[i];
283           if (stat_vm)
284             vec_add1 (stat_vms, stat_vm);
285         }
286
287       /*
288        * Barrier sync across stats scraping.
289        * Otherwise, the counts will be grossly inaccurate.
290        */
291       vlib_worker_thread_barrier_sync (vm);
292
293       for (j = 0; j < vec_len (stat_vms); j++)
294         {
295           stat_vm = stat_vms[j];
296           nm = &stat_vm->node_main;
297
298           for (i = 0; i < vec_len (nm->nodes); i++)
299             {
300               n = nm->nodes[i];
301               vlib_node_sync_stats (stat_vm, n);
302             }
303
304           nodes = vec_dup (nm->nodes);
305
306           vec_add1 (node_dups, nodes);
307           vec_add1 (vectors_per_main_loop,
308                     vlib_last_vectors_per_main_loop_as_f64 (stat_vm));
309           vec_add1 (last_vector_length_per_node,
310                     vlib_last_vector_length_per_node (stat_vm));
311         }
312       vlib_worker_thread_barrier_release (vm);
313
314
315       for (j = 0; j < vec_len (stat_vms); j++)
316         {
317           stat_vm = stat_vms[j];
318           nodes = node_dups[j];
319
320           vec_sort_with_function (nodes, node_cmp);
321
322           n_input = n_output = n_drop = n_punt = n_clocks = 0;
323           n_internal_vectors = n_internal_calls = 0;
324           for (i = 0; i < vec_len (nodes); i++)
325             {
326               n = nodes[i];
327
328               l = n->stats_total.clocks - n->stats_last_clear.clocks;
329               n_clocks += l;
330
331               v = n->stats_total.vectors - n->stats_last_clear.vectors;
332               c = n->stats_total.calls - n->stats_last_clear.calls;
333
334               switch (n->type)
335                 {
336                 default:
337                   continue;
338
339                 case VLIB_NODE_TYPE_INTERNAL:
340                   n_output += (n->flags & VLIB_NODE_FLAG_IS_OUTPUT) ? v : 0;
341                   n_drop += (n->flags & VLIB_NODE_FLAG_IS_DROP) ? v : 0;
342                   n_punt += (n->flags & VLIB_NODE_FLAG_IS_PUNT) ? v : 0;
343                   if (!(n->flags & VLIB_NODE_FLAG_IS_OUTPUT))
344                     {
345                       n_internal_vectors += v;
346                       n_internal_calls += c;
347                     }
348                   if (n->flags & VLIB_NODE_FLAG_IS_HANDOFF)
349                     n_input += v;
350                   break;
351
352                 case VLIB_NODE_TYPE_INPUT:
353                   n_input += v;
354                   break;
355                 }
356             }
357
358           if (vec_len (vlib_mains) > 1)
359             {
360               vlib_worker_thread_t *w = vlib_worker_threads + j;
361               if (j > 0)
362                 vlib_cli_output (vm, "---------------");
363
364               if (w->cpu_id > -1)
365                 vlib_cli_output (vm, "Thread %d %s (lcore %u)", j, w->name,
366                                  w->cpu_id);
367               else
368                 vlib_cli_output (vm, "Thread %d %s", j, w->name);
369             }
370
371           dt = time_now - nm->time_last_runtime_stats_clear;
372           vlib_cli_output
373             (vm,
374              "Time %.1f, average vectors/node %.2f, last %d main loops %.2f per node %.2f"
375              "\n  vector rates in %.4e, out %.4e, drop %.4e, punt %.4e",
376              dt,
377              (n_internal_calls > 0
378               ? (f64) n_internal_vectors / (f64) n_internal_calls
379               : 0),
380              1 << VLIB_LOG2_MAIN_LOOPS_PER_STATS_UPDATE,
381              vectors_per_main_loop[j],
382              last_vector_length_per_node[j],
383              (f64) n_input / dt,
384              (f64) n_output / dt, (f64) n_drop / dt, (f64) n_punt / dt);
385
386           vlib_cli_output (vm, "%U", format_vlib_node_stats, stat_vm, 0, max);
387           for (i = 0; i < vec_len (nodes); i++)
388             {
389               c =
390                 nodes[i]->stats_total.calls -
391                 nodes[i]->stats_last_clear.calls;
392               d =
393                 nodes[i]->stats_total.suspends -
394                 nodes[i]->stats_last_clear.suspends;
395               if (c || d || !brief)
396                 {
397                   vlib_cli_output (vm, "%U", format_vlib_node_stats, stat_vm,
398                                    nodes[i], max);
399                 }
400             }
401           vec_free (nodes);
402         }
403       vec_free (stat_vms);
404       vec_free (node_dups);
405       vec_free (vectors_per_main_loop);
406       vec_free (last_vector_length_per_node);
407     }
408
409   return 0;
410 }
411
412 /* *INDENT-OFF* */
413 VLIB_CLI_COMMAND (show_node_runtime_command, static) = {
414   .path = "show runtime",
415   .short_help = "Show packet processing runtime",
416   .function = show_node_runtime,
417   .is_mp_safe = 1,
418 };
419 /* *INDENT-ON* */
420
421 static clib_error_t *
422 clear_node_runtime (vlib_main_t * vm,
423                     unformat_input_t * input, vlib_cli_command_t * cmd)
424 {
425   vlib_node_main_t *nm;
426   vlib_node_t *n;
427   int i, j;
428   vlib_main_t **stat_vms = 0, *stat_vm;
429   vlib_node_runtime_t *r;
430
431   for (i = 0; i < vec_len (vlib_mains); i++)
432     {
433       stat_vm = vlib_mains[i];
434       if (stat_vm)
435         vec_add1 (stat_vms, stat_vm);
436     }
437
438   vlib_worker_thread_barrier_sync (vm);
439
440   for (j = 0; j < vec_len (stat_vms); j++)
441     {
442       stat_vm = stat_vms[j];
443       nm = &stat_vm->node_main;
444
445       for (i = 0; i < vec_len (nm->nodes); i++)
446         {
447           n = nm->nodes[i];
448           vlib_node_sync_stats (stat_vm, n);
449           n->stats_last_clear = n->stats_total;
450
451           r = vlib_node_get_runtime (stat_vm, n->index);
452           r->max_clock = 0;
453         }
454       /* Note: input/output rates computed using vlib_global_main */
455       nm->time_last_runtime_stats_clear = vlib_time_now (vm);
456     }
457
458   vlib_worker_thread_barrier_release (vm);
459
460   vec_free (stat_vms);
461
462   return 0;
463 }
464
465 /* *INDENT-OFF* */
466 VLIB_CLI_COMMAND (clear_node_runtime_command, static) = {
467   .path = "clear runtime",
468   .short_help = "Clear packet processing runtime statistics",
469   .function = clear_node_runtime,
470 };
471 /* *INDENT-ON* */
472
473 static clib_error_t *
474 show_node (vlib_main_t * vm, unformat_input_t * input,
475            vlib_cli_command_t * cmd)
476 {
477   unformat_input_t _line_input, *line_input = &_line_input;
478   clib_error_t *error = 0;
479   vlib_node_main_t *nm = &vm->node_main;
480   vlib_node_t *n;
481   u8 *s = 0, *s2 = 0;
482   u32 i, node_index = ~0;
483   char *type_str;
484   u8 valid_node_name = 0;
485
486   if (!unformat_user (input, unformat_line_input, line_input))
487     return 0;
488
489   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
490     {
491       if (unformat (line_input, "index %u", &node_index))
492         ;
493       else
494         if (unformat (line_input, "%U", unformat_vlib_node, vm, &node_index))
495         valid_node_name = 1;
496       else if (!valid_node_name)
497         error = clib_error_return (0, "unknown node name: '%U'",
498                                    format_unformat_error, line_input);
499       else
500         error = clib_error_return (0, "unknown input '%U'",
501                                    format_unformat_error, line_input);
502     }
503
504   unformat_free (line_input);
505
506   if (error)
507     return error;
508
509   if (node_index >= vec_len (vm->node_main.nodes))
510     return clib_error_return (0, "please specify valid node");
511
512   n = vlib_get_node (vm, node_index);
513   vlib_node_sync_stats (vm, n);
514
515   switch (n->type)
516     {
517     case VLIB_NODE_TYPE_INTERNAL:
518       type_str = "internal";
519       break;
520     case VLIB_NODE_TYPE_INPUT:
521       type_str = "input";
522       break;
523     case VLIB_NODE_TYPE_PRE_INPUT:
524       type_str = "pre-input";
525       break;
526     case VLIB_NODE_TYPE_PROCESS:
527       type_str = "process";
528       break;
529     default:
530       type_str = "unknown";
531     }
532
533   if (n->sibling_of)
534     s = format (s, ", sibling-of %s", n->sibling_of);
535
536   vlib_cli_output (vm, "node %s, type %s, state %U, index %d%v\n",
537                    n->name, type_str, format_vlib_node_state, vm, n,
538                    n->index, s);
539   vec_reset_length (s);
540
541   if (n->node_fn_registrations)
542     {
543       vlib_node_fn_registration_t *fnr = n->node_fn_registrations;
544       while (fnr)
545         {
546           if (vec_len (s) == 0)
547             s = format (s, "\n    %-15s  %=8s  %6s",
548                         "Name", "Priority", "Active");
549           s = format (s, "\n    %-15s  %=8u  %=6s", fnr->name, fnr->priority,
550                       fnr->function == n->function ? "yes" : "");
551           fnr = fnr->next_registration;
552         }
553     }
554   else
555     s = format (s, "\n    default only");
556   vlib_cli_output (vm, "  node function variants:%v\n", s);
557   vec_reset_length (s);
558
559   for (i = 0; i < vec_len (n->next_nodes); i++)
560     {
561       vlib_node_t *pn;
562       if (n->next_nodes[i] == VLIB_INVALID_NODE_INDEX)
563         continue;
564
565       pn = vec_elt (nm->nodes, n->next_nodes[i]);
566
567       if (vec_len (s) == 0)
568         s = format (s, "\n    %10s  %10s  %=30s %8s",
569                     "next-index", "node-index", "Node", "Vectors");
570
571       s = format (s, "\n    %=10u  %=10u  %-30v %=8llu", i, n->next_nodes[i],
572                   pn->name, vec_elt (n->n_vectors_by_next_node, i));
573     }
574
575   if (vec_len (s) == 0)
576     s = format (s, "\n    none");
577   vlib_cli_output (vm, "\n  next nodes:%v\n", s);
578   vec_reset_length (s);
579
580   if (n->type == VLIB_NODE_TYPE_INTERNAL)
581     {
582       int j = 0;
583       /* *INDENT-OFF* */
584       clib_bitmap_foreach (i, n->prev_node_bitmap, ({
585             vlib_node_t *pn = vlib_get_node (vm, i);
586             if (j++ % 3 == 0)
587               s = format (s, "\n    ");
588             s2 = format (s2, "%v (%u)", pn->name, i);
589             s = format (s, "%-35v", s2);
590             vec_reset_length (s2);
591           }));
592       /* *INDENT-ON* */
593
594       if (vec_len (s) == 0)
595         s = format (s, "\n    none");
596       vlib_cli_output (vm, "\n  known previous nodes:%v\n", s);
597       vec_reset_length (s);
598     }
599
600   vec_free (s);
601   vec_free (s2);
602   return 0;
603 }
604
605 /* *INDENT-OFF* */
606 VLIB_CLI_COMMAND (show_node_command, static) = {
607   .path = "show node",
608   .short_help = "show node [index] <node-name | node-index>",
609   .function = show_node,
610 };
611
612 static clib_error_t *
613 set_node_fn(vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
614 {
615   unformat_input_t _line_input, *line_input = &_line_input;
616   u32 node_index;
617   vlib_node_t *n;
618   clib_error_t *err = 0;
619   vlib_node_fn_registration_t *fnr;
620   u8 *variant = 0;
621
622   if (!unformat_user (input, unformat_line_input, line_input))
623     return 0;
624
625   if (!unformat (line_input, "%U", unformat_vlib_node, vm, &node_index))
626     {
627       err = clib_error_return (0, "please specify valid node name");
628       goto done;
629     }
630
631   if (!unformat (line_input, "%s", &variant))
632     {
633       err = clib_error_return (0, "please specify node functional variant");
634       goto done;
635     }
636
637   n = vlib_get_node (vm, node_index);
638
639   if (n->node_fn_registrations == 0)
640     {
641       err = clib_error_return (0, "node doesn't have functional variants");
642       goto done;
643     }
644
645   fnr = n->node_fn_registrations;
646   vec_add1 (variant, 0);
647
648   while (fnr)
649     {
650       if (!strncmp (fnr->name, (char *) variant, vec_len (variant) - 1))
651         {
652           int i;
653
654           n->function = fnr->function;
655
656           for (i = 0; i < vec_len (vlib_mains); i++)
657             {
658               vlib_node_runtime_t *nrt;
659               nrt = vlib_node_get_runtime (vlib_mains[i], n->index);
660               nrt->function = fnr->function;
661             }
662           goto done;
663         }
664       fnr = fnr->next_registration;
665     }
666
667   err = clib_error_return (0, "node functional variant '%s' not found", variant);
668
669 done:
670   vec_free (variant);
671   unformat_free (line_input);
672   return err;
673 }
674
675 /* *INDENT-OFF* */
676 VLIB_CLI_COMMAND (set_node_fn_command, static) = {
677   .path = "set node function",
678   .short_help = "set node function <node-name> <variant-name>",
679   .function = set_node_fn,
680 };
681 /* *INDENT-ON* */
682
683 /* Dummy function to get us linked in. */
684 void
685 vlib_node_cli_reference (void)
686 {
687 }
688
689 /*
690  * fd.io coding-style-patch-verification: ON
691  *
692  * Local Variables:
693  * eval: (c-set-style "gnu")
694  * End:
695  */