blob: c57f333ba68b2ad5632e6350281155d03d3d56e1 [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);
Benoît Ganneb2f09142019-12-16 15:37:28 +0100459 CLIB_MEM_POISON (hp, file_size);
Florin Corase86a8ed2018-01-05 03:20:25 -0800460 return;
461 }
462 if (hp->wrapped)
463 vlib_cli_output (vm,
464 "Note: wrapped/incomplete trace, results may vary\n");
465
466 if (which == CUSTOM_DUMP)
467 {
468 saved_print_handlers = (void **) vec_dup (am->msg_print_handlers);
469 vl_msg_api_custom_dump_configure (am);
470 }
Benoît Ganneb2f09142019-12-16 15:37:28 +0100471
Florin Corase86a8ed2018-01-05 03:20:25 -0800472 msg = (u8 *) (hp + 1);
473
Ole Troanedfe2c02019-07-30 15:38:13 +0200474 u16 *msgid_vec = 0;
475 serialize_main_t _sm, *sm = &_sm;
476 u32 msgtbl_size = ntohl (hp->msgtbl_size);
477 u8 *name_and_crc;
478
479 unserialize_open_data (sm, msg, msgtbl_size);
480 unserialize_integer (sm, &nitems_msgtbl, sizeof (u32));
481
482 for (i = 0; i < nitems_msgtbl; i++)
483 {
484 u16 msg_index = unserialize_likely_small_unsigned_integer (sm);
485 unserialize_cstring (sm, (char **) &name_and_crc);
486 u16 msg_index2 = vl_msg_api_get_msg_index (name_and_crc);
487 vec_validate (msgid_vec, msg_index);
488 msgid_vec[msg_index] = msg_index2;
489 }
490
491 msg += msgtbl_size;
492
Florin Corase86a8ed2018-01-05 03:20:25 -0800493 for (i = 0; i < first_index; i++)
494 {
495 trace_cfg_t *cfgp;
496 int size;
497 u16 msg_id;
498
499 size = clib_host_to_net_u32 (*(u32 *) msg);
500 msg += sizeof (u32);
501
Ole Troanedfe2c02019-07-30 15:38:13 +0200502 msg_id = ntohs (*((u16 *) msg));
503 if (msg_id < vec_len (msgid_vec))
504 msg_id = msgid_vec[msg_id];
Florin Corase86a8ed2018-01-05 03:20:25 -0800505 cfgp = am->api_trace_cfg + msg_id;
506 if (!cfgp)
507 {
508 vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
509 munmap (hp, file_size);
Benoît Ganneb2f09142019-12-16 15:37:28 +0100510 CLIB_MEM_POISON (hp, file_size);
Florin Corase86a8ed2018-01-05 03:20:25 -0800511 return;
512 }
513 msg += size;
514 }
515
516 if (which == REPLAY)
517 am->replay_in_progress = 1;
518
519 for (; i <= last_index; i++)
520 {
521 trace_cfg_t *cfgp;
Florin Corase86a8ed2018-01-05 03:20:25 -0800522 u16 msg_id;
523 int size;
524
525 if (which == DUMP)
526 vlib_cli_output (vm, "---------- trace %d -----------\n", i);
527
528 size = clib_host_to_net_u32 (*(u32 *) msg);
529 msg += sizeof (u32);
530
Ole Troanedfe2c02019-07-30 15:38:13 +0200531 msg_id = ntohs (*((u16 *) msg));
532 if (msg_id < vec_len (msgid_vec))
533 {
534 msg_id = msgid_vec[msg_id];
535 }
Florin Corase86a8ed2018-01-05 03:20:25 -0800536
537 cfgp = am->api_trace_cfg + msg_id;
538 if (!cfgp)
539 {
540 vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
541 munmap (hp, file_size);
Benoît Ganneb2f09142019-12-16 15:37:28 +0100542 CLIB_MEM_POISON (hp, file_size);
Florin Corase86a8ed2018-01-05 03:20:25 -0800543 vec_free (tmpbuf);
544 am->replay_in_progress = 0;
545 return;
546 }
547
548 /* Copy the buffer (from the read-only mmap'ed file) */
549 vec_validate (tmpbuf, size - 1 + sizeof (uword));
550 clib_memcpy (tmpbuf + sizeof (uword), msg, size);
Dave Barachb7b92992018-10-17 10:38:51 -0400551 clib_memset (tmpbuf, 0xf, sizeof (uword));
Florin Corase86a8ed2018-01-05 03:20:25 -0800552
553 /*
Ole Troanedfe2c02019-07-30 15:38:13 +0200554 * Endian swap if needed. All msg data is supposed to be in
555 * network byte order.
Florin Corase86a8ed2018-01-05 03:20:25 -0800556 */
Ole Troan33a58172019-09-04 09:12:29 +0200557 if (((which == DUMP || which == CUSTOM_DUMP)
558 && clib_arch_is_little_endian))
Florin Corase86a8ed2018-01-05 03:20:25 -0800559 {
560 void (*endian_fp) (void *);
561 if (msg_id >= vec_len (am->msg_endian_handlers)
562 || (am->msg_endian_handlers[msg_id] == 0))
563 {
564 vlib_cli_output (vm, "Ugh: msg id %d no endian swap\n", msg_id);
565 munmap (hp, file_size);
Benoît Ganneb2f09142019-12-16 15:37:28 +0100566 CLIB_MEM_POISON (hp, file_size);
Florin Corase86a8ed2018-01-05 03:20:25 -0800567 vec_free (tmpbuf);
568 am->replay_in_progress = 0;
569 return;
570 }
571 endian_fp = am->msg_endian_handlers[msg_id];
572 (*endian_fp) (tmpbuf + sizeof (uword));
573 }
574
575 /* msg_id always in network byte order */
576 if (clib_arch_is_little_endian)
577 {
Ole Troanedfe2c02019-07-30 15:38:13 +0200578 u16 *msg_idp = (u16 *) (tmpbuf + sizeof (uword));
Florin Corase86a8ed2018-01-05 03:20:25 -0800579 *msg_idp = msg_id;
580 }
581
582 switch (which)
583 {
584 case CUSTOM_DUMP:
585 case DUMP:
586 if (msg_id < vec_len (am->msg_print_handlers) &&
587 am->msg_print_handlers[msg_id])
588 {
589 u8 *(*print_fp) (void *, void *);
590
591 print_fp = (void *) am->msg_print_handlers[msg_id];
592 (*print_fp) (tmpbuf + sizeof (uword), vm);
593 }
594 else
595 {
596 vlib_cli_output (vm, "Skipping msg id %d: no print fcn\n",
597 msg_id);
598 break;
599 }
600 break;
601
602 case INITIALIZERS:
603 if (msg_id < vec_len (am->msg_print_handlers) &&
604 am->msg_print_handlers[msg_id])
605 {
606 u8 *s;
607 int j;
608 u8 *(*print_fp) (void *, void *);
609
610 print_fp = (void *) am->msg_print_handlers[msg_id];
611
612 vlib_cli_output (vm, "/*");
613
614 (*print_fp) (tmpbuf + sizeof (uword), vm);
615 vlib_cli_output (vm, "*/\n");
616
617 s = format (0, "static u8 * vl_api_%s_%d[%d] = {",
618 am->msg_names[msg_id], i,
619 am->api_trace_cfg[msg_id].size);
620
621 for (j = 0; j < am->api_trace_cfg[msg_id].size; j++)
622 {
623 if ((j & 7) == 0)
624 s = format (s, "\n ");
625 s = format (s, "0x%02x,", tmpbuf[sizeof (uword) + j]);
626 }
627 s = format (s, "\n};\n%c", 0);
628 vlib_cli_output (vm, (char *) s);
629 vec_free (s);
630 }
631 break;
632
633 case REPLAY:
634 if (msg_id < vec_len (am->msg_print_handlers) &&
635 am->msg_print_handlers[msg_id] && cfgp->replay_enable)
636 {
John Loa56f2702018-06-25 20:38:35 -0400637 void (*handler) (void *, vlib_main_t *);
Florin Corase86a8ed2018-01-05 03:20:25 -0800638
639 handler = (void *) am->msg_handlers[msg_id];
640
641 if (!am->is_mp_safe[msg_id])
642 vl_msg_api_barrier_sync ();
John Loa56f2702018-06-25 20:38:35 -0400643 (*handler) (tmpbuf + sizeof (uword), vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800644 if (!am->is_mp_safe[msg_id])
645 vl_msg_api_barrier_release ();
646 }
647 else
648 {
649 if (cfgp->replay_enable)
650 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
651 msg_id);
652 break;
653 }
654 break;
655 }
656
657 _vec_len (tmpbuf) = 0;
658 msg += size;
659 }
660
661 if (saved_print_handlers)
662 {
663 clib_memcpy (am->msg_print_handlers, saved_print_handlers,
664 vec_len (am->msg_print_handlers) * sizeof (void *));
665 vec_free (saved_print_handlers);
666 }
667
668 munmap (hp, file_size);
Benoît Ganneb2f09142019-12-16 15:37:28 +0100669 CLIB_MEM_POISON (hp, file_size);
Florin Corase86a8ed2018-01-05 03:20:25 -0800670 vec_free (tmpbuf);
671 am->replay_in_progress = 0;
672}
673
Dave Barachf66f8832020-01-23 08:44:40 -0500674/** api_trace_command_fn - control the binary API trace / replay feature
675
676 Note: this command MUST be marked thread-safe. Replay with
677 multiple worker threads depends in many cases on worker thread
678 graph replica maintenance. If we (implicitly) assert a worker
679 thread barrier at the debug CLI level, all graph replica changes
680 are deferred until the replay operation completes. If an interface
681 is deleted, the wheels fall off.
682 */
683
Florin Corase86a8ed2018-01-05 03:20:25 -0800684static clib_error_t *
685api_trace_command_fn (vlib_main_t * vm,
686 unformat_input_t * input, vlib_cli_command_t * cmd)
687{
688 u32 nitems = 256 << 10;
Dave Barach39d69112019-11-27 11:42:13 -0500689 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800690 vl_api_trace_which_t which = VL_API_TRACE_RX;
Dave Barachf35a0722019-06-12 16:50:38 -0400691 u8 *filename = 0;
692 u8 *chroot_filename = 0;
Florin Corase86a8ed2018-01-05 03:20:25 -0800693 u32 first = 0;
694 u32 last = (u32) ~ 0;
695 FILE *fp;
696 int rv;
697
698 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
699 {
700 if (unformat (input, "on") || unformat (input, "enable"))
701 {
702 if (unformat (input, "nitems %d", &nitems))
703 ;
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 }
709 else if (unformat (input, "off"))
710 {
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 }
715 else if (unformat (input, "save %s", &filename))
716 {
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 }
759 else if (unformat (input, "dump %s", &filename))
760 {
761 vl_msg_api_process_file (vm, filename, first, last, DUMP);
762 }
763 else if (unformat (input, "custom-dump %s", &filename))
764 {
765 vl_msg_api_process_file (vm, filename, first, last, CUSTOM_DUMP);
766 }
767 else if (unformat (input, "replay %s", &filename))
768 {
769 vl_msg_api_process_file (vm, filename, first, last, REPLAY);
770 }
771 else if (unformat (input, "initializers %s", &filename))
772 {
773 vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
774 }
775 else if (unformat (input, "tx"))
776 {
777 which = VL_API_TRACE_TX;
778 }
779 else if (unformat (input, "first %d", &first))
780 {
781 ;
782 }
783 else if (unformat (input, "last %d", &last))
784 {
785 ;
786 }
787 else if (unformat (input, "status"))
788 {
789 vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
790 am, which);
791 }
792 else if (unformat (input, "free"))
793 {
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 }
799 else if (unformat (input, "post-mortem-on"))
800 vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
801 else if (unformat (input, "post-mortem-off"))
802 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);
Florin Corase86a8ed2018-01-05 03:20:25 -0800810 return 0;
811}
812
813/*?
814 * Display, replay, or save a binary API trace
815?*/
816
817/* *INDENT-OFF* */
818VLIB_CLI_COMMAND (api_trace_command, static) =
819{
820 .path = "api trace",
821 .short_help = "api trace [on|off][first <n>][last <n>][status][free]"
Dave Barachf66f8832020-01-23 08:44:40 -0500822 "[post-mortem-on][dump|custom-dump|save|replay <file>]",
Florin Corase86a8ed2018-01-05 03:20:25 -0800823 .function = api_trace_command_fn,
Dave Barachf66f8832020-01-23 08:44:40 -0500824 .is_mp_safe = 1,
Florin Corase86a8ed2018-01-05 03:20:25 -0800825};
826/* *INDENT-ON* */
827
828static clib_error_t *
829vl_api_trace_command (vlib_main_t * vm,
830 unformat_input_t * input, vlib_cli_command_t * cli_cmd)
831{
832 u32 nitems = 1024;
833 vl_api_trace_which_t which = VL_API_TRACE_RX;
Dave Barach39d69112019-11-27 11:42:13 -0500834 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800835
836 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
837 {
838 if (unformat (input, "rx nitems %u", &nitems) || unformat (input, "rx"))
839 goto configure;
840 else if (unformat (input, "tx nitems %u", &nitems)
841 || unformat (input, "tx"))
842 {
843 which = VL_API_TRACE_RX;
844 goto configure;
845 }
846 else if (unformat (input, "on rx"))
847 {
848 vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
849 }
850 else if (unformat (input, "on tx"))
851 {
852 vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 1);
853 }
854 else if (unformat (input, "on"))
855 {
856 vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
857 }
858 else if (unformat (input, "off"))
859 {
860 vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
861 vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
862 }
863 else if (unformat (input, "free"))
864 {
865 vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
866 vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
867 vl_msg_api_trace_free (am, VL_API_TRACE_RX);
868 vl_msg_api_trace_free (am, VL_API_TRACE_TX);
869 }
870 else if (unformat (input, "debug on"))
871 {
872 am->msg_print_flag = 1;
873 }
874 else if (unformat (input, "debug off"))
875 {
876 am->msg_print_flag = 0;
877 }
878 else
879 return clib_error_return (0, "unknown input `%U'",
880 format_unformat_error, input);
881 }
882 return 0;
883
884configure:
885 if (vl_msg_api_trace_configure (am, which, nitems))
886 {
887 vlib_cli_output (vm, "warning: trace configure error (%d, %d)",
888 which, nitems);
889 }
890
891 return 0;
892}
893
894/*?
895 * Control the binary API trace mechanism
896?*/
897/* *INDENT-OFF* */
898VLIB_CLI_COMMAND (trace, static) =
899{
ezkexmabcee60d2019-03-20 12:02:33 -0400900 .path = "set api-trace",
901 .short_help = "API trace [on][on tx][on rx][off][free][debug on][debug off]",
Florin Corase86a8ed2018-01-05 03:20:25 -0800902 .function = vl_api_trace_command,
903};
904/* *INDENT-ON* */
905
906static clib_error_t *
907api_trace_config_fn (vlib_main_t * vm, unformat_input_t * input)
908{
909 u32 nitems = 256 << 10;
910 vl_api_trace_which_t which = VL_API_TRACE_RX;
Dave Barach39d69112019-11-27 11:42:13 -0500911 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800912
913 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
914 {
915 if (unformat (input, "on") || unformat (input, "enable"))
916 {
917 if (unformat (input, "nitems %d", &nitems))
918 ;
919 vl_msg_api_trace_configure (am, which, nitems);
920 vl_msg_api_trace_onoff (am, which, 1 /* on */ );
921 vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
922 }
923 else if (unformat (input, "save-api-table %s",
924 &am->save_msg_table_filename))
925 ;
926 else
927 return clib_error_return (0, "unknown input `%U'",
928 format_unformat_error, input);
929 }
930 return 0;
931}
932
933/*?
934 * This module has three configuration parameters:
935 * "on" or "enable" - enables binary api tracing
936 * "nitems <nnn>" - sets the size of the circular buffer to <nnn>
937 * "save-api-table <filename>" - dumps the API message table to /tmp/<filename>
938?*/
939VLIB_CONFIG_FUNCTION (api_trace_config_fn, "api-trace");
940
941static clib_error_t *
942api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
943{
Dave Barach39d69112019-11-27 11:42:13 -0500944 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800945 u32 nitems;
946
947 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
948 {
949 if (unformat (input, "length %d", &nitems) ||
950 (unformat (input, "len %d", &nitems)))
951 {
952 if (nitems >= 1024)
953 am->vlib_input_queue_length = nitems;
954 else
955 clib_warning ("vlib input queue length %d too small, ignored",
956 nitems);
957 }
958 else
959 return clib_error_return (0, "unknown input `%U'",
960 format_unformat_error, input);
961 }
962 return 0;
963}
964
965VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
966
967static u8 *
968extract_name (u8 * s)
969{
970 u8 *rv;
971
972 rv = vec_dup (s);
973
974 while (vec_len (rv) && rv[vec_len (rv)] != '_')
975 _vec_len (rv)--;
976
977 rv[vec_len (rv)] = 0;
978
979 return rv;
980}
981
982static u8 *
983extract_crc (u8 * s)
984{
985 int i;
986 u8 *rv;
987
988 rv = vec_dup (s);
989
990 for (i = vec_len (rv) - 1; i >= 0; i--)
991 {
992 if (rv[i] == '_')
993 {
994 vec_delete (rv, i + 1, 0);
995 break;
996 }
997 }
998 return rv;
999}
1000
1001typedef struct
1002{
1003 u8 *name_and_crc;
1004 u8 *name;
1005 u8 *crc;
1006 u32 msg_index;
1007 int which;
1008} msg_table_unserialize_t;
1009
1010static int
1011table_id_cmp (void *a1, void *a2)
1012{
1013 msg_table_unserialize_t *n1 = a1;
1014 msg_table_unserialize_t *n2 = a2;
1015
1016 return (n1->msg_index - n2->msg_index);
1017}
1018
1019static int
1020table_name_and_crc_cmp (void *a1, void *a2)
1021{
1022 msg_table_unserialize_t *n1 = a1;
1023 msg_table_unserialize_t *n2 = a2;
1024
1025 return strcmp ((char *) n1->name_and_crc, (char *) n2->name_and_crc);
1026}
1027
1028static clib_error_t *
1029dump_api_table_file_command_fn (vlib_main_t * vm,
1030 unformat_input_t * input,
1031 vlib_cli_command_t * cmd)
1032{
1033 u8 *filename = 0;
Dave Barach39d69112019-11-27 11:42:13 -05001034 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001035 serialize_main_t _sm, *sm = &_sm;
1036 clib_error_t *error;
1037 u32 nmsgs;
1038 u32 msg_index;
1039 u8 *name_and_crc;
1040 int compare_current = 0;
1041 int numeric_sort = 0;
1042 msg_table_unserialize_t *table = 0, *item;
1043 u32 i;
1044 u32 ndifferences = 0;
1045
1046 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1047 {
1048 if (unformat (input, "file %s", &filename))
1049 ;
1050 else if (unformat (input, "compare-current")
1051 || unformat (input, "compare"))
1052 compare_current = 1;
1053 else if (unformat (input, "numeric"))
1054 numeric_sort = 1;
1055 else
1056 return clib_error_return (0, "unknown input `%U'",
1057 format_unformat_error, input);
1058 }
1059
1060 if (numeric_sort && compare_current)
1061 return clib_error_return
1062 (0, "Comparison and numeric sorting are incompatible");
1063
1064 if (filename == 0)
1065 return clib_error_return (0, "File not specified");
1066
1067 /* Load the serialized message table from the table dump */
1068
1069 error = unserialize_open_clib_file (sm, (char *) filename);
1070
1071 if (error)
1072 return error;
1073
1074 unserialize_integer (sm, &nmsgs, sizeof (u32));
1075
1076 for (i = 0; i < nmsgs; i++)
1077 {
1078 msg_index = unserialize_likely_small_unsigned_integer (sm);
1079 unserialize_cstring (sm, (char **) &name_and_crc);
1080 vec_add2 (table, item, 1);
1081 item->msg_index = msg_index;
1082 item->name_and_crc = name_and_crc;
1083 item->name = extract_name (name_and_crc);
1084 item->crc = extract_crc (name_and_crc);
1085 item->which = 0; /* file */
1086 }
Ole Troanedfe2c02019-07-30 15:38:13 +02001087 unserialize_close (sm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001088
1089 /* Compare with the current image? */
1090 if (compare_current)
1091 {
1092 /* Append the current message table */
1093 u8 *tblv = vl_api_serialize_message_table (am, 0);
1094
1095 serialize_open_vector (sm, tblv);
1096 unserialize_integer (sm, &nmsgs, sizeof (u32));
1097
1098 for (i = 0; i < nmsgs; i++)
1099 {
1100 msg_index = unserialize_likely_small_unsigned_integer (sm);
1101 unserialize_cstring (sm, (char **) &name_and_crc);
1102
1103 vec_add2 (table, item, 1);
1104 item->msg_index = msg_index;
1105 item->name_and_crc = name_and_crc;
1106 item->name = extract_name (name_and_crc);
1107 item->crc = extract_crc (name_and_crc);
1108 item->which = 1; /* current_image */
1109 }
1110 vec_free (tblv);
1111 }
1112
1113 /* Sort the table. */
1114 if (numeric_sort)
1115 vec_sort_with_function (table, table_id_cmp);
1116 else
1117 vec_sort_with_function (table, table_name_and_crc_cmp);
1118
1119 if (compare_current)
1120 {
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001121 u8 *dashes = 0;
Florin Corase86a8ed2018-01-05 03:20:25 -08001122 ndifferences = 0;
1123
1124 /*
1125 * In this case, the recovered table will have two entries per
1126 * API message. So, if entries i and i+1 match, the message definitions
1127 * are identical. Otherwise, the crc is different, or a message is
1128 * present in only one of the tables.
1129 */
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001130 vlib_cli_output (vm, "%-60s | %s", "Message Name", "Result");
1131 vec_validate_init_empty (dashes, 60, '-');
1132 vec_terminate_c_string (dashes);
1133 vlib_cli_output (vm, "%60s-|-%s", dashes, "-----------------");
1134 vec_free (dashes);
Florin Corase86a8ed2018-01-05 03:20:25 -08001135 for (i = 0; i < vec_len (table);)
1136 {
1137 /* Last message lonely? */
1138 if (i == vec_len (table) - 1)
1139 {
1140 ndifferences++;
1141 goto last_unique;
1142 }
1143
1144 /* Identical pair? */
1145 if (!strncmp
1146 ((char *) table[i].name_and_crc,
1147 (char *) table[i + 1].name_and_crc,
1148 vec_len (table[i].name_and_crc)))
1149 {
1150 i += 2;
1151 continue;
1152 }
1153
1154 ndifferences++;
1155
1156 /* Only in one of two tables? */
Andrew Yourtchenkoae605b82019-01-21 16:39:33 +01001157 if (i + 1 == vec_len (table)
1158 || strcmp ((char *) table[i].name, (char *) table[i + 1].name))
Florin Corase86a8ed2018-01-05 03:20:25 -08001159 {
1160 last_unique:
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001161 vlib_cli_output (vm, "%-60s | only in %s",
Florin Corase86a8ed2018-01-05 03:20:25 -08001162 table[i].name, table[i].which ?
1163 "image" : "file");
1164 i++;
1165 continue;
1166 }
1167 /* In both tables, but with different signatures */
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001168 vlib_cli_output (vm, "%-60s | definition changed", table[i].name);
Florin Corase86a8ed2018-01-05 03:20:25 -08001169 i += 2;
1170 }
1171 if (ndifferences == 0)
1172 vlib_cli_output (vm, "No api message signature differences found.");
1173 else
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001174 vlib_cli_output (vm, "\nFound %u api message signature differences",
Florin Corase86a8ed2018-01-05 03:20:25 -08001175 ndifferences);
1176 goto cleanup;
1177 }
1178
1179 /* Dump the table, sorted as shown above */
1180 vlib_cli_output (vm, "%=60s %=8s %=10s", "Message name", "MsgID", "CRC");
1181
1182 for (i = 0; i < vec_len (table); i++)
1183 {
1184 item = table + i;
1185 vlib_cli_output (vm, "%-60s %8u %10s", item->name,
1186 item->msg_index, item->crc);
1187 }
1188
1189cleanup:
1190 for (i = 0; i < vec_len (table); i++)
1191 {
1192 vec_free (table[i].name_and_crc);
1193 vec_free (table[i].name);
1194 vec_free (table[i].crc);
1195 }
1196
1197 vec_free (table);
1198
1199 return 0;
1200}
1201
1202/*?
1203 * Displays a serialized API message decode table, sorted by message name
1204 *
1205 * @cliexpar
1206 * @cliexstart{show api dump file <filename>}
1207 * Message name MsgID CRC
1208 * accept_session 407 8e2a127e
1209 * accept_session_reply 408 67d8c22a
1210 * add_node_next 549 e4202993
1211 * add_node_next_reply 550 e89d6eed
1212 * etc.
1213 * @cliexend
1214?*/
1215
1216/*?
1217 * Compares a serialized API message decode table with the current image
1218 *
1219 * @cliexpar
1220 * @cliexstart{show api dump file <filename> compare}
1221 * ip_add_del_route definition changed
1222 * ip_table_add_del definition changed
1223 * l2_macs_event only in image
1224 * vnet_ip4_fib_counters only in file
1225 * vnet_ip4_nbr_counters only in file
1226 * @cliexend
1227?*/
1228
1229/*?
1230 * Display a serialized API message decode table, compare a saved
1231 * decode table with the current image, to establish API differences.
1232 *
1233?*/
1234/* *INDENT-OFF* */
1235VLIB_CLI_COMMAND (dump_api_table_file, static) =
1236{
1237 .path = "show api dump",
1238 .short_help = "show api dump file <filename> [numeric | compare-current]",
1239 .function = dump_api_table_file_command_fn,
1240};
1241
1242/* *INDENT-ON* */
1243/*
1244 * fd.io coding-style-patch-verification: ON
1245 *
1246 * Local Variables:
1247 * eval: (c-set-style "gnu")
1248 * End:
1249 */