blob: 5a06c654473e7f71870f150fa3388f25e842f8e4 [file] [log] [blame]
Florin Corase86a8ed2018-01-05 03:20:25 -08001/*
2 *------------------------------------------------------------------
3 * Copyright (c) 2018 Cisco and/or its affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *------------------------------------------------------------------
16 */
17
18#include <fcntl.h>
19#include <unistd.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22
23#include <vlibapi/api.h>
24#include <vlibmemory/api.h>
25
26static clib_error_t *
27vl_api_show_histogram_command (vlib_main_t * vm,
28 unformat_input_t * input,
29 vlib_cli_command_t * cli_cmd)
30{
31 u64 total_counts = 0;
32 int i;
33
34 for (i = 0; i < SLEEP_N_BUCKETS; i++)
35 {
36 total_counts += vector_rate_histogram[i];
37 }
38
39 if (total_counts == 0)
40 {
41 vlib_cli_output (vm, "No control-plane activity.");
42 return 0;
43 }
44
45#define _(n) \
46 do { \
47 f64 percent; \
48 percent = ((f64) vector_rate_histogram[SLEEP_##n##_US]) \
49 / (f64) total_counts; \
50 percent *= 100.0; \
51 vlib_cli_output (vm, "Sleep %3d us: %llu, %.2f%%",n, \
52 vector_rate_histogram[SLEEP_##n##_US], \
53 percent); \
54 } while (0);
55 foreach_histogram_bucket;
56#undef _
57
58 return 0;
59}
60
61/*?
62 * Display the binary api sleep-time histogram
63?*/
64/* *INDENT-OFF* */
65VLIB_CLI_COMMAND (cli_show_api_histogram_command, static) =
66{
67 .path = "show api histogram",
68 .short_help = "show api histogram",
69 .function = vl_api_show_histogram_command,
70};
71/* *INDENT-ON* */
72
73static clib_error_t *
74vl_api_clear_histogram_command (vlib_main_t * vm,
75 unformat_input_t * input,
76 vlib_cli_command_t * cli_cmd)
77{
78 int i;
79
80 for (i = 0; i < SLEEP_N_BUCKETS; i++)
81 vector_rate_histogram[i] = 0;
82 return 0;
83}
84
85/*?
86 * Clear the binary api sleep-time histogram
87?*/
88/* *INDENT-OFF* */
89VLIB_CLI_COMMAND (cli_clear_api_histogram_command, static) =
90{
91 .path = "clear api histogram",
92 .short_help = "clear api histogram",
93 .function = vl_api_clear_histogram_command,
94};
95/* *INDENT-ON* */
96
97static clib_error_t *
98vl_api_client_command (vlib_main_t * vm,
99 unformat_input_t * input, vlib_cli_command_t * cli_cmd)
100{
101 vl_api_registration_t **regpp, *regp;
102 svm_queue_t *q;
103 char *health;
Dave Barach39d69112019-11-27 11:42:13 -0500104 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800105 u32 *confused_indices = 0;
106
107 if (!pool_elts (am->vl_clients))
108 goto socket_clients;
109 vlib_cli_output (vm, "Shared memory clients");
110 vlib_cli_output (vm, "%20s %8s %14s %18s %s",
111 "Name", "PID", "Queue Length", "Queue VA", "Health");
112
113 /* *INDENT-OFF* */
114 pool_foreach (regpp, am->vl_clients,
115 ({
116 regp = *regpp;
117
118 if (regp)
119 {
120 if (regp->unanswered_pings > 0)
121 health = "questionable";
122 else
123 health = "OK";
124
125 q = regp->vl_input_queue;
126
127 vlib_cli_output (vm, "%20s %8d %14d 0x%016llx %s\n",
128 regp->name, q->consumer_pid, q->cursize,
129 q, health);
130 }
131 else
132 {
133 clib_warning ("NULL client registration index %d",
134 regpp - am->vl_clients);
135 vec_add1 (confused_indices, regpp - am->vl_clients);
136 }
137 }));
138 /* *INDENT-ON* */
139
140 /* This should "never happen," but if it does, fix it... */
141 if (PREDICT_FALSE (vec_len (confused_indices) > 0))
142 {
143 int i;
144 for (i = 0; i < vec_len (confused_indices); i++)
145 {
146 pool_put_index (am->vl_clients, confused_indices[i]);
147 }
148 }
149 vec_free (confused_indices);
150
151 if (am->missing_clients)
152 vlib_cli_output (vm, "%u messages with missing clients",
153 am->missing_clients);
154socket_clients:
155 vl_sock_api_dump_clients (vm, am);
156
157 return 0;
158}
159
160static clib_error_t *
161vl_api_status_command (vlib_main_t * vm,
162 unformat_input_t * input, vlib_cli_command_t * cli_cmd)
163{
Dave Barach39d69112019-11-27 11:42:13 -0500164 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800165
166 /* check if rx_trace and tx_trace are not null pointers */
167 if (am->rx_trace == 0)
168 {
169 vlib_cli_output (vm, "RX Trace disabled\n");
170 }
171 else
172 {
173 if (am->rx_trace->enabled == 0)
174 vlib_cli_output (vm, "RX Trace disabled\n");
175 else
176 vlib_cli_output (vm, "RX Trace enabled\n");
177 }
178
179 if (am->tx_trace == 0)
180 {
181 vlib_cli_output (vm, "TX Trace disabled\n");
182 }
183 else
184 {
185 if (am->tx_trace->enabled == 0)
186 vlib_cli_output (vm, "TX Trace disabled\n");
187 else
188 vlib_cli_output (vm, "TX Trace enabled\n");
189 }
190
191 return 0;
192}
193
194/* *INDENT-OFF* */
195VLIB_CLI_COMMAND (cli_show_api_command, static) =
196{
197 .path = "show api",
198 .short_help = "Show API information",
199};
200/* *INDENT-ON* */
201
202/*?
203 * Display current api client connections
204?*/
205/* *INDENT-OFF* */
206VLIB_CLI_COMMAND (cli_show_api_clients_command, static) =
207{
208 .path = "show api clients",
209 .short_help = "Client information",
210 .function = vl_api_client_command,
211};
212/* *INDENT-ON* */
213
214/*?
215 * Display the current api message tracing status
216?*/
217/* *INDENT-OFF* */
218VLIB_CLI_COMMAND (cli_show_api_status_command, static) =
219{
220 .path = "show api trace-status",
221 .short_help = "Display API trace status",
222 .function = vl_api_status_command,
223};
224/* *INDENT-ON* */
225
226static clib_error_t *
227vl_api_message_table_command (vlib_main_t * vm,
228 unformat_input_t * input,
229 vlib_cli_command_t * cli_cmd)
230{
Dave Barach39d69112019-11-27 11:42:13 -0500231 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800232 int i;
233 int verbose = 0;
234
235 if (unformat (input, "verbose"))
236 verbose = 1;
237
238
239 if (verbose == 0)
240 vlib_cli_output (vm, "%-4s %s", "ID", "Name");
241 else
242 vlib_cli_output (vm, "%-4s %-40s %6s %7s", "ID", "Name", "Bounce",
243 "MP-safe");
244
245 for (i = 1; i < vec_len (am->msg_names); i++)
246 {
247 if (verbose == 0)
248 {
249 vlib_cli_output (vm, "%-4d %s", i,
250 am->msg_names[i] ? am->msg_names[i] :
251 " [no handler]");
252 }
253 else
254 {
255 vlib_cli_output (vm, "%-4d %-40s %6d %7d", i,
256 am->msg_names[i] ? am->msg_names[i] :
257 " [no handler]", am->message_bounce[i],
258 am->is_mp_safe[i]);
259 }
260 }
261
262 return 0;
263}
264
265/*?
266 * Display the current api message decode tables
267?*/
268/* *INDENT-OFF* */
269VLIB_CLI_COMMAND (cli_show_api_message_table_command, static) =
270{
271 .path = "show api message-table",
272 .short_help = "Message Table",
273 .function = vl_api_message_table_command,
274};
275/* *INDENT-ON* */
276
277static int
278range_compare (vl_api_msg_range_t * a0, vl_api_msg_range_t * a1)
279{
280 int len0, len1, clen;
281
282 len0 = vec_len (a0->name);
283 len1 = vec_len (a1->name);
284 clen = len0 < len1 ? len0 : len1;
285 return (strncmp ((char *) a0->name, (char *) a1->name, clen));
286}
287
288static u8 *
289format_api_msg_range (u8 * s, va_list * args)
290{
291 vl_api_msg_range_t *rp = va_arg (*args, vl_api_msg_range_t *);
292
293 if (rp == 0)
294 s = format (s, "%-50s%9s%9s", "Name", "First-ID", "Last-ID");
295 else
296 s = format (s, "%-50s%9d%9d", rp->name, rp->first_msg_id,
297 rp->last_msg_id);
298
299 return s;
300}
301
302static clib_error_t *
303vl_api_show_plugin_command (vlib_main_t * vm,
304 unformat_input_t * input,
305 vlib_cli_command_t * cli_cmd)
306{
Dave Barach39d69112019-11-27 11:42:13 -0500307 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800308 vl_api_msg_range_t *rp = 0;
309 int i;
310
311 if (vec_len (am->msg_ranges) == 0)
312 {
313 vlib_cli_output (vm, "No plugin API message ranges configured...");
314 return 0;
315 }
316
317 rp = vec_dup (am->msg_ranges);
318
319 vec_sort_with_function (rp, range_compare);
320
321 vlib_cli_output (vm, "Plugin API message ID ranges...\n");
322 vlib_cli_output (vm, "%U", format_api_msg_range, 0 /* header */ );
323
324 for (i = 0; i < vec_len (rp); i++)
325 vlib_cli_output (vm, "%U", format_api_msg_range, rp + i);
326
327 vec_free (rp);
328
329 return 0;
330}
331
332/*?
333 * Display the plugin binary API message range table
334?*/
335/* *INDENT-OFF* */
336VLIB_CLI_COMMAND (cli_show_api_plugin_command, static) =
337{
338 .path = "show api plugin",
339 .short_help = "show api plugin",
340 .function = vl_api_show_plugin_command,
341};
342/* *INDENT-ON* */
343
344typedef enum
345{
346 DUMP,
347 CUSTOM_DUMP,
348 REPLAY,
349 INITIALIZERS,
350} vl_api_replay_t;
351
352u8 *
353format_vl_msg_api_trace_status (u8 * s, va_list * args)
354{
355 api_main_t *am = va_arg (*args, api_main_t *);
356 vl_api_trace_which_t which = va_arg (*args, vl_api_trace_which_t);
357 vl_api_trace_t *tp;
358 char *trace_name;
359
360 switch (which)
361 {
362 case VL_API_TRACE_TX:
363 tp = am->tx_trace;
364 trace_name = "TX trace";
365 break;
366
367 case VL_API_TRACE_RX:
368 tp = am->rx_trace;
369 trace_name = "RX trace";
370 break;
371
372 default:
373 abort ();
374 }
375
376 if (tp == 0)
377 {
378 s = format (s, "%s: not yet configured.\n", trace_name);
379 return s;
380 }
381
382 s = format (s, "%s: used %d of %d items, %s enabled, %s wrapped\n",
383 trace_name, vec_len (tp->traces), tp->nitems,
384 tp->enabled ? "is" : "is not", tp->wrapped ? "has" : "has not");
385 return s;
386}
387
388void vl_msg_api_custom_dump_configure (api_main_t * am)
389 __attribute__ ((weak));
390void
391vl_msg_api_custom_dump_configure (api_main_t * am)
392{
393}
394
395static void
396vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
397 u32 first_index, u32 last_index,
398 vl_api_replay_t which)
399{
400 vl_api_trace_file_header_t *hp;
401 int i, fd;
402 struct stat statb;
403 size_t file_size;
404 u8 *msg;
Dave Barach39d69112019-11-27 11:42:13 -0500405 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800406 u8 *tmpbuf = 0;
Ole Troanedfe2c02019-07-30 15:38:13 +0200407 u32 nitems, nitems_msgtbl;
Florin Corase86a8ed2018-01-05 03:20:25 -0800408 void **saved_print_handlers = 0;
409
410 fd = open ((char *) filename, O_RDONLY);
411
412 if (fd < 0)
413 {
414 vlib_cli_output (vm, "Couldn't open %s\n", filename);
415 return;
416 }
417
418 if (fstat (fd, &statb) < 0)
419 {
420 vlib_cli_output (vm, "Couldn't stat %s\n", filename);
421 close (fd);
422 return;
423 }
424
425 if (!(statb.st_mode & S_IFREG) || (statb.st_size < sizeof (*hp)))
426 {
427 vlib_cli_output (vm, "File not plausible: %s\n", filename);
428 close (fd);
429 return;
430 }
431
432 file_size = statb.st_size;
Benoît Ganneaba49832020-01-21 18:35:49 +0100433 file_size = (file_size + 4095) & ~(4095);
Florin Corase86a8ed2018-01-05 03:20:25 -0800434
435 hp = mmap (0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
436
437 if (hp == (vl_api_trace_file_header_t *) MAP_FAILED)
438 {
439 vlib_cli_output (vm, "mmap failed: %s\n", filename);
440 close (fd);
441 return;
442 }
443 close (fd);
444
Benoît Ganneb2f09142019-12-16 15:37:28 +0100445 CLIB_MEM_UNPOISON (hp, file_size);
446
Ole Troanedfe2c02019-07-30 15:38:13 +0200447 nitems = ntohl (hp->nitems);
Florin Corase86a8ed2018-01-05 03:20:25 -0800448
449 if (last_index == (u32) ~ 0)
450 {
451 last_index = nitems - 1;
452 }
453
454 if (first_index >= nitems || last_index >= nitems)
455 {
456 vlib_cli_output (vm, "Range (%d, %d) outside file range (0, %d)\n",
457 first_index, last_index, nitems - 1);
458 munmap (hp, file_size);
459 return;
460 }
461 if (hp->wrapped)
462 vlib_cli_output (vm,
463 "Note: wrapped/incomplete trace, results may vary\n");
464
465 if (which == CUSTOM_DUMP)
466 {
467 saved_print_handlers = (void **) vec_dup (am->msg_print_handlers);
468 vl_msg_api_custom_dump_configure (am);
469 }
Benoît Ganneb2f09142019-12-16 15:37:28 +0100470
Florin Corase86a8ed2018-01-05 03:20:25 -0800471 msg = (u8 *) (hp + 1);
472
Ole Troanedfe2c02019-07-30 15:38:13 +0200473 u16 *msgid_vec = 0;
474 serialize_main_t _sm, *sm = &_sm;
475 u32 msgtbl_size = ntohl (hp->msgtbl_size);
476 u8 *name_and_crc;
477
478 unserialize_open_data (sm, msg, msgtbl_size);
479 unserialize_integer (sm, &nitems_msgtbl, sizeof (u32));
480
481 for (i = 0; i < nitems_msgtbl; i++)
482 {
483 u16 msg_index = unserialize_likely_small_unsigned_integer (sm);
484 unserialize_cstring (sm, (char **) &name_and_crc);
485 u16 msg_index2 = vl_msg_api_get_msg_index (name_and_crc);
486 vec_validate (msgid_vec, msg_index);
487 msgid_vec[msg_index] = msg_index2;
488 }
489
490 msg += msgtbl_size;
491
Florin Corase86a8ed2018-01-05 03:20:25 -0800492 for (i = 0; i < first_index; i++)
493 {
494 trace_cfg_t *cfgp;
495 int size;
496 u16 msg_id;
497
498 size = clib_host_to_net_u32 (*(u32 *) msg);
499 msg += sizeof (u32);
500
Ole Troanedfe2c02019-07-30 15:38:13 +0200501 msg_id = ntohs (*((u16 *) msg));
502 if (msg_id < vec_len (msgid_vec))
503 msg_id = msgid_vec[msg_id];
Florin Corase86a8ed2018-01-05 03:20:25 -0800504 cfgp = am->api_trace_cfg + msg_id;
505 if (!cfgp)
506 {
507 vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
508 munmap (hp, file_size);
509 return;
510 }
511 msg += size;
512 }
513
514 if (which == REPLAY)
515 am->replay_in_progress = 1;
516
517 for (; i <= last_index; i++)
518 {
519 trace_cfg_t *cfgp;
Florin Corase86a8ed2018-01-05 03:20:25 -0800520 u16 msg_id;
521 int size;
522
523 if (which == DUMP)
524 vlib_cli_output (vm, "---------- trace %d -----------\n", i);
525
526 size = clib_host_to_net_u32 (*(u32 *) msg);
527 msg += sizeof (u32);
528
Ole Troanedfe2c02019-07-30 15:38:13 +0200529 msg_id = ntohs (*((u16 *) msg));
530 if (msg_id < vec_len (msgid_vec))
531 {
532 msg_id = msgid_vec[msg_id];
533 }
Florin Corase86a8ed2018-01-05 03:20:25 -0800534
535 cfgp = am->api_trace_cfg + msg_id;
536 if (!cfgp)
537 {
538 vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
539 munmap (hp, file_size);
540 vec_free (tmpbuf);
541 am->replay_in_progress = 0;
542 return;
543 }
544
545 /* Copy the buffer (from the read-only mmap'ed file) */
546 vec_validate (tmpbuf, size - 1 + sizeof (uword));
547 clib_memcpy (tmpbuf + sizeof (uword), msg, size);
Dave Barachb7b92992018-10-17 10:38:51 -0400548 clib_memset (tmpbuf, 0xf, sizeof (uword));
Florin Corase86a8ed2018-01-05 03:20:25 -0800549
550 /*
Ole Troanedfe2c02019-07-30 15:38:13 +0200551 * Endian swap if needed. All msg data is supposed to be in
552 * network byte order.
Florin Corase86a8ed2018-01-05 03:20:25 -0800553 */
Ole Troan33a58172019-09-04 09:12:29 +0200554 if (((which == DUMP || which == CUSTOM_DUMP)
555 && clib_arch_is_little_endian))
Florin Corase86a8ed2018-01-05 03:20:25 -0800556 {
557 void (*endian_fp) (void *);
558 if (msg_id >= vec_len (am->msg_endian_handlers)
559 || (am->msg_endian_handlers[msg_id] == 0))
560 {
561 vlib_cli_output (vm, "Ugh: msg id %d no endian swap\n", msg_id);
562 munmap (hp, file_size);
563 vec_free (tmpbuf);
564 am->replay_in_progress = 0;
565 return;
566 }
567 endian_fp = am->msg_endian_handlers[msg_id];
568 (*endian_fp) (tmpbuf + sizeof (uword));
569 }
570
571 /* msg_id always in network byte order */
572 if (clib_arch_is_little_endian)
573 {
Ole Troanedfe2c02019-07-30 15:38:13 +0200574 u16 *msg_idp = (u16 *) (tmpbuf + sizeof (uword));
Florin Corase86a8ed2018-01-05 03:20:25 -0800575 *msg_idp = msg_id;
576 }
577
578 switch (which)
579 {
580 case CUSTOM_DUMP:
581 case DUMP:
582 if (msg_id < vec_len (am->msg_print_handlers) &&
583 am->msg_print_handlers[msg_id])
584 {
585 u8 *(*print_fp) (void *, void *);
586
587 print_fp = (void *) am->msg_print_handlers[msg_id];
588 (*print_fp) (tmpbuf + sizeof (uword), vm);
589 }
590 else
591 {
592 vlib_cli_output (vm, "Skipping msg id %d: no print fcn\n",
593 msg_id);
594 break;
595 }
596 break;
597
598 case INITIALIZERS:
599 if (msg_id < vec_len (am->msg_print_handlers) &&
600 am->msg_print_handlers[msg_id])
601 {
602 u8 *s;
603 int j;
604 u8 *(*print_fp) (void *, void *);
605
606 print_fp = (void *) am->msg_print_handlers[msg_id];
607
608 vlib_cli_output (vm, "/*");
609
610 (*print_fp) (tmpbuf + sizeof (uword), vm);
611 vlib_cli_output (vm, "*/\n");
612
613 s = format (0, "static u8 * vl_api_%s_%d[%d] = {",
614 am->msg_names[msg_id], i,
615 am->api_trace_cfg[msg_id].size);
616
617 for (j = 0; j < am->api_trace_cfg[msg_id].size; j++)
618 {
619 if ((j & 7) == 0)
620 s = format (s, "\n ");
621 s = format (s, "0x%02x,", tmpbuf[sizeof (uword) + j]);
622 }
623 s = format (s, "\n};\n%c", 0);
624 vlib_cli_output (vm, (char *) s);
625 vec_free (s);
626 }
627 break;
628
629 case REPLAY:
630 if (msg_id < vec_len (am->msg_print_handlers) &&
631 am->msg_print_handlers[msg_id] && cfgp->replay_enable)
632 {
John Loa56f2702018-06-25 20:38:35 -0400633 void (*handler) (void *, vlib_main_t *);
Florin Corase86a8ed2018-01-05 03:20:25 -0800634
635 handler = (void *) am->msg_handlers[msg_id];
636
637 if (!am->is_mp_safe[msg_id])
638 vl_msg_api_barrier_sync ();
John Loa56f2702018-06-25 20:38:35 -0400639 (*handler) (tmpbuf + sizeof (uword), vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800640 if (!am->is_mp_safe[msg_id])
641 vl_msg_api_barrier_release ();
642 }
643 else
644 {
645 if (cfgp->replay_enable)
646 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
647 msg_id);
648 break;
649 }
650 break;
651 }
652
653 _vec_len (tmpbuf) = 0;
654 msg += size;
655 }
656
657 if (saved_print_handlers)
658 {
659 clib_memcpy (am->msg_print_handlers, saved_print_handlers,
660 vec_len (am->msg_print_handlers) * sizeof (void *));
661 vec_free (saved_print_handlers);
662 }
663
664 munmap (hp, file_size);
665 vec_free (tmpbuf);
666 am->replay_in_progress = 0;
667}
668
Dave Barachf66f8832020-01-23 08:44:40 -0500669/** api_trace_command_fn - control the binary API trace / replay feature
670
671 Note: this command MUST be marked thread-safe. Replay with
672 multiple worker threads depends in many cases on worker thread
673 graph replica maintenance. If we (implicitly) assert a worker
674 thread barrier at the debug CLI level, all graph replica changes
675 are deferred until the replay operation completes. If an interface
676 is deleted, the wheels fall off.
677 */
678
Florin Corase86a8ed2018-01-05 03:20:25 -0800679static clib_error_t *
680api_trace_command_fn (vlib_main_t * vm,
681 unformat_input_t * input, vlib_cli_command_t * cmd)
682{
Neale Rannsecff1cb2020-04-22 12:14:52 -0400683 unformat_input_t _line_input, *line_input = &_line_input;
Florin Corase86a8ed2018-01-05 03:20:25 -0800684 u32 nitems = 256 << 10;
Dave Barach39d69112019-11-27 11:42:13 -0500685 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800686 vl_api_trace_which_t which = VL_API_TRACE_RX;
Dave Barachf35a0722019-06-12 16:50:38 -0400687 u8 *filename = 0;
688 u8 *chroot_filename = 0;
Florin Corase86a8ed2018-01-05 03:20:25 -0800689 u32 first = 0;
690 u32 last = (u32) ~ 0;
691 FILE *fp;
692 int rv;
693
Neale Rannsecff1cb2020-04-22 12:14:52 -0400694 /* Get a line of input. */
695 if (!unformat_user (input, unformat_line_input, line_input))
696 return 0;
697
698 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
Florin Corase86a8ed2018-01-05 03:20:25 -0800699 {
Neale Rannsecff1cb2020-04-22 12:14:52 -0400700 if (unformat (line_input, "on") || unformat (line_input, "enable"))
Florin Corase86a8ed2018-01-05 03:20:25 -0800701 {
Neale Rannsecff1cb2020-04-22 12:14:52 -0400702 if (unformat (line_input, "nitems %d", &nitems))
Florin Corase86a8ed2018-01-05 03:20:25 -0800703 ;
Dave Barachf66f8832020-01-23 08:44:40 -0500704 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800705 vl_msg_api_trace_configure (am, which, nitems);
706 vl_msg_api_trace_onoff (am, which, 1 /* on */ );
Dave Barachf66f8832020-01-23 08:44:40 -0500707 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800708 }
Neale Rannsecff1cb2020-04-22 12:14:52 -0400709 else if (unformat (line_input, "off"))
Florin Corase86a8ed2018-01-05 03:20:25 -0800710 {
Dave Barachf66f8832020-01-23 08:44:40 -0500711 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800712 vl_msg_api_trace_onoff (am, which, 0);
Dave Barachf66f8832020-01-23 08:44:40 -0500713 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800714 }
Neale Rannsecff1cb2020-04-22 12:14:52 -0400715 else if (unformat (line_input, "save %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -0800716 {
Florin Corase86a8ed2018-01-05 03:20:25 -0800717 if (strstr ((char *) filename, "..")
718 || index ((char *) filename, '/'))
719 {
720 vlib_cli_output (vm, "illegal characters in filename '%s'",
721 filename);
Dave Barachf35a0722019-06-12 16:50:38 -0400722 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -0800723 }
724
725 chroot_filename = format (0, "/tmp/%s%c", filename, 0);
726
727 vec_free (filename);
728
729 fp = fopen ((char *) chroot_filename, "w");
730 if (fp == NULL)
731 {
732 vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
Dave Barachf35a0722019-06-12 16:50:38 -0400733 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -0800734 }
Dave Barachf66f8832020-01-23 08:44:40 -0500735 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800736 rv = vl_msg_api_trace_save (am, which, fp);
Dave Barachf66f8832020-01-23 08:44:40 -0500737 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800738 fclose (fp);
739 if (rv == -1)
740 vlib_cli_output (vm, "API Trace data not present\n");
741 else if (rv == -2)
742 vlib_cli_output (vm, "File for writing is closed\n");
743 else if (rv == -10)
744 vlib_cli_output (vm, "Error while writing header to file\n");
745 else if (rv == -11)
746 vlib_cli_output (vm, "Error while writing trace to file\n");
747 else if (rv == -12)
748 vlib_cli_output (vm,
749 "Error while writing end of buffer trace to file\n");
750 else if (rv == -13)
751 vlib_cli_output (vm,
752 "Error while writing start of buffer trace to file\n");
753 else if (rv < 0)
Andrey "Zed" Zaikin701625b2018-04-18 17:07:07 +0300754 vlib_cli_output (vm, "Unknown error while saving: %d", rv);
Florin Corase86a8ed2018-01-05 03:20:25 -0800755 else
756 vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
Dave Barachf35a0722019-06-12 16:50:38 -0400757 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -0800758 }
Neale Rannsecff1cb2020-04-22 12:14:52 -0400759 else if (unformat (line_input, "dump %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -0800760 {
761 vl_msg_api_process_file (vm, filename, first, last, DUMP);
762 }
Neale Rannsecff1cb2020-04-22 12:14:52 -0400763 else if (unformat (line_input, "custom-dump %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -0800764 {
765 vl_msg_api_process_file (vm, filename, first, last, CUSTOM_DUMP);
766 }
Neale Rannsecff1cb2020-04-22 12:14:52 -0400767 else if (unformat (line_input, "replay %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -0800768 {
769 vl_msg_api_process_file (vm, filename, first, last, REPLAY);
770 }
Neale Rannsecff1cb2020-04-22 12:14:52 -0400771 else if (unformat (line_input, "initializers %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -0800772 {
773 vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
774 }
Neale Rannsecff1cb2020-04-22 12:14:52 -0400775 else if (unformat (line_input, "tx"))
Florin Corase86a8ed2018-01-05 03:20:25 -0800776 {
777 which = VL_API_TRACE_TX;
778 }
Neale Rannsecff1cb2020-04-22 12:14:52 -0400779 else if (unformat (line_input, "first %d", &first))
Florin Corase86a8ed2018-01-05 03:20:25 -0800780 {
781 ;
782 }
Neale Rannsecff1cb2020-04-22 12:14:52 -0400783 else if (unformat (line_input, "last %d", &last))
Florin Corase86a8ed2018-01-05 03:20:25 -0800784 {
785 ;
786 }
Neale Rannsecff1cb2020-04-22 12:14:52 -0400787 else if (unformat (line_input, "status"))
Florin Corase86a8ed2018-01-05 03:20:25 -0800788 {
789 vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
790 am, which);
791 }
Neale Rannsecff1cb2020-04-22 12:14:52 -0400792 else if (unformat (line_input, "free"))
Florin Corase86a8ed2018-01-05 03:20:25 -0800793 {
Dave Barachf66f8832020-01-23 08:44:40 -0500794 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800795 vl_msg_api_trace_onoff (am, which, 0);
796 vl_msg_api_trace_free (am, which);
Dave Barachf66f8832020-01-23 08:44:40 -0500797 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800798 }
Neale Rannsecff1cb2020-04-22 12:14:52 -0400799 else if (unformat (line_input, "post-mortem-on"))
Florin Corase86a8ed2018-01-05 03:20:25 -0800800 vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
Neale Rannsecff1cb2020-04-22 12:14:52 -0400801 else if (unformat (line_input, "post-mortem-off"))
Florin Corase86a8ed2018-01-05 03:20:25 -0800802 vl_msg_api_post_mortem_dump_enable_disable (0 /* enable */ );
803 else
804 return clib_error_return (0, "unknown input `%U'",
805 format_unformat_error, input);
806 }
Dave Barachf35a0722019-06-12 16:50:38 -0400807out:
808 vec_free (filename);
809 vec_free (chroot_filename);
Neale Rannsecff1cb2020-04-22 12:14:52 -0400810 unformat_free (line_input);
Florin Corase86a8ed2018-01-05 03:20:25 -0800811 return 0;
812}
813
814/*?
815 * Display, replay, or save a binary API trace
816?*/
817
818/* *INDENT-OFF* */
819VLIB_CLI_COMMAND (api_trace_command, static) =
820{
821 .path = "api trace",
822 .short_help = "api trace [on|off][first <n>][last <n>][status][free]"
Dave Barachf66f8832020-01-23 08:44:40 -0500823 "[post-mortem-on][dump|custom-dump|save|replay <file>]",
Florin Corase86a8ed2018-01-05 03:20:25 -0800824 .function = api_trace_command_fn,
Dave Barachf66f8832020-01-23 08:44:40 -0500825 .is_mp_safe = 1,
Florin Corase86a8ed2018-01-05 03:20:25 -0800826};
827/* *INDENT-ON* */
828
829static clib_error_t *
830vl_api_trace_command (vlib_main_t * vm,
831 unformat_input_t * input, vlib_cli_command_t * cli_cmd)
832{
833 u32 nitems = 1024;
834 vl_api_trace_which_t which = VL_API_TRACE_RX;
Dave Barach39d69112019-11-27 11:42:13 -0500835 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800836
837 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
838 {
839 if (unformat (input, "rx nitems %u", &nitems) || unformat (input, "rx"))
840 goto configure;
841 else if (unformat (input, "tx nitems %u", &nitems)
842 || unformat (input, "tx"))
843 {
844 which = VL_API_TRACE_RX;
845 goto configure;
846 }
847 else if (unformat (input, "on rx"))
848 {
849 vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
850 }
851 else if (unformat (input, "on tx"))
852 {
853 vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 1);
854 }
855 else if (unformat (input, "on"))
856 {
857 vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
858 }
859 else if (unformat (input, "off"))
860 {
861 vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
862 vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
863 }
864 else if (unformat (input, "free"))
865 {
866 vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
867 vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
868 vl_msg_api_trace_free (am, VL_API_TRACE_RX);
869 vl_msg_api_trace_free (am, VL_API_TRACE_TX);
870 }
871 else if (unformat (input, "debug on"))
872 {
873 am->msg_print_flag = 1;
874 }
875 else if (unformat (input, "debug off"))
876 {
877 am->msg_print_flag = 0;
878 }
879 else
880 return clib_error_return (0, "unknown input `%U'",
881 format_unformat_error, input);
882 }
883 return 0;
884
885configure:
886 if (vl_msg_api_trace_configure (am, which, nitems))
887 {
888 vlib_cli_output (vm, "warning: trace configure error (%d, %d)",
889 which, nitems);
890 }
891
892 return 0;
893}
894
895/*?
896 * Control the binary API trace mechanism
897?*/
898/* *INDENT-OFF* */
899VLIB_CLI_COMMAND (trace, static) =
900{
ezkexmabcee60d2019-03-20 12:02:33 -0400901 .path = "set api-trace",
902 .short_help = "API trace [on][on tx][on rx][off][free][debug on][debug off]",
Florin Corase86a8ed2018-01-05 03:20:25 -0800903 .function = vl_api_trace_command,
904};
905/* *INDENT-ON* */
906
907static clib_error_t *
908api_trace_config_fn (vlib_main_t * vm, unformat_input_t * input)
909{
910 u32 nitems = 256 << 10;
911 vl_api_trace_which_t which = VL_API_TRACE_RX;
Dave Barach39d69112019-11-27 11:42:13 -0500912 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800913
914 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
915 {
916 if (unformat (input, "on") || unformat (input, "enable"))
917 {
918 if (unformat (input, "nitems %d", &nitems))
919 ;
920 vl_msg_api_trace_configure (am, which, nitems);
921 vl_msg_api_trace_onoff (am, which, 1 /* on */ );
922 vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
923 }
924 else if (unformat (input, "save-api-table %s",
925 &am->save_msg_table_filename))
926 ;
927 else
928 return clib_error_return (0, "unknown input `%U'",
929 format_unformat_error, input);
930 }
931 return 0;
932}
933
934/*?
935 * This module has three configuration parameters:
936 * "on" or "enable" - enables binary api tracing
937 * "nitems <nnn>" - sets the size of the circular buffer to <nnn>
938 * "save-api-table <filename>" - dumps the API message table to /tmp/<filename>
939?*/
940VLIB_CONFIG_FUNCTION (api_trace_config_fn, "api-trace");
941
942static clib_error_t *
943api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
944{
Dave Barach39d69112019-11-27 11:42:13 -0500945 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800946 u32 nitems;
947
948 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
949 {
950 if (unformat (input, "length %d", &nitems) ||
951 (unformat (input, "len %d", &nitems)))
952 {
953 if (nitems >= 1024)
954 am->vlib_input_queue_length = nitems;
955 else
956 clib_warning ("vlib input queue length %d too small, ignored",
957 nitems);
958 }
959 else
960 return clib_error_return (0, "unknown input `%U'",
961 format_unformat_error, input);
962 }
963 return 0;
964}
965
966VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
967
968static u8 *
969extract_name (u8 * s)
970{
971 u8 *rv;
972
973 rv = vec_dup (s);
974
975 while (vec_len (rv) && rv[vec_len (rv)] != '_')
976 _vec_len (rv)--;
977
978 rv[vec_len (rv)] = 0;
979
980 return rv;
981}
982
983static u8 *
984extract_crc (u8 * s)
985{
986 int i;
987 u8 *rv;
988
989 rv = vec_dup (s);
990
991 for (i = vec_len (rv) - 1; i >= 0; i--)
992 {
993 if (rv[i] == '_')
994 {
995 vec_delete (rv, i + 1, 0);
996 break;
997 }
998 }
999 return rv;
1000}
1001
1002typedef struct
1003{
1004 u8 *name_and_crc;
1005 u8 *name;
1006 u8 *crc;
1007 u32 msg_index;
1008 int which;
1009} msg_table_unserialize_t;
1010
1011static int
1012table_id_cmp (void *a1, void *a2)
1013{
1014 msg_table_unserialize_t *n1 = a1;
1015 msg_table_unserialize_t *n2 = a2;
1016
1017 return (n1->msg_index - n2->msg_index);
1018}
1019
1020static int
1021table_name_and_crc_cmp (void *a1, void *a2)
1022{
1023 msg_table_unserialize_t *n1 = a1;
1024 msg_table_unserialize_t *n2 = a2;
1025
1026 return strcmp ((char *) n1->name_and_crc, (char *) n2->name_and_crc);
1027}
1028
1029static clib_error_t *
1030dump_api_table_file_command_fn (vlib_main_t * vm,
1031 unformat_input_t * input,
1032 vlib_cli_command_t * cmd)
1033{
1034 u8 *filename = 0;
Dave Barach39d69112019-11-27 11:42:13 -05001035 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001036 serialize_main_t _sm, *sm = &_sm;
1037 clib_error_t *error;
1038 u32 nmsgs;
1039 u32 msg_index;
1040 u8 *name_and_crc;
1041 int compare_current = 0;
1042 int numeric_sort = 0;
1043 msg_table_unserialize_t *table = 0, *item;
1044 u32 i;
1045 u32 ndifferences = 0;
1046
1047 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1048 {
1049 if (unformat (input, "file %s", &filename))
1050 ;
1051 else if (unformat (input, "compare-current")
1052 || unformat (input, "compare"))
1053 compare_current = 1;
1054 else if (unformat (input, "numeric"))
1055 numeric_sort = 1;
1056 else
1057 return clib_error_return (0, "unknown input `%U'",
1058 format_unformat_error, input);
1059 }
1060
1061 if (numeric_sort && compare_current)
1062 return clib_error_return
1063 (0, "Comparison and numeric sorting are incompatible");
1064
1065 if (filename == 0)
1066 return clib_error_return (0, "File not specified");
1067
1068 /* Load the serialized message table from the table dump */
1069
1070 error = unserialize_open_clib_file (sm, (char *) filename);
1071
1072 if (error)
1073 return error;
1074
1075 unserialize_integer (sm, &nmsgs, sizeof (u32));
1076
1077 for (i = 0; i < nmsgs; i++)
1078 {
1079 msg_index = unserialize_likely_small_unsigned_integer (sm);
1080 unserialize_cstring (sm, (char **) &name_and_crc);
1081 vec_add2 (table, item, 1);
1082 item->msg_index = msg_index;
1083 item->name_and_crc = name_and_crc;
1084 item->name = extract_name (name_and_crc);
1085 item->crc = extract_crc (name_and_crc);
1086 item->which = 0; /* file */
1087 }
Ole Troanedfe2c02019-07-30 15:38:13 +02001088 unserialize_close (sm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001089
1090 /* Compare with the current image? */
1091 if (compare_current)
1092 {
1093 /* Append the current message table */
1094 u8 *tblv = vl_api_serialize_message_table (am, 0);
1095
1096 serialize_open_vector (sm, tblv);
1097 unserialize_integer (sm, &nmsgs, sizeof (u32));
1098
1099 for (i = 0; i < nmsgs; i++)
1100 {
1101 msg_index = unserialize_likely_small_unsigned_integer (sm);
1102 unserialize_cstring (sm, (char **) &name_and_crc);
1103
1104 vec_add2 (table, item, 1);
1105 item->msg_index = msg_index;
1106 item->name_and_crc = name_and_crc;
1107 item->name = extract_name (name_and_crc);
1108 item->crc = extract_crc (name_and_crc);
1109 item->which = 1; /* current_image */
1110 }
1111 vec_free (tblv);
1112 }
1113
1114 /* Sort the table. */
1115 if (numeric_sort)
1116 vec_sort_with_function (table, table_id_cmp);
1117 else
1118 vec_sort_with_function (table, table_name_and_crc_cmp);
1119
1120 if (compare_current)
1121 {
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001122 u8 *dashes = 0;
Florin Corase86a8ed2018-01-05 03:20:25 -08001123 ndifferences = 0;
1124
1125 /*
1126 * In this case, the recovered table will have two entries per
1127 * API message. So, if entries i and i+1 match, the message definitions
1128 * are identical. Otherwise, the crc is different, or a message is
1129 * present in only one of the tables.
1130 */
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001131 vlib_cli_output (vm, "%-60s | %s", "Message Name", "Result");
1132 vec_validate_init_empty (dashes, 60, '-');
1133 vec_terminate_c_string (dashes);
1134 vlib_cli_output (vm, "%60s-|-%s", dashes, "-----------------");
1135 vec_free (dashes);
Florin Corase86a8ed2018-01-05 03:20:25 -08001136 for (i = 0; i < vec_len (table);)
1137 {
1138 /* Last message lonely? */
1139 if (i == vec_len (table) - 1)
1140 {
1141 ndifferences++;
1142 goto last_unique;
1143 }
1144
1145 /* Identical pair? */
1146 if (!strncmp
1147 ((char *) table[i].name_and_crc,
1148 (char *) table[i + 1].name_and_crc,
1149 vec_len (table[i].name_and_crc)))
1150 {
1151 i += 2;
1152 continue;
1153 }
1154
1155 ndifferences++;
1156
1157 /* Only in one of two tables? */
Andrew Yourtchenkoae605b82019-01-21 16:39:33 +01001158 if (i + 1 == vec_len (table)
1159 || strcmp ((char *) table[i].name, (char *) table[i + 1].name))
Florin Corase86a8ed2018-01-05 03:20:25 -08001160 {
1161 last_unique:
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001162 vlib_cli_output (vm, "%-60s | only in %s",
Florin Corase86a8ed2018-01-05 03:20:25 -08001163 table[i].name, table[i].which ?
1164 "image" : "file");
1165 i++;
1166 continue;
1167 }
1168 /* In both tables, but with different signatures */
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001169 vlib_cli_output (vm, "%-60s | definition changed", table[i].name);
Florin Corase86a8ed2018-01-05 03:20:25 -08001170 i += 2;
1171 }
1172 if (ndifferences == 0)
1173 vlib_cli_output (vm, "No api message signature differences found.");
1174 else
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001175 vlib_cli_output (vm, "\nFound %u api message signature differences",
Florin Corase86a8ed2018-01-05 03:20:25 -08001176 ndifferences);
1177 goto cleanup;
1178 }
1179
1180 /* Dump the table, sorted as shown above */
1181 vlib_cli_output (vm, "%=60s %=8s %=10s", "Message name", "MsgID", "CRC");
1182
1183 for (i = 0; i < vec_len (table); i++)
1184 {
1185 item = table + i;
1186 vlib_cli_output (vm, "%-60s %8u %10s", item->name,
1187 item->msg_index, item->crc);
1188 }
1189
1190cleanup:
1191 for (i = 0; i < vec_len (table); i++)
1192 {
1193 vec_free (table[i].name_and_crc);
1194 vec_free (table[i].name);
1195 vec_free (table[i].crc);
1196 }
1197
1198 vec_free (table);
1199
1200 return 0;
1201}
1202
1203/*?
1204 * Displays a serialized API message decode table, sorted by message name
1205 *
1206 * @cliexpar
1207 * @cliexstart{show api dump file <filename>}
1208 * Message name MsgID CRC
1209 * accept_session 407 8e2a127e
1210 * accept_session_reply 408 67d8c22a
1211 * add_node_next 549 e4202993
1212 * add_node_next_reply 550 e89d6eed
1213 * etc.
1214 * @cliexend
1215?*/
1216
1217/*?
1218 * Compares a serialized API message decode table with the current image
1219 *
1220 * @cliexpar
1221 * @cliexstart{show api dump file <filename> compare}
1222 * ip_add_del_route definition changed
1223 * ip_table_add_del definition changed
1224 * l2_macs_event only in image
1225 * vnet_ip4_fib_counters only in file
1226 * vnet_ip4_nbr_counters only in file
1227 * @cliexend
1228?*/
1229
1230/*?
1231 * Display a serialized API message decode table, compare a saved
1232 * decode table with the current image, to establish API differences.
1233 *
1234?*/
1235/* *INDENT-OFF* */
1236VLIB_CLI_COMMAND (dump_api_table_file, static) =
1237{
1238 .path = "show api dump",
1239 .short_help = "show api dump file <filename> [numeric | compare-current]",
1240 .function = dump_api_table_file_command_fn,
1241};
1242
1243/* *INDENT-ON* */
1244/*
1245 * fd.io coding-style-patch-verification: ON
1246 *
1247 * Local Variables:
1248 * eval: (c-set-style "gnu")
1249 * End:
1250 */