blob: 74ad3c5cd7686bb22c965078ffa60bf28770ce78 [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* */
Damjan Marionb2c31b62020-12-13 21:47:40 +0100114 pool_foreach (regpp, am->vl_clients)
115 {
Florin Corase86a8ed2018-01-05 03:20:25 -0800116 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 }
Damjan Marionb2c31b62020-12-13 21:47:40 +0100137 }
Florin Corase86a8ed2018-01-05 03:20:25 -0800138 /* *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,
Filip Tehlar36217e32021-07-23 08:51:10 +0000347 DUMP_JSON,
Florin Corase86a8ed2018-01-05 03:20:25 -0800348 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
Florin Corase86a8ed2018-01-05 03:20:25 -0800388static void
389vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
390 u32 first_index, u32 last_index,
391 vl_api_replay_t which)
392{
393 vl_api_trace_file_header_t *hp;
394 int i, fd;
Filip Tehlar36217e32021-07-23 08:51:10 +0000395 u16 *msgid_vec = 0;
Florin Corase86a8ed2018-01-05 03:20:25 -0800396 struct stat statb;
397 size_t file_size;
398 u8 *msg;
Dave Barach39d69112019-11-27 11:42:13 -0500399 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800400 u8 *tmpbuf = 0;
Ole Troanedfe2c02019-07-30 15:38:13 +0200401 u32 nitems, nitems_msgtbl;
Florin Corase86a8ed2018-01-05 03:20:25 -0800402
403 fd = open ((char *) filename, O_RDONLY);
404
405 if (fd < 0)
406 {
407 vlib_cli_output (vm, "Couldn't open %s\n", filename);
408 return;
409 }
410
411 if (fstat (fd, &statb) < 0)
412 {
413 vlib_cli_output (vm, "Couldn't stat %s\n", filename);
414 close (fd);
415 return;
416 }
417
418 if (!(statb.st_mode & S_IFREG) || (statb.st_size < sizeof (*hp)))
419 {
420 vlib_cli_output (vm, "File not plausible: %s\n", filename);
421 close (fd);
422 return;
423 }
424
425 file_size = statb.st_size;
Benoît Ganneaba49832020-01-21 18:35:49 +0100426 file_size = (file_size + 4095) & ~(4095);
Florin Corase86a8ed2018-01-05 03:20:25 -0800427
428 hp = mmap (0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
429
430 if (hp == (vl_api_trace_file_header_t *) MAP_FAILED)
431 {
432 vlib_cli_output (vm, "mmap failed: %s\n", filename);
433 close (fd);
434 return;
435 }
436 close (fd);
437
Benoît Ganneb2f09142019-12-16 15:37:28 +0100438 CLIB_MEM_UNPOISON (hp, file_size);
439
Ole Troanedfe2c02019-07-30 15:38:13 +0200440 nitems = ntohl (hp->nitems);
Florin Corase86a8ed2018-01-05 03:20:25 -0800441
442 if (last_index == (u32) ~ 0)
443 {
444 last_index = nitems - 1;
445 }
446
447 if (first_index >= nitems || last_index >= nitems)
448 {
449 vlib_cli_output (vm, "Range (%d, %d) outside file range (0, %d)\n",
450 first_index, last_index, nitems - 1);
451 munmap (hp, file_size);
452 return;
453 }
454 if (hp->wrapped)
455 vlib_cli_output (vm,
456 "Note: wrapped/incomplete trace, results may vary\n");
457
Filip Tehlar36217e32021-07-23 08:51:10 +0000458 size_t file_size_left = file_size;
459
460#define assert_size(size_left, s) \
461 do \
462 { \
463 if ((s) >= size_left) \
464 { \
465 vlib_cli_output (vm, "corrupted file"); \
466 munmap (hp, file_size); \
467 vec_free (msgid_vec); \
468 return; \
469 } \
470 size_left -= s; \
471 } \
472 while (0);
473
474 assert_size (file_size_left, sizeof (hp[0]));
Florin Corase86a8ed2018-01-05 03:20:25 -0800475 msg = (u8 *) (hp + 1);
476
Ole Troanedfe2c02019-07-30 15:38:13 +0200477 serialize_main_t _sm, *sm = &_sm;
478 u32 msgtbl_size = ntohl (hp->msgtbl_size);
479 u8 *name_and_crc;
480
Filip Tehlar36217e32021-07-23 08:51:10 +0000481 assert_size (file_size_left, msgtbl_size);
482
Ole Troanedfe2c02019-07-30 15:38:13 +0200483 unserialize_open_data (sm, msg, msgtbl_size);
484 unserialize_integer (sm, &nitems_msgtbl, sizeof (u32));
485
486 for (i = 0; i < nitems_msgtbl; i++)
487 {
488 u16 msg_index = unserialize_likely_small_unsigned_integer (sm);
489 unserialize_cstring (sm, (char **) &name_and_crc);
490 u16 msg_index2 = vl_msg_api_get_msg_index (name_and_crc);
491 vec_validate (msgid_vec, msg_index);
492 msgid_vec[msg_index] = msg_index2;
493 }
494
495 msg += msgtbl_size;
496
Florin Corase86a8ed2018-01-05 03:20:25 -0800497 for (i = 0; i < first_index; i++)
498 {
499 trace_cfg_t *cfgp;
500 int size;
501 u16 msg_id;
502
Filip Tehlar36217e32021-07-23 08:51:10 +0000503 assert_size (file_size_left, sizeof (u32));
Florin Corase86a8ed2018-01-05 03:20:25 -0800504 size = clib_host_to_net_u32 (*(u32 *) msg);
505 msg += sizeof (u32);
506
Filip Tehlar36217e32021-07-23 08:51:10 +0000507 assert_size (file_size_left, size);
Ole Troanedfe2c02019-07-30 15:38:13 +0200508 msg_id = ntohs (*((u16 *) msg));
509 if (msg_id < vec_len (msgid_vec))
510 msg_id = msgid_vec[msg_id];
Florin Corase86a8ed2018-01-05 03:20:25 -0800511 cfgp = am->api_trace_cfg + msg_id;
512 if (!cfgp)
513 {
514 vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
515 munmap (hp, file_size);
Filip Tehlar36217e32021-07-23 08:51:10 +0000516 vec_free (msgid_vec);
Florin Corase86a8ed2018-01-05 03:20:25 -0800517 return;
518 }
519 msg += size;
520 }
521
522 if (which == REPLAY)
523 am->replay_in_progress = 1;
524
525 for (; i <= last_index; i++)
526 {
527 trace_cfg_t *cfgp;
Florin Corase86a8ed2018-01-05 03:20:25 -0800528 u16 msg_id;
529 int size;
530
531 if (which == DUMP)
532 vlib_cli_output (vm, "---------- trace %d -----------\n", i);
533
534 size = clib_host_to_net_u32 (*(u32 *) msg);
535 msg += sizeof (u32);
536
Ole Troanedfe2c02019-07-30 15:38:13 +0200537 msg_id = ntohs (*((u16 *) msg));
538 if (msg_id < vec_len (msgid_vec))
539 {
540 msg_id = msgid_vec[msg_id];
541 }
Florin Corase86a8ed2018-01-05 03:20:25 -0800542
543 cfgp = am->api_trace_cfg + msg_id;
544 if (!cfgp)
545 {
546 vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
547 munmap (hp, file_size);
548 vec_free (tmpbuf);
549 am->replay_in_progress = 0;
550 return;
551 }
552
553 /* Copy the buffer (from the read-only mmap'ed file) */
554 vec_validate (tmpbuf, size - 1 + sizeof (uword));
555 clib_memcpy (tmpbuf + sizeof (uword), msg, size);
Dave Barachb7b92992018-10-17 10:38:51 -0400556 clib_memset (tmpbuf, 0xf, sizeof (uword));
Florin Corase86a8ed2018-01-05 03:20:25 -0800557
558 /*
Ole Troanedfe2c02019-07-30 15:38:13 +0200559 * Endian swap if needed. All msg data is supposed to be in
560 * network byte order.
Florin Corase86a8ed2018-01-05 03:20:25 -0800561 */
Filip Tehlar36217e32021-07-23 08:51:10 +0000562 if (((which == DUMP || which == DUMP_JSON) &&
563 clib_arch_is_little_endian))
Florin Corase86a8ed2018-01-05 03:20:25 -0800564 {
565 void (*endian_fp) (void *);
566 if (msg_id >= vec_len (am->msg_endian_handlers)
567 || (am->msg_endian_handlers[msg_id] == 0))
568 {
569 vlib_cli_output (vm, "Ugh: msg id %d no endian swap\n", msg_id);
570 munmap (hp, file_size);
571 vec_free (tmpbuf);
572 am->replay_in_progress = 0;
573 return;
574 }
575 endian_fp = am->msg_endian_handlers[msg_id];
576 (*endian_fp) (tmpbuf + sizeof (uword));
577 }
578
579 /* msg_id always in network byte order */
580 if (clib_arch_is_little_endian)
581 {
Ole Troanedfe2c02019-07-30 15:38:13 +0200582 u16 *msg_idp = (u16 *) (tmpbuf + sizeof (uword));
Florin Corase86a8ed2018-01-05 03:20:25 -0800583 *msg_idp = msg_id;
584 }
585
586 switch (which)
587 {
Filip Tehlar36217e32021-07-23 08:51:10 +0000588 case DUMP_JSON:
589 if (msg_id < vec_len (am->msg_print_json_handlers) &&
590 am->msg_print_json_handlers[msg_id])
591 {
592 u8 *(*print_fp) (void *, void *);
593
594 print_fp = (void *) am->msg_print_json_handlers[msg_id];
595 (*print_fp) (tmpbuf + sizeof (uword), vm);
596 }
597 else
598 {
599 vlib_cli_output (vm, "Skipping msg id %d: no JSON print fcn\n",
600 msg_id);
601 break;
602 }
603 break;
604
Florin Corase86a8ed2018-01-05 03:20:25 -0800605 case DUMP:
606 if (msg_id < vec_len (am->msg_print_handlers) &&
607 am->msg_print_handlers[msg_id])
608 {
609 u8 *(*print_fp) (void *, void *);
610
611 print_fp = (void *) am->msg_print_handlers[msg_id];
612 (*print_fp) (tmpbuf + sizeof (uword), vm);
613 }
614 else
615 {
616 vlib_cli_output (vm, "Skipping msg id %d: no print fcn\n",
617 msg_id);
618 break;
619 }
620 break;
621
622 case INITIALIZERS:
623 if (msg_id < vec_len (am->msg_print_handlers) &&
624 am->msg_print_handlers[msg_id])
625 {
626 u8 *s;
627 int j;
628 u8 *(*print_fp) (void *, void *);
629
630 print_fp = (void *) am->msg_print_handlers[msg_id];
631
632 vlib_cli_output (vm, "/*");
633
634 (*print_fp) (tmpbuf + sizeof (uword), vm);
635 vlib_cli_output (vm, "*/\n");
636
637 s = format (0, "static u8 * vl_api_%s_%d[%d] = {",
638 am->msg_names[msg_id], i,
639 am->api_trace_cfg[msg_id].size);
640
641 for (j = 0; j < am->api_trace_cfg[msg_id].size; j++)
642 {
643 if ((j & 7) == 0)
644 s = format (s, "\n ");
645 s = format (s, "0x%02x,", tmpbuf[sizeof (uword) + j]);
646 }
647 s = format (s, "\n};\n%c", 0);
648 vlib_cli_output (vm, (char *) s);
649 vec_free (s);
650 }
651 break;
652
653 case REPLAY:
654 if (msg_id < vec_len (am->msg_print_handlers) &&
655 am->msg_print_handlers[msg_id] && cfgp->replay_enable)
656 {
John Loa56f2702018-06-25 20:38:35 -0400657 void (*handler) (void *, vlib_main_t *);
Florin Corase86a8ed2018-01-05 03:20:25 -0800658
659 handler = (void *) am->msg_handlers[msg_id];
660
661 if (!am->is_mp_safe[msg_id])
662 vl_msg_api_barrier_sync ();
John Loa56f2702018-06-25 20:38:35 -0400663 (*handler) (tmpbuf + sizeof (uword), vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800664 if (!am->is_mp_safe[msg_id])
665 vl_msg_api_barrier_release ();
666 }
667 else
668 {
669 if (cfgp->replay_enable)
670 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
671 msg_id);
672 break;
673 }
674 break;
675 }
676
677 _vec_len (tmpbuf) = 0;
678 msg += size;
679 }
680
Florin Corase86a8ed2018-01-05 03:20:25 -0800681 munmap (hp, file_size);
682 vec_free (tmpbuf);
Filip Tehlar36217e32021-07-23 08:51:10 +0000683 vec_free (msgid_vec);
Florin Corase86a8ed2018-01-05 03:20:25 -0800684 am->replay_in_progress = 0;
685}
686
Filip Tehlar36217e32021-07-23 08:51:10 +0000687static int
688file_exists (u8 *fname)
689{
690 FILE *fp = 0;
691 fp = fopen ((char *) fname, "r");
692 if (fp)
693 {
694 fclose (fp);
695 return 1;
696 }
697 return 0;
698}
699
700typedef struct
701{
702 vlib_main_t *vm;
703 u8 is_json;
704} vl_msg_print_args;
705
706static int
707vl_msg_print_trace (u8 *msg, void *ctx)
708{
709 vl_msg_print_args *a = ctx;
710 api_main_t *am = vlibapi_get_main ();
711 u16 msg_id = ntohs (*((u16 *) msg));
712 void (*print_fp) (void *, void *);
713 void (**handlers) (void *, void *);
714 u8 is_json = a->is_json;
715 u8 *tmpbuf = 0;
716
717 if (clib_arch_is_little_endian)
718 {
719 u32 msg_length = vec_len (msg);
720 vec_validate (tmpbuf, msg_length - 1);
721 clib_memcpy_fast (tmpbuf, msg, msg_length);
722 msg = tmpbuf;
723
724 void (*endian_fp) (void *);
725 endian_fp = am->msg_endian_handlers[msg_id];
726 (*endian_fp) (tmpbuf);
727 }
728
729 if (is_json)
730 handlers = am->msg_print_json_handlers;
731 else
732 handlers = am->msg_print_handlers;
733
734 if (msg_id < vec_len (handlers) && handlers[msg_id])
735 {
736 print_fp = (void *) handlers[msg_id];
737 (*print_fp) (msg, a->vm);
738 }
739 else
740 {
741 vlib_cli_output (a->vm, "Skipping msg id %d: no print fcn\n", msg_id);
742 }
743
744 vec_free (tmpbuf);
745 return 0;
746}
747
748static int
749vl_msg_api_dump_trace (vlib_main_t *vm, vl_api_trace_which_t which, u8 is_json)
750{
751 api_main_t *am = vlibapi_get_main ();
752 vl_api_trace_t *tp;
753
754 switch (which)
755 {
756 case VL_API_TRACE_TX:
757 tp = am->tx_trace;
758 break;
759 case VL_API_TRACE_RX:
760 tp = am->rx_trace;
761 break;
762 default:
763 return -1;
764 }
765
766 if (tp == 0 || tp->nitems == 0 || vec_len (tp->traces) == 0)
767 return -1;
768
769 vl_msg_print_args args;
770 clib_memset (&args, 0, sizeof (args));
771 args.is_json = is_json;
772 args.vm = vm;
773 vl_msg_traverse_trace (tp, vl_msg_print_trace, &args);
774
775 return 0;
776}
777
778static char *
779vl_msg_read_file (FILE *f)
780{
781 const size_t bufsize = 1024;
782 char *buf[bufsize], *v = 0;
783 size_t n;
784
785 while ((n = fread (buf, 1, bufsize, f)))
786 vec_add (v, buf, n);
787
788 return v;
789}
790
791static u16
792vl_msg_find_id_by_name_and_crc (vlib_main_t *vm, api_main_t *am, char *name)
793{
794 uword *p;
795 p = hash_get_mem (am->msg_index_by_name_and_crc, name);
796 if (!p)
797 return (u16) ~0;
798
799 return p[0];
800}
801
802static u16
803vl_msg_find_id_by_name (vlib_main_t *vm, api_main_t *am, char *name)
804{
805 uword *p;
806
807 if (!am->msg_id_by_name)
808 {
809 vlib_cli_output (vm, "message id table not yet initialized!\n");
810 return (u16) ~0;
811 }
812
813 p = hash_get_mem (am->msg_id_by_name, name);
814 if (!p)
815 return (u16) ~0;
816
817 return p[0];
818}
819
820static int
821vl_msg_exec_json_command (vlib_main_t *vm, cJSON *o)
822{
823 api_main_t *am = vlibapi_get_main ();
824 u16 msg_id;
825 void *(*fromjson) (cJSON *, int *);
826 int len = 0, rv = -1;
827 trace_cfg_t *cfgp;
828 u8 *msg = 0;
829
830 cJSON *msg_id_obj = cJSON_GetObjectItem (o, "_msgname");
831 if (!msg_id_obj)
832 {
833 vlib_cli_output (vm, "Missing '_msgname' element!\n");
834 return rv;
835 }
836 char *name = cJSON_GetStringValue (msg_id_obj);
837
838 cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc");
839 if (!msg_id_obj)
840 {
841 vlib_cli_output (vm, "Missing '_crc' element!\n");
842 return rv;
843 }
844 char *crc = cJSON_GetStringValue (crc_obj);
845 u8 proc_warning = 0;
846
847 u8 *name_crc = format (0, "%s_%s%c", name, crc, 0);
848 msg_id = vl_msg_find_id_by_name_and_crc (vm, am, (char *) name_crc);
849 if (msg_id == (u16) ~0)
850 {
851 msg_id = vl_msg_find_id_by_name (vm, am, name);
852 if (msg_id == (u16) ~0)
853 {
854 vlib_cli_output (vm, "unknown msg id %d!\n", msg_id);
855 vec_free (name_crc);
856 return rv;
857 }
858 proc_warning = 1;
859 }
860 vec_free (name_crc);
861
862 cfgp = am->api_trace_cfg + msg_id;
863 if (!cfgp)
864 {
865 vlib_cli_output (vm, "msg id %d no trace config\n", msg_id);
866 return rv;
867 }
868
869 if (cfgp->replay_enable)
870 {
871
872 if (proc_warning)
873 vlib_cli_output (vm, "warning: msg %d has different signature\n");
874
875 fromjson = am->msg_fromjson_handlers[msg_id];
876 if (!fromjson)
877 {
878 vlib_cli_output (vm, "missing fromjson convert function! id %d\n",
879 msg_id);
880 return rv;
881 }
882
883 msg = (u8 *) fromjson (o, &len);
884 if (!msg)
885 {
886 vlib_cli_output (vm, "failed to convert JSON (msg id %d)!\n",
887 msg_id);
888 return rv;
889 }
890
891 if (clib_arch_is_little_endian)
892 {
893 void (*endian_fp) (void *);
894 endian_fp = am->msg_endian_handlers[msg_id];
895 (*endian_fp) (msg);
896 }
897
898 void (*handler) (void *, vlib_main_t *);
899 handler = (void *) am->msg_handlers[msg_id];
900 if (!handler)
901 {
902 vlib_cli_output (vm, "no handler for msg id %d!\n", msg_id);
903 goto end;
904 }
905
906 if (!am->is_mp_safe[msg_id])
907 vl_msg_api_barrier_sync ();
908 (*handler) (msg, vm);
909 if (!am->is_mp_safe[msg_id])
910 vl_msg_api_barrier_release ();
911 }
912
913 rv = 0;
914end:
915 if (msg)
916 cJSON_free (msg);
917 return rv;
918}
919
920static void
921vl_msg_replay_json (vlib_main_t *vm, u8 *filename)
922{
923 api_main_t *am = vlibapi_get_main ();
924 cJSON *o = 0;
925 int rv = 0;
926 FILE *f = fopen ((char *) filename, "r");
927
928 if (!f)
929 {
930 vlib_cli_output (vm, "failed to open %s!\n", filename);
931 return;
932 }
933
934 char *buf = vl_msg_read_file (f);
935 fclose (f);
936
937 o = cJSON_Parse (buf);
938 vec_free (buf);
939 if (!o)
940 {
941 vlib_cli_output (vm, "%s: Failed parsing JSON input: %s\n", filename,
942 cJSON_GetErrorPtr ());
943 return;
944 }
945
946 if (cJSON_IsArray (o))
947 {
948 am->replay_in_progress = 1;
949 size_t size = cJSON_GetArraySize (o);
950 for (int i = 0; i < size; i++)
951 {
952 rv = vl_msg_exec_json_command (vm, cJSON_GetArrayItem (o, i));
953 if (rv < 0)
954 {
955 am->replay_in_progress = 0;
956 break;
957 }
958 }
959 }
960 else
961 {
962 rv = vl_msg_exec_json_command (vm, o);
963 }
964
965 if (rv < 0)
966 vlib_cli_output (vm, "error during replaying API trace");
967
968 cJSON_Delete (o);
969}
970
971static void
972vl_msg_dump_file_json (vlib_main_t *vm, u8 *filename)
973{
974 FILE *f = fopen ((char *) filename, "r");
975 char *buf;
976
977 if (!f)
978 {
979 vlib_cli_output (vm, "failed to open %s!\n", filename);
980 return;
981 }
982
983 buf = vl_msg_read_file (f);
984 fclose (f);
985
986 if (!buf)
987 {
988 vlib_cli_output (vm, "no content in %s!\n", filename);
989 return;
990 }
991
992 vlib_cli_output (vm, buf);
993 vec_free (buf);
994}
995
Dave Barachf66f8832020-01-23 08:44:40 -0500996/** api_trace_command_fn - control the binary API trace / replay feature
997
998 Note: this command MUST be marked thread-safe. Replay with
999 multiple worker threads depends in many cases on worker thread
1000 graph replica maintenance. If we (implicitly) assert a worker
1001 thread barrier at the debug CLI level, all graph replica changes
1002 are deferred until the replay operation completes. If an interface
1003 is deleted, the wheels fall off.
1004 */
1005
Florin Corase86a8ed2018-01-05 03:20:25 -08001006static clib_error_t *
1007api_trace_command_fn (vlib_main_t * vm,
1008 unformat_input_t * input, vlib_cli_command_t * cmd)
1009{
Neale Rannsecff1cb2020-04-22 12:14:52 -04001010 unformat_input_t _line_input, *line_input = &_line_input;
Florin Corase86a8ed2018-01-05 03:20:25 -08001011 u32 nitems = 256 << 10;
Dave Barach39d69112019-11-27 11:42:13 -05001012 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001013 vl_api_trace_which_t which = VL_API_TRACE_RX;
Dave Barachf35a0722019-06-12 16:50:38 -04001014 u8 *filename = 0;
1015 u8 *chroot_filename = 0;
Florin Corase86a8ed2018-01-05 03:20:25 -08001016 u32 first = 0;
1017 u32 last = (u32) ~ 0;
1018 FILE *fp;
1019 int rv;
1020
Neale Rannsecff1cb2020-04-22 12:14:52 -04001021 /* Get a line of input. */
1022 if (!unformat_user (input, unformat_line_input, line_input))
1023 return 0;
1024
1025 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
Florin Corase86a8ed2018-01-05 03:20:25 -08001026 {
Neale Rannsecff1cb2020-04-22 12:14:52 -04001027 if (unformat (line_input, "on") || unformat (line_input, "enable"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001028 {
Neale Rannsecff1cb2020-04-22 12:14:52 -04001029 if (unformat (line_input, "nitems %d", &nitems))
Florin Corase86a8ed2018-01-05 03:20:25 -08001030 ;
Dave Barachf66f8832020-01-23 08:44:40 -05001031 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001032 vl_msg_api_trace_configure (am, which, nitems);
1033 vl_msg_api_trace_onoff (am, which, 1 /* on */ );
Dave Barachf66f8832020-01-23 08:44:40 -05001034 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001035 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001036 else if (unformat (line_input, "off"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001037 {
Dave Barachf66f8832020-01-23 08:44:40 -05001038 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001039 vl_msg_api_trace_onoff (am, which, 0);
Dave Barachf66f8832020-01-23 08:44:40 -05001040 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001041 }
Filip Tehlar36217e32021-07-23 08:51:10 +00001042 else if (unformat (line_input, "save-json %s", &filename))
1043 {
1044 if (strstr ((char *) filename, "..") ||
1045 index ((char *) filename, '/'))
1046 {
1047 vlib_cli_output (vm, "illegal characters in filename '%s'",
1048 filename);
1049 goto out;
1050 }
1051
1052 chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1053
1054 vec_free (filename);
1055
1056 if (file_exists (chroot_filename))
1057 {
1058 vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1059 goto out;
1060 }
1061
1062 fp = fopen ((char *) chroot_filename, "w");
1063 if (fp == NULL)
1064 {
1065 vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
1066 goto out;
1067 }
1068 vlib_worker_thread_barrier_sync (vm);
1069 rv = vl_msg_api_trace_save (am, which, fp, 1);
1070 if (rv == -1)
1071 vlib_cli_output (vm, "API Trace data not present\n");
1072 else if (rv < 0)
1073 vlib_cli_output (vm, "failed to save api trace\n");
1074 else
1075 vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
1076 vlib_worker_thread_barrier_release (vm);
1077 fclose (fp);
1078 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001079 else if (unformat (line_input, "save %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001080 {
Florin Corase86a8ed2018-01-05 03:20:25 -08001081 if (strstr ((char *) filename, "..")
1082 || index ((char *) filename, '/'))
1083 {
1084 vlib_cli_output (vm, "illegal characters in filename '%s'",
1085 filename);
Dave Barachf35a0722019-06-12 16:50:38 -04001086 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -08001087 }
1088
1089 chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1090
1091 vec_free (filename);
1092
Filip Tehlar36217e32021-07-23 08:51:10 +00001093 if (file_exists (chroot_filename))
1094 {
1095 vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1096 goto out;
1097 }
1098
Florin Corase86a8ed2018-01-05 03:20:25 -08001099 fp = fopen ((char *) chroot_filename, "w");
1100 if (fp == NULL)
1101 {
1102 vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
Dave Barachf35a0722019-06-12 16:50:38 -04001103 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -08001104 }
Dave Barachf66f8832020-01-23 08:44:40 -05001105 vlib_worker_thread_barrier_sync (vm);
Filip Tehlar36217e32021-07-23 08:51:10 +00001106 rv = vl_msg_api_trace_save (am, which, fp, 0);
Dave Barachf66f8832020-01-23 08:44:40 -05001107 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001108 fclose (fp);
1109 if (rv == -1)
1110 vlib_cli_output (vm, "API Trace data not present\n");
1111 else if (rv == -2)
1112 vlib_cli_output (vm, "File for writing is closed\n");
1113 else if (rv == -10)
1114 vlib_cli_output (vm, "Error while writing header to file\n");
1115 else if (rv == -11)
1116 vlib_cli_output (vm, "Error while writing trace to file\n");
1117 else if (rv == -12)
1118 vlib_cli_output (vm,
1119 "Error while writing end of buffer trace to file\n");
1120 else if (rv == -13)
1121 vlib_cli_output (vm,
1122 "Error while writing start of buffer trace to file\n");
1123 else if (rv < 0)
Andrey "Zed" Zaikin701625b2018-04-18 17:07:07 +03001124 vlib_cli_output (vm, "Unknown error while saving: %d", rv);
Florin Corase86a8ed2018-01-05 03:20:25 -08001125 else
1126 vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
Dave Barachf35a0722019-06-12 16:50:38 -04001127 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -08001128 }
Filip Tehlar36217e32021-07-23 08:51:10 +00001129 else if (unformat (line_input, "tojson %s", &filename))
1130 {
1131 vl_msg_api_process_file (vm, filename, first, last, DUMP_JSON);
1132 }
1133 else if (unformat (line_input, "dump-file-json %s", &filename))
1134 {
1135 vl_msg_dump_file_json (vm, filename);
1136 }
1137 else if (unformat (line_input, "dump-file %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001138 {
1139 vl_msg_api_process_file (vm, filename, first, last, DUMP);
1140 }
Filip Tehlar36217e32021-07-23 08:51:10 +00001141 else if (unformat (line_input, "dump-json"))
1142 {
1143 vl_msg_api_dump_trace (vm, which, 1);
1144 }
1145 else if (unformat (line_input, "dump"))
1146 {
1147 vl_msg_api_dump_trace (vm, which, 0);
1148 }
1149 else if (unformat (line_input, "replay-json %s", &filename))
1150 {
1151 vl_msg_replay_json (vm, filename);
1152 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001153 else if (unformat (line_input, "replay %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001154 {
1155 vl_msg_api_process_file (vm, filename, first, last, REPLAY);
1156 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001157 else if (unformat (line_input, "initializers %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001158 {
1159 vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
1160 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001161 else if (unformat (line_input, "tx"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001162 {
1163 which = VL_API_TRACE_TX;
1164 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001165 else if (unformat (line_input, "first %d", &first))
Florin Corase86a8ed2018-01-05 03:20:25 -08001166 {
1167 ;
1168 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001169 else if (unformat (line_input, "last %d", &last))
Florin Corase86a8ed2018-01-05 03:20:25 -08001170 {
1171 ;
1172 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001173 else if (unformat (line_input, "status"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001174 {
1175 vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
1176 am, which);
1177 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001178 else if (unformat (line_input, "free"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001179 {
Dave Barachf66f8832020-01-23 08:44:40 -05001180 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001181 vl_msg_api_trace_onoff (am, which, 0);
1182 vl_msg_api_trace_free (am, which);
Dave Barachf66f8832020-01-23 08:44:40 -05001183 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001184 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001185 else if (unformat (line_input, "post-mortem-on"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001186 vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
Neale Rannsecff1cb2020-04-22 12:14:52 -04001187 else if (unformat (line_input, "post-mortem-off"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001188 vl_msg_api_post_mortem_dump_enable_disable (0 /* enable */ );
1189 else
1190 return clib_error_return (0, "unknown input `%U'",
1191 format_unformat_error, input);
1192 }
Dave Barachf35a0722019-06-12 16:50:38 -04001193out:
1194 vec_free (filename);
1195 vec_free (chroot_filename);
Neale Rannsecff1cb2020-04-22 12:14:52 -04001196 unformat_free (line_input);
Florin Corase86a8ed2018-01-05 03:20:25 -08001197 return 0;
1198}
1199
1200/*?
1201 * Display, replay, or save a binary API trace
1202?*/
1203
1204/* *INDENT-OFF* */
Filip Tehlarc863a912021-06-15 10:29:54 +00001205VLIB_CLI_COMMAND (api_trace_command, static) = {
Florin Corase86a8ed2018-01-05 03:20:25 -08001206 .path = "api trace",
Filip Tehlar36217e32021-07-23 08:51:10 +00001207 .short_help = "api trace [tx][on|off][first <n>][last <n>][status][free]"
1208 "[post-mortem-on][dump|dump-file|dump-json|save|tojson|save-"
1209 "json|replay <file>|replay-json <file>][nitems <n>]"
1210 "[initializers <file>]",
Florin Corase86a8ed2018-01-05 03:20:25 -08001211 .function = api_trace_command_fn,
Dave Barachf66f8832020-01-23 08:44:40 -05001212 .is_mp_safe = 1,
Florin Corase86a8ed2018-01-05 03:20:25 -08001213};
1214/* *INDENT-ON* */
1215
1216static clib_error_t *
Florin Corase86a8ed2018-01-05 03:20:25 -08001217api_trace_config_fn (vlib_main_t * vm, unformat_input_t * input)
1218{
1219 u32 nitems = 256 << 10;
1220 vl_api_trace_which_t which = VL_API_TRACE_RX;
Dave Barach39d69112019-11-27 11:42:13 -05001221 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001222
1223 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1224 {
1225 if (unformat (input, "on") || unformat (input, "enable"))
1226 {
1227 if (unformat (input, "nitems %d", &nitems))
1228 ;
1229 vl_msg_api_trace_configure (am, which, nitems);
1230 vl_msg_api_trace_onoff (am, which, 1 /* on */ );
1231 vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1232 }
1233 else if (unformat (input, "save-api-table %s",
1234 &am->save_msg_table_filename))
1235 ;
1236 else
1237 return clib_error_return (0, "unknown input `%U'",
1238 format_unformat_error, input);
1239 }
1240 return 0;
1241}
1242
1243/*?
1244 * This module has three configuration parameters:
1245 * "on" or "enable" - enables binary api tracing
1246 * "nitems <nnn>" - sets the size of the circular buffer to <nnn>
1247 * "save-api-table <filename>" - dumps the API message table to /tmp/<filename>
1248?*/
1249VLIB_CONFIG_FUNCTION (api_trace_config_fn, "api-trace");
1250
1251static clib_error_t *
1252api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
1253{
Dave Barach39d69112019-11-27 11:42:13 -05001254 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001255 u32 nitems;
1256
1257 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1258 {
1259 if (unformat (input, "length %d", &nitems) ||
1260 (unformat (input, "len %d", &nitems)))
1261 {
1262 if (nitems >= 1024)
1263 am->vlib_input_queue_length = nitems;
1264 else
1265 clib_warning ("vlib input queue length %d too small, ignored",
1266 nitems);
1267 }
1268 else
1269 return clib_error_return (0, "unknown input `%U'",
1270 format_unformat_error, input);
1271 }
1272 return 0;
1273}
1274
1275VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
1276
1277static u8 *
1278extract_name (u8 * s)
1279{
1280 u8 *rv;
1281
1282 rv = vec_dup (s);
1283
1284 while (vec_len (rv) && rv[vec_len (rv)] != '_')
1285 _vec_len (rv)--;
1286
1287 rv[vec_len (rv)] = 0;
1288
1289 return rv;
1290}
1291
1292static u8 *
1293extract_crc (u8 * s)
1294{
1295 int i;
1296 u8 *rv;
1297
1298 rv = vec_dup (s);
1299
1300 for (i = vec_len (rv) - 1; i >= 0; i--)
1301 {
1302 if (rv[i] == '_')
1303 {
1304 vec_delete (rv, i + 1, 0);
1305 break;
1306 }
1307 }
1308 return rv;
1309}
1310
1311typedef struct
1312{
1313 u8 *name_and_crc;
1314 u8 *name;
1315 u8 *crc;
1316 u32 msg_index;
1317 int which;
1318} msg_table_unserialize_t;
1319
1320static int
1321table_id_cmp (void *a1, void *a2)
1322{
1323 msg_table_unserialize_t *n1 = a1;
1324 msg_table_unserialize_t *n2 = a2;
1325
1326 return (n1->msg_index - n2->msg_index);
1327}
1328
1329static int
1330table_name_and_crc_cmp (void *a1, void *a2)
1331{
1332 msg_table_unserialize_t *n1 = a1;
1333 msg_table_unserialize_t *n2 = a2;
1334
1335 return strcmp ((char *) n1->name_and_crc, (char *) n2->name_and_crc);
1336}
1337
1338static clib_error_t *
1339dump_api_table_file_command_fn (vlib_main_t * vm,
1340 unformat_input_t * input,
1341 vlib_cli_command_t * cmd)
1342{
1343 u8 *filename = 0;
Dave Barach39d69112019-11-27 11:42:13 -05001344 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001345 serialize_main_t _sm, *sm = &_sm;
1346 clib_error_t *error;
1347 u32 nmsgs;
1348 u32 msg_index;
1349 u8 *name_and_crc;
1350 int compare_current = 0;
1351 int numeric_sort = 0;
1352 msg_table_unserialize_t *table = 0, *item;
1353 u32 i;
1354 u32 ndifferences = 0;
1355
1356 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1357 {
1358 if (unformat (input, "file %s", &filename))
1359 ;
1360 else if (unformat (input, "compare-current")
1361 || unformat (input, "compare"))
1362 compare_current = 1;
1363 else if (unformat (input, "numeric"))
1364 numeric_sort = 1;
1365 else
1366 return clib_error_return (0, "unknown input `%U'",
1367 format_unformat_error, input);
1368 }
1369
1370 if (numeric_sort && compare_current)
1371 return clib_error_return
1372 (0, "Comparison and numeric sorting are incompatible");
1373
1374 if (filename == 0)
1375 return clib_error_return (0, "File not specified");
1376
1377 /* Load the serialized message table from the table dump */
1378
1379 error = unserialize_open_clib_file (sm, (char *) filename);
1380
1381 if (error)
1382 return error;
1383
1384 unserialize_integer (sm, &nmsgs, sizeof (u32));
1385
1386 for (i = 0; i < nmsgs; i++)
1387 {
1388 msg_index = unserialize_likely_small_unsigned_integer (sm);
1389 unserialize_cstring (sm, (char **) &name_and_crc);
1390 vec_add2 (table, item, 1);
1391 item->msg_index = msg_index;
1392 item->name_and_crc = name_and_crc;
1393 item->name = extract_name (name_and_crc);
1394 item->crc = extract_crc (name_and_crc);
1395 item->which = 0; /* file */
1396 }
Ole Troanedfe2c02019-07-30 15:38:13 +02001397 unserialize_close (sm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001398
1399 /* Compare with the current image? */
1400 if (compare_current)
1401 {
1402 /* Append the current message table */
1403 u8 *tblv = vl_api_serialize_message_table (am, 0);
1404
1405 serialize_open_vector (sm, tblv);
1406 unserialize_integer (sm, &nmsgs, sizeof (u32));
1407
1408 for (i = 0; i < nmsgs; i++)
1409 {
1410 msg_index = unserialize_likely_small_unsigned_integer (sm);
1411 unserialize_cstring (sm, (char **) &name_and_crc);
1412
1413 vec_add2 (table, item, 1);
1414 item->msg_index = msg_index;
1415 item->name_and_crc = name_and_crc;
1416 item->name = extract_name (name_and_crc);
1417 item->crc = extract_crc (name_and_crc);
1418 item->which = 1; /* current_image */
1419 }
1420 vec_free (tblv);
1421 }
1422
1423 /* Sort the table. */
1424 if (numeric_sort)
1425 vec_sort_with_function (table, table_id_cmp);
1426 else
1427 vec_sort_with_function (table, table_name_and_crc_cmp);
1428
1429 if (compare_current)
1430 {
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001431 u8 *dashes = 0;
Florin Corase86a8ed2018-01-05 03:20:25 -08001432 ndifferences = 0;
1433
1434 /*
1435 * In this case, the recovered table will have two entries per
1436 * API message. So, if entries i and i+1 match, the message definitions
1437 * are identical. Otherwise, the crc is different, or a message is
1438 * present in only one of the tables.
1439 */
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001440 vlib_cli_output (vm, "%-60s | %s", "Message Name", "Result");
1441 vec_validate_init_empty (dashes, 60, '-');
1442 vec_terminate_c_string (dashes);
1443 vlib_cli_output (vm, "%60s-|-%s", dashes, "-----------------");
1444 vec_free (dashes);
Florin Corase86a8ed2018-01-05 03:20:25 -08001445 for (i = 0; i < vec_len (table);)
1446 {
1447 /* Last message lonely? */
1448 if (i == vec_len (table) - 1)
1449 {
1450 ndifferences++;
1451 goto last_unique;
1452 }
1453
1454 /* Identical pair? */
1455 if (!strncmp
1456 ((char *) table[i].name_and_crc,
1457 (char *) table[i + 1].name_and_crc,
1458 vec_len (table[i].name_and_crc)))
1459 {
1460 i += 2;
1461 continue;
1462 }
1463
1464 ndifferences++;
1465
1466 /* Only in one of two tables? */
Andrew Yourtchenkoae605b82019-01-21 16:39:33 +01001467 if (i + 1 == vec_len (table)
1468 || strcmp ((char *) table[i].name, (char *) table[i + 1].name))
Florin Corase86a8ed2018-01-05 03:20:25 -08001469 {
1470 last_unique:
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001471 vlib_cli_output (vm, "%-60s | only in %s",
Florin Corase86a8ed2018-01-05 03:20:25 -08001472 table[i].name, table[i].which ?
1473 "image" : "file");
1474 i++;
1475 continue;
1476 }
1477 /* In both tables, but with different signatures */
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001478 vlib_cli_output (vm, "%-60s | definition changed", table[i].name);
Florin Corase86a8ed2018-01-05 03:20:25 -08001479 i += 2;
1480 }
1481 if (ndifferences == 0)
1482 vlib_cli_output (vm, "No api message signature differences found.");
1483 else
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001484 vlib_cli_output (vm, "\nFound %u api message signature differences",
Florin Corase86a8ed2018-01-05 03:20:25 -08001485 ndifferences);
1486 goto cleanup;
1487 }
1488
1489 /* Dump the table, sorted as shown above */
1490 vlib_cli_output (vm, "%=60s %=8s %=10s", "Message name", "MsgID", "CRC");
1491
1492 for (i = 0; i < vec_len (table); i++)
1493 {
1494 item = table + i;
1495 vlib_cli_output (vm, "%-60s %8u %10s", item->name,
1496 item->msg_index, item->crc);
1497 }
1498
1499cleanup:
1500 for (i = 0; i < vec_len (table); i++)
1501 {
1502 vec_free (table[i].name_and_crc);
1503 vec_free (table[i].name);
1504 vec_free (table[i].crc);
1505 }
1506
1507 vec_free (table);
1508
1509 return 0;
1510}
1511
1512/*?
1513 * Displays a serialized API message decode table, sorted by message name
1514 *
1515 * @cliexpar
1516 * @cliexstart{show api dump file <filename>}
1517 * Message name MsgID CRC
1518 * accept_session 407 8e2a127e
1519 * accept_session_reply 408 67d8c22a
1520 * add_node_next 549 e4202993
1521 * add_node_next_reply 550 e89d6eed
1522 * etc.
1523 * @cliexend
1524?*/
1525
1526/*?
1527 * Compares a serialized API message decode table with the current image
1528 *
1529 * @cliexpar
1530 * @cliexstart{show api dump file <filename> compare}
1531 * ip_add_del_route definition changed
1532 * ip_table_add_del definition changed
1533 * l2_macs_event only in image
1534 * vnet_ip4_fib_counters only in file
1535 * vnet_ip4_nbr_counters only in file
1536 * @cliexend
1537?*/
1538
1539/*?
1540 * Display a serialized API message decode table, compare a saved
1541 * decode table with the current image, to establish API differences.
1542 *
1543?*/
1544/* *INDENT-OFF* */
1545VLIB_CLI_COMMAND (dump_api_table_file, static) =
1546{
1547 .path = "show api dump",
1548 .short_help = "show api dump file <filename> [numeric | compare-current]",
1549 .function = dump_api_table_file_command_fn,
1550};
1551
1552/* *INDENT-ON* */
1553/*
1554 * fd.io coding-style-patch-verification: ON
1555 *
1556 * Local Variables:
1557 * eval: (c-set-style "gnu")
1558 * End:
1559 */