perfmon: bundles with multiple types
[vpp.git] / src / plugins / perfmon / cli.c
1 /*
2  * Copyright (c) 2020 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 #include <vnet/vnet.h>
17 #include <perfmon/perfmon.h>
18 #include <vppinfra/format_table.h>
19
20 uword
21 unformat_perfmon_bundle_name (unformat_input_t *input, va_list *args)
22 {
23   perfmon_main_t *pm = &perfmon_main;
24   perfmon_bundle_t **b = va_arg (*args, perfmon_bundle_t **);
25   uword *p;
26   u8 *str = 0;
27
28   if (unformat (input, "%s", &str) == 0)
29     return 0;
30
31   p = hash_get_mem (pm->bundle_by_name, str);
32
33   if (p)
34     b[0] = (perfmon_bundle_t *) p[0];
35
36   vec_free (str);
37   return p ? 1 : 0;
38 }
39
40 uword
41 unformat_perfmon_active_type (unformat_input_t *input, va_list *args)
42 {
43   perfmon_bundle_t *b = va_arg (*args, perfmon_bundle_t *);
44   perfmon_bundle_type_t *bundle_type = va_arg (*args, perfmon_bundle_type_t *);
45   char *str = 0;
46
47   char *_str_types[PERFMON_BUNDLE_TYPE_MAX];
48
49 #define _(type, pstr) _str_types[type] = (char *) pstr;
50
51   foreach_perfmon_bundle_type
52 #undef _
53
54     if (!b) return 0;
55
56   if (unformat (input, "%s", &str) == 0)
57     return 0;
58
59   for (int i = PERFMON_BUNDLE_TYPE_NODE; i < PERFMON_BUNDLE_TYPE_MAX; i++)
60     {
61       /* match the name and confirm it is available on this cpu */
62       if (strncmp (str, _str_types[i], strlen (_str_types[i])) == 0 &&
63           (b->type_flags & 1 << i))
64         {
65           *bundle_type = i;
66           break;
67         }
68     }
69
70   vec_free (str);
71   return bundle_type ? 1 : 0;
72 }
73
74 uword
75 unformat_perfmon_source_name (unformat_input_t *input, va_list *args)
76 {
77   perfmon_main_t *pm = &perfmon_main;
78   perfmon_source_t **b = va_arg (*args, perfmon_source_t **);
79   uword *p;
80   u8 *str = 0;
81
82   if (unformat (input, "%s", &str) == 0)
83     return 0;
84
85   p = hash_get_mem (pm->source_by_name, str);
86
87   if (p)
88     b[0] = (perfmon_source_t *) p[0];
89
90   vec_free (str);
91   return p ? 1 : 0;
92 }
93
94 u8 *
95 format_perfmon_bundle (u8 *s, va_list *args)
96 {
97   perfmon_bundle_t *b = va_arg (*args, perfmon_bundle_t *);
98   int verbose = va_arg (*args, int);
99
100   int vl = 0;
101
102   u8 *_bundle_type = 0;
103   const char *bundle_type[PERFMON_BUNDLE_TYPE_MAX];
104 #define _(type, pstr) bundle_type[type] = (const char *) pstr;
105
106   foreach_perfmon_bundle_type
107 #undef _
108
109     if (b == 0) return format (s, "%-20s%-20s%-20s%s", "Name", "Type(s)",
110                                "Source", "Description");
111
112   if (verbose)
113     {
114       s = format (s, "name: %s\n", b->name);
115       s = format (s, "description: %s\n", b->description);
116       s = format (s, "source: %s\n", b->src->name);
117       for (int i = 0; i < b->n_events; i++)
118         {
119           perfmon_event_t *e = b->src->events + b->events[i];
120           s = format (s, "event %u: %s\n", i, e->name);
121         }
122     }
123   else
124     {
125       s = format (s, "%-20s", b->name);
126       for (int i = PERFMON_BUNDLE_TYPE_NODE; i < PERFMON_BUNDLE_TYPE_MAX; i++)
127         {
128           /* check the type is available on this uarch*/
129           if (b->type_flags & 1 << i)
130             _bundle_type = format (_bundle_type, "%s,", bundle_type[i]);
131         }
132       /* remove any stray commas */
133       if ((vl = vec_len (_bundle_type)))
134         _bundle_type[vl - 1] = 0;
135
136       s =
137         format (s, "%-20s%-20s%s", _bundle_type, b->src->name, b->description);
138     }
139
140   vec_free (_bundle_type);
141
142   return s;
143 }
144
145 static int
146 bundle_name_sort_cmp (void *a1, void *a2)
147 {
148   perfmon_bundle_t **n1 = a1;
149   perfmon_bundle_t **n2 = a2;
150
151   return clib_strcmp ((char *) (*n1)->name, (char *) (*n2)->name);
152 }
153
154 static clib_error_t *
155 show_perfmon_bundle_command_fn (vlib_main_t *vm, unformat_input_t *input,
156                                 vlib_cli_command_t *cmd)
157 {
158   perfmon_main_t *pm = &perfmon_main;
159   unformat_input_t _line_input, *line_input = &_line_input;
160   perfmon_bundle_t *b = 0, **vb = 0;
161   int verbose = 0;
162
163   if (unformat_user (input, unformat_line_input, line_input))
164     {
165       while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
166         {
167           if (unformat (line_input, "verbose"))
168             verbose = 1;
169           else if (unformat (line_input, "%U", unformat_perfmon_bundle_name,
170                              &b))
171             vec_add (vb, &b, 1);
172           else
173             return clib_error_return (0, "unknown input `%U'",
174                                       format_unformat_error, line_input);
175         }
176       unformat_free (line_input);
177     }
178
179   if (vb == 0)
180     {
181       char *key;
182       hash_foreach_mem (key, b, pm->bundle_by_name, vec_add (vb, &b, 1););
183     }
184   else
185     verbose = 1;
186
187   if (verbose == 0)
188     vlib_cli_output (vm, "%U\n", format_perfmon_bundle, 0, 0);
189
190   vec_sort_with_function (vb, bundle_name_sort_cmp);
191
192   for (int i = 0; i < vec_len (vb); i++)
193     /* bundle type will be unknown if no cpu_supports matched */
194     if (vb[i]->type_flags)
195       vlib_cli_output (vm, "%U\n", format_perfmon_bundle, vb[i], verbose);
196
197   vec_free (vb);
198   return 0;
199 }
200
201 VLIB_CLI_COMMAND (show_perfmon_bundle_command, static) = {
202   .path = "show perfmon bundle",
203   .short_help = "show perfmon bundle [<bundle-name>] [verbose]",
204   .function = show_perfmon_bundle_command_fn,
205   .is_mp_safe = 1,
206 };
207
208 u8 *
209 format_perfmon_source (u8 *s, va_list *args)
210 {
211   perfmon_source_t *src = va_arg (*args, perfmon_source_t *);
212   int verbose = va_arg (*args, int);
213
214   if (src == 0)
215     return format (s, "%-20s%-9s %s", "Name", "NumEvents", "Description");
216
217   if (verbose)
218     {
219       s = format (s, "name:        %s\n", src->name);
220       s = format (s, "description: %s\n", src->description);
221       s = format (s, "Events:\n");
222       for (int i = 0; i < src->n_events; i++)
223         {
224           perfmon_event_t *e = src->events + i;
225           s = format (s, "  %s", e->name);
226           if (src->format_config)
227             s = format (s, " (%U)\n", src->format_config, e->config);
228           else
229             s = format (s, " (0x%x)\n", e->config);
230           if (e->description)
231             s = format (s, "    %s\n", e->description);
232         }
233
234       if (src->instances_by_type)
235         {
236           s = format (s, "Instances:\n");
237           for (int i = 0; i < vec_len (src->instances_by_type); i++)
238             {
239               perfmon_instance_type_t *it;
240               it = vec_elt_at_index (src->instances_by_type, i);
241               if (vec_len (it->instances) == 0)
242                 continue;
243               s = format (s, "  %s:\n   ", it->name);
244               for (int j = 0; j < vec_len (it->instances); j++)
245                 {
246                   perfmon_instance_t *in = vec_elt_at_index (it->instances, j);
247                   s = format (s, " %s", in->name);
248                 }
249               s = format (s, "\n");
250             }
251         }
252     }
253   else
254     s = format (s, "%-20s%9u %s", src->name, src->n_events, src->description);
255
256   return s;
257 }
258
259 static clib_error_t *
260 show_perfmon_source_command_fn (vlib_main_t *vm, unformat_input_t *input,
261                                 vlib_cli_command_t *cmd)
262 {
263   perfmon_main_t *pm = &perfmon_main;
264   unformat_input_t _line_input, *line_input = &_line_input;
265   perfmon_source_t *s = 0, **vs = 0;
266   int verbose = 0;
267
268   if (unformat_user (input, unformat_line_input, line_input))
269     {
270       while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
271         {
272           if (unformat (line_input, "verbose"))
273             verbose = 1;
274           else if (unformat (line_input, "%U", unformat_perfmon_source_name,
275                              &s))
276             vec_add (vs, &s, 1);
277           else
278             return clib_error_return (0, "unknown input `%U'",
279                                       format_unformat_error, line_input);
280         }
281       unformat_free (line_input);
282     }
283
284   if (vs == 0)
285     {
286       char *key;
287       hash_foreach_mem (key, s, pm->source_by_name, vec_add (vs, &s, 1););
288     }
289   else
290     verbose = 1;
291
292   if (verbose == 0)
293     vlib_cli_output (vm, "%U\n", format_perfmon_source, 0, 0);
294
295   for (int i = 0; i < vec_len (vs); i++)
296     vlib_cli_output (vm, "%U\n", format_perfmon_source, vs[i], verbose);
297
298   vec_free (vs);
299   return 0;
300 }
301
302 VLIB_CLI_COMMAND (show_perfmon_source_command, static) = {
303   .path = "show perfmon source",
304   .short_help = "show perfmon source [<source-name>] [verbose]",
305   .function = show_perfmon_source_command_fn,
306   .is_mp_safe = 1,
307 };
308
309 static clib_error_t *
310 show_perfmon_active_bundle_command_fn (vlib_main_t *vm,
311                                        unformat_input_t *input,
312                                        vlib_cli_command_t *cmd)
313 {
314   perfmon_main_t *pm = &perfmon_main;
315
316   vlib_cli_output (vm, "%U\n", format_perfmon_bundle, pm->active_bundle, 1);
317   return 0;
318 }
319
320 VLIB_CLI_COMMAND (show_perfmon_active_bundle_command, static) = {
321   .path = "show perfmon active-bundle",
322   .short_help = "show perfmon active-bundle",
323   .function = show_perfmon_active_bundle_command_fn,
324   .is_mp_safe = 1,
325 };
326
327 static clib_error_t *
328 show_perfmon_stats_command_fn (vlib_main_t *vm, unformat_input_t *input,
329                                vlib_cli_command_t *cmd)
330 {
331   perfmon_main_t *pm = &perfmon_main;
332   perfmon_bundle_t *b = pm->active_bundle;
333   clib_error_t *err = 0;
334   table_t table = {}, *t = &table;
335   u32 n_instances;
336   perfmon_reading_t *r, *readings = 0;
337   perfmon_instance_type_t *it = pm->active_instance_type;
338   perfmon_instance_t *in;
339   u8 *s = 0;
340   int n_row = 0;
341
342   if (b == 0)
343     return clib_error_return (0, "no bundle selected");
344
345   n_instances = vec_len (it->instances);
346   vec_validate (readings, n_instances - 1);
347
348   /*Only perform read() for THREAD or SYSTEM bundles*/
349   for (int i = 0;
350        i < n_instances && b->active_type != PERFMON_BUNDLE_TYPE_NODE; i++)
351     {
352       in = vec_elt_at_index (it->instances, i);
353       r = vec_elt_at_index (readings, i);
354
355       if (read (pm->group_fds[i], r, (b->n_events + 3) * sizeof (u64)) == -1)
356         {
357           err = clib_error_return_unix (0, "read");
358           goto done;
359         }
360     }
361
362   table_format_title (t, "%s", b->description);
363
364   table_add_header_col (t, 0);
365   table_add_header_row (t, 0);
366
367   if (b->column_headers)
368     {
369       char **hdr = b->column_headers;
370       while (hdr[0])
371         table_format_cell (t, -1, n_row++, "%s", hdr++[0]);
372     }
373
374   int col = 0;
375   for (int i = 0; i < n_instances; i++)
376     {
377       in = vec_elt_at_index (it->instances, i);
378       r = vec_elt_at_index (readings, i);
379       table_format_cell (t, col, -1, "%s", in->name, b->active_type);
380       if (b->active_type == PERFMON_BUNDLE_TYPE_NODE)
381         {
382           perfmon_thread_runtime_t *tr;
383           tr = vec_elt_at_index (pm->thread_runtimes, i);
384           for (int j = 0; j < tr->n_nodes; j++)
385             if (tr->node_stats[j].n_calls)
386               {
387                 perfmon_node_stats_t ns;
388                 table_format_cell (t, ++col, -1, "%U", format_vlib_node_name,
389                                    vm, j, b->active_type);
390                 table_set_cell_align (t, col, -1, TTAA_RIGHT);
391                 table_set_cell_fg_color (t, col, -1, TTAC_CYAN);
392                 clib_memcpy_fast (&ns, tr->node_stats + j, sizeof (ns));
393
394                 for (int j = 0; j < n_row; j++)
395                   table_format_cell (t, col, j, "%U", b->format_fn, &ns, j,
396                                      b->active_type);
397               }
398         }
399       else
400         {
401           for (int j = 0; j < n_row; j++)
402             table_format_cell (t, i, j, "%U", b->format_fn, r, j,
403                                b->active_type);
404         }
405       col++;
406     }
407
408   vlib_cli_output (vm, "%U\n", format_table, t);
409   table_free (t);
410
411   if (b->footer)
412     vlib_cli_output (vm, "\n%s\n", b->footer);
413
414 done:
415   vec_free (readings);
416   vec_free (s);
417   return err;
418 }
419
420 VLIB_CLI_COMMAND (show_perfmon_stats_command, static) = {
421   .path = "show perfmon statistics",
422   .short_help = "show perfmon statistics [raw]",
423   .function = show_perfmon_stats_command_fn,
424   .is_mp_safe = 1,
425 };
426
427 static clib_error_t *
428 perfmon_reset_command_fn (vlib_main_t *vm, unformat_input_t *input,
429                           vlib_cli_command_t *cmd)
430 {
431   perfmon_reset (vm);
432   return 0;
433 }
434
435 VLIB_CLI_COMMAND (perfmon_reset_command, static) = {
436   .path = "perfmon reset",
437   .short_help = "perfmon reset",
438   .function = perfmon_reset_command_fn,
439   .is_mp_safe = 1,
440 };
441
442 static clib_error_t *
443 perfmon_start_command_fn (vlib_main_t *vm, unformat_input_t *input,
444                           vlib_cli_command_t *cmd)
445 {
446   perfmon_main_t *pm = &perfmon_main;
447   unformat_input_t _line_input, *line_input = &_line_input;
448   perfmon_bundle_t *b = 0;
449   perfmon_bundle_type_t bundle_type = PERFMON_BUNDLE_TYPE_UNKNOWN;
450
451   if (pm->is_running)
452     return clib_error_return (0, "please stop first");
453
454   if (unformat_user (input, unformat_line_input, line_input) == 0)
455     return clib_error_return (0, "please specify bundle name");
456
457   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
458     {
459       if (unformat (line_input, "bundle %U", unformat_perfmon_bundle_name, &b))
460         ;
461       else if (unformat (line_input, "type %U", unformat_perfmon_active_type,
462                          b, &bundle_type))
463         ;
464       else
465         return clib_error_return (0, "unknown input '%U'",
466                                   format_unformat_error, line_input);
467     }
468   unformat_free (line_input);
469
470   if (b == 0)
471     return clib_error_return (0, "please specify bundle name");
472
473   /* if there is more than one valid mode */
474   if (count_set_bits (b->type_flags) > 1)
475     {
476       /* what did the user indicate */
477       if (!bundle_type)
478         return clib_error_return (0, "please specify a valid type");
479     }
480   else /* otherwise just use the default  */
481     {
482       if (bundle_type && !(b->type_flags & bundle_type))
483         return clib_error_return (0, "please specify a valid type");
484
485       bundle_type =
486         (perfmon_bundle_type_t) count_trailing_zeros (b->type_flags);
487     }
488
489   b->active_type = bundle_type;
490
491   return perfmon_start (vm, b);
492 }
493
494 VLIB_CLI_COMMAND (perfmon_start_command, static) = {
495   .path = "perfmon start",
496   .short_help = "perfmon start bundle [<bundle-name>] type [<node|thread>]",
497   .function = perfmon_start_command_fn,
498   .is_mp_safe = 1,
499 };
500
501 static clib_error_t *
502 perfmon_stop_command_fn (vlib_main_t *vm, unformat_input_t *input,
503                          vlib_cli_command_t *cmd)
504 {
505   return perfmon_stop (vm);
506 }
507
508 VLIB_CLI_COMMAND (perfmon_stop_command, static) = {
509   .path = "perfmon stop",
510   .short_help = "perfmon stop",
511   .function = perfmon_stop_command_fn,
512   .is_mp_safe = 1,
513 };