blob: bdbedec009bc9f05b507c3ae1edb84e9a65ba30e [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
Damjan Marion79934e82022-04-05 12:40:31 +0200438 clib_mem_unpoison (hp, file_size);
Benoît Ganneb2f09142019-12-16 15:37:28 +0100439
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);
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100490 u32 msg_index2 = vl_msg_api_get_msg_index (name_and_crc);
491 ASSERT (~0 == msg_index2 || msg_index2 <= 65535);
492 if (~0 == msg_index2)
493 vlib_cli_output (vm, "warning: can't find msg index for id %d\n",
494 msg_index);
Ole Troanedfe2c02019-07-30 15:38:13 +0200495 vec_validate (msgid_vec, msg_index);
496 msgid_vec[msg_index] = msg_index2;
497 }
498
499 msg += msgtbl_size;
500
Florin Corase86a8ed2018-01-05 03:20:25 -0800501 for (i = 0; i < first_index; i++)
502 {
Florin Corase86a8ed2018-01-05 03:20:25 -0800503 int size;
504 u16 msg_id;
505
Filip Tehlar36217e32021-07-23 08:51:10 +0000506 assert_size (file_size_left, sizeof (u32));
Florin Corase86a8ed2018-01-05 03:20:25 -0800507 size = clib_host_to_net_u32 (*(u32 *) msg);
508 msg += sizeof (u32);
509
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100510 assert_size (file_size_left, clib_max (size, sizeof (u16)));
Ole Troanedfe2c02019-07-30 15:38:13 +0200511 msg_id = ntohs (*((u16 *) msg));
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100512 if (msg_id >= vec_len (msgid_vec) ||
513 msgid_vec[msg_id] >= vec_len (am->api_trace_cfg))
514 vlib_cli_output (vm, "warning: unknown msg id %d for msg number %d\n",
515 msg_id, i);
516
Florin Corase86a8ed2018-01-05 03:20:25 -0800517 msg += size;
518 }
519
520 if (which == REPLAY)
521 am->replay_in_progress = 1;
522
523 for (; i <= last_index; i++)
524 {
525 trace_cfg_t *cfgp;
Florin Corase86a8ed2018-01-05 03:20:25 -0800526 u16 msg_id;
527 int size;
528
529 if (which == DUMP)
530 vlib_cli_output (vm, "---------- trace %d -----------\n", i);
531
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100532 assert_size (file_size_left, sizeof (u32));
Florin Corase86a8ed2018-01-05 03:20:25 -0800533 size = clib_host_to_net_u32 (*(u32 *) msg);
534 msg += sizeof (u32);
535
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100536 assert_size (file_size_left, clib_max (size, sizeof (u16)));
Ole Troanedfe2c02019-07-30 15:38:13 +0200537 msg_id = ntohs (*((u16 *) msg));
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100538
539 if (msg_id >= vec_len (msgid_vec) ||
540 msgid_vec[msg_id] >= vec_len (am->api_trace_cfg))
Ole Troanedfe2c02019-07-30 15:38:13 +0200541 {
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100542 vlib_cli_output (
543 vm, "warning: unknown msg id %d for msg number %d, skipping\n",
544 msg_id, i);
545 msg += size;
546 continue;
Ole Troanedfe2c02019-07-30 15:38:13 +0200547 }
Florin Corase86a8ed2018-01-05 03:20:25 -0800548
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100549 msg_id = msgid_vec[msg_id];
Florin Corase86a8ed2018-01-05 03:20:25 -0800550 cfgp = am->api_trace_cfg + msg_id;
Florin Corase86a8ed2018-01-05 03:20:25 -0800551
552 /* Copy the buffer (from the read-only mmap'ed file) */
553 vec_validate (tmpbuf, size - 1 + sizeof (uword));
554 clib_memcpy (tmpbuf + sizeof (uword), msg, size);
Dave Barachb7b92992018-10-17 10:38:51 -0400555 clib_memset (tmpbuf, 0xf, sizeof (uword));
Florin Corase86a8ed2018-01-05 03:20:25 -0800556
557 /*
Ole Troanedfe2c02019-07-30 15:38:13 +0200558 * Endian swap if needed. All msg data is supposed to be in
559 * network byte order.
Florin Corase86a8ed2018-01-05 03:20:25 -0800560 */
Filip Tehlar36217e32021-07-23 08:51:10 +0000561 if (((which == DUMP || which == DUMP_JSON) &&
562 clib_arch_is_little_endian))
Florin Corase86a8ed2018-01-05 03:20:25 -0800563 {
564 void (*endian_fp) (void *);
565 if (msg_id >= vec_len (am->msg_endian_handlers)
566 || (am->msg_endian_handlers[msg_id] == 0))
567 {
568 vlib_cli_output (vm, "Ugh: msg id %d no endian swap\n", msg_id);
569 munmap (hp, file_size);
570 vec_free (tmpbuf);
571 am->replay_in_progress = 0;
572 return;
573 }
574 endian_fp = am->msg_endian_handlers[msg_id];
575 (*endian_fp) (tmpbuf + sizeof (uword));
576 }
577
578 /* msg_id always in network byte order */
579 if (clib_arch_is_little_endian)
580 {
Ole Troanedfe2c02019-07-30 15:38:13 +0200581 u16 *msg_idp = (u16 *) (tmpbuf + sizeof (uword));
Florin Corase86a8ed2018-01-05 03:20:25 -0800582 *msg_idp = msg_id;
583 }
584
585 switch (which)
586 {
Filip Tehlar36217e32021-07-23 08:51:10 +0000587 case DUMP_JSON:
588 if (msg_id < vec_len (am->msg_print_json_handlers) &&
589 am->msg_print_json_handlers[msg_id])
590 {
591 u8 *(*print_fp) (void *, void *);
592
593 print_fp = (void *) am->msg_print_json_handlers[msg_id];
594 (*print_fp) (tmpbuf + sizeof (uword), vm);
595 }
596 else
597 {
598 vlib_cli_output (vm, "Skipping msg id %d: no JSON print fcn\n",
599 msg_id);
600 break;
601 }
602 break;
603
Florin Corase86a8ed2018-01-05 03:20:25 -0800604 case DUMP:
605 if (msg_id < vec_len (am->msg_print_handlers) &&
606 am->msg_print_handlers[msg_id])
607 {
608 u8 *(*print_fp) (void *, void *);
609
610 print_fp = (void *) am->msg_print_handlers[msg_id];
611 (*print_fp) (tmpbuf + sizeof (uword), vm);
612 }
613 else
614 {
615 vlib_cli_output (vm, "Skipping msg id %d: no print fcn\n",
616 msg_id);
617 break;
618 }
619 break;
620
621 case INITIALIZERS:
622 if (msg_id < vec_len (am->msg_print_handlers) &&
623 am->msg_print_handlers[msg_id])
624 {
625 u8 *s;
626 int j;
627 u8 *(*print_fp) (void *, void *);
628
629 print_fp = (void *) am->msg_print_handlers[msg_id];
630
631 vlib_cli_output (vm, "/*");
632
633 (*print_fp) (tmpbuf + sizeof (uword), vm);
634 vlib_cli_output (vm, "*/\n");
635
636 s = format (0, "static u8 * vl_api_%s_%d[%d] = {",
637 am->msg_names[msg_id], i,
638 am->api_trace_cfg[msg_id].size);
639
640 for (j = 0; j < am->api_trace_cfg[msg_id].size; j++)
641 {
642 if ((j & 7) == 0)
643 s = format (s, "\n ");
644 s = format (s, "0x%02x,", tmpbuf[sizeof (uword) + j]);
645 }
646 s = format (s, "\n};\n%c", 0);
647 vlib_cli_output (vm, (char *) s);
648 vec_free (s);
649 }
650 break;
651
652 case REPLAY:
653 if (msg_id < vec_len (am->msg_print_handlers) &&
654 am->msg_print_handlers[msg_id] && cfgp->replay_enable)
655 {
John Loa56f2702018-06-25 20:38:35 -0400656 void (*handler) (void *, vlib_main_t *);
Florin Corase86a8ed2018-01-05 03:20:25 -0800657
658 handler = (void *) am->msg_handlers[msg_id];
659
660 if (!am->is_mp_safe[msg_id])
661 vl_msg_api_barrier_sync ();
John Loa56f2702018-06-25 20:38:35 -0400662 (*handler) (tmpbuf + sizeof (uword), vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800663 if (!am->is_mp_safe[msg_id])
664 vl_msg_api_barrier_release ();
665 }
666 else
667 {
668 if (cfgp->replay_enable)
669 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
670 msg_id);
671 break;
672 }
673 break;
674 }
675
Damjan Marion8bea5892022-04-04 22:40:45 +0200676 vec_set_len (tmpbuf, 0);
Florin Corase86a8ed2018-01-05 03:20:25 -0800677 msg += size;
678 }
679
Florin Corase86a8ed2018-01-05 03:20:25 -0800680 munmap (hp, file_size);
681 vec_free (tmpbuf);
Filip Tehlar36217e32021-07-23 08:51:10 +0000682 vec_free (msgid_vec);
Florin Corase86a8ed2018-01-05 03:20:25 -0800683 am->replay_in_progress = 0;
684}
685
Filip Tehlar36217e32021-07-23 08:51:10 +0000686static int
687file_exists (u8 *fname)
688{
689 FILE *fp = 0;
690 fp = fopen ((char *) fname, "r");
691 if (fp)
692 {
693 fclose (fp);
694 return 1;
695 }
696 return 0;
697}
698
699typedef struct
700{
701 vlib_main_t *vm;
702 u8 is_json;
703} vl_msg_print_args;
704
705static int
706vl_msg_print_trace (u8 *msg, void *ctx)
707{
708 vl_msg_print_args *a = ctx;
709 api_main_t *am = vlibapi_get_main ();
710 u16 msg_id = ntohs (*((u16 *) msg));
711 void (*print_fp) (void *, void *);
712 void (**handlers) (void *, void *);
713 u8 is_json = a->is_json;
714 u8 *tmpbuf = 0;
715
716 if (clib_arch_is_little_endian)
717 {
718 u32 msg_length = vec_len (msg);
719 vec_validate (tmpbuf, msg_length - 1);
720 clib_memcpy_fast (tmpbuf, msg, msg_length);
721 msg = tmpbuf;
722
723 void (*endian_fp) (void *);
724 endian_fp = am->msg_endian_handlers[msg_id];
725 (*endian_fp) (tmpbuf);
726 }
727
728 if (is_json)
729 handlers = am->msg_print_json_handlers;
730 else
731 handlers = am->msg_print_handlers;
732
733 if (msg_id < vec_len (handlers) && handlers[msg_id])
734 {
735 print_fp = (void *) handlers[msg_id];
736 (*print_fp) (msg, a->vm);
737 }
738 else
739 {
740 vlib_cli_output (a->vm, "Skipping msg id %d: no print fcn\n", msg_id);
741 }
742
743 vec_free (tmpbuf);
744 return 0;
745}
746
747static int
748vl_msg_api_dump_trace (vlib_main_t *vm, vl_api_trace_which_t which, u8 is_json)
749{
750 api_main_t *am = vlibapi_get_main ();
751 vl_api_trace_t *tp;
752
753 switch (which)
754 {
755 case VL_API_TRACE_TX:
756 tp = am->tx_trace;
757 break;
758 case VL_API_TRACE_RX:
759 tp = am->rx_trace;
760 break;
761 default:
762 return -1;
763 }
764
765 if (tp == 0 || tp->nitems == 0 || vec_len (tp->traces) == 0)
766 return -1;
767
768 vl_msg_print_args args;
769 clib_memset (&args, 0, sizeof (args));
770 args.is_json = is_json;
771 args.vm = vm;
772 vl_msg_traverse_trace (tp, vl_msg_print_trace, &args);
773
774 return 0;
775}
776
777static char *
778vl_msg_read_file (FILE *f)
779{
780 const size_t bufsize = 1024;
781 char *buf[bufsize], *v = 0;
782 size_t n;
783
784 while ((n = fread (buf, 1, bufsize, f)))
785 vec_add (v, buf, n);
786
Benoît Ganne2b9a4bf2021-10-11 13:49:59 +0200787 /* most callers expect a NULL-terminated C-string */
788 if (v)
789 vec_add1 (v, 0);
790
Filip Tehlar36217e32021-07-23 08:51:10 +0000791 return v;
792}
793
794static u16
795vl_msg_find_id_by_name_and_crc (vlib_main_t *vm, api_main_t *am, char *name)
796{
797 uword *p;
798 p = hash_get_mem (am->msg_index_by_name_and_crc, name);
799 if (!p)
800 return (u16) ~0;
801
802 return p[0];
803}
804
805static u16
806vl_msg_find_id_by_name (vlib_main_t *vm, api_main_t *am, char *name)
807{
808 uword *p;
809
810 if (!am->msg_id_by_name)
811 {
812 vlib_cli_output (vm, "message id table not yet initialized!\n");
813 return (u16) ~0;
814 }
815
816 p = hash_get_mem (am->msg_id_by_name, name);
817 if (!p)
818 return (u16) ~0;
819
820 return p[0];
821}
822
823static int
824vl_msg_exec_json_command (vlib_main_t *vm, cJSON *o)
825{
826 api_main_t *am = vlibapi_get_main ();
827 u16 msg_id;
828 void *(*fromjson) (cJSON *, int *);
829 int len = 0, rv = -1;
830 trace_cfg_t *cfgp;
831 u8 *msg = 0;
832
833 cJSON *msg_id_obj = cJSON_GetObjectItem (o, "_msgname");
834 if (!msg_id_obj)
835 {
836 vlib_cli_output (vm, "Missing '_msgname' element!\n");
837 return rv;
838 }
839 char *name = cJSON_GetStringValue (msg_id_obj);
840
841 cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc");
Florin Corasf6e6da92021-10-12 08:41:09 -0700842 if (!crc_obj)
Filip Tehlar36217e32021-07-23 08:51:10 +0000843 {
844 vlib_cli_output (vm, "Missing '_crc' element!\n");
845 return rv;
846 }
847 char *crc = cJSON_GetStringValue (crc_obj);
848 u8 proc_warning = 0;
849
850 u8 *name_crc = format (0, "%s_%s%c", name, crc, 0);
851 msg_id = vl_msg_find_id_by_name_and_crc (vm, am, (char *) name_crc);
852 if (msg_id == (u16) ~0)
853 {
854 msg_id = vl_msg_find_id_by_name (vm, am, name);
855 if (msg_id == (u16) ~0)
856 {
857 vlib_cli_output (vm, "unknown msg id %d!\n", msg_id);
858 vec_free (name_crc);
859 return rv;
860 }
861 proc_warning = 1;
862 }
863 vec_free (name_crc);
864
865 cfgp = am->api_trace_cfg + msg_id;
Florin Corasf6e6da92021-10-12 08:41:09 -0700866 if (!am->api_trace_cfg)
Filip Tehlar36217e32021-07-23 08:51:10 +0000867 {
868 vlib_cli_output (vm, "msg id %d no trace config\n", msg_id);
869 return rv;
870 }
871
872 if (cfgp->replay_enable)
873 {
874
875 if (proc_warning)
876 vlib_cli_output (vm, "warning: msg %d has different signature\n");
877
878 fromjson = am->msg_fromjson_handlers[msg_id];
879 if (!fromjson)
880 {
881 vlib_cli_output (vm, "missing fromjson convert function! id %d\n",
882 msg_id);
883 return rv;
884 }
885
886 msg = (u8 *) fromjson (o, &len);
887 if (!msg)
888 {
889 vlib_cli_output (vm, "failed to convert JSON (msg id %d)!\n",
890 msg_id);
891 return rv;
892 }
893
894 if (clib_arch_is_little_endian)
895 {
896 void (*endian_fp) (void *);
897 endian_fp = am->msg_endian_handlers[msg_id];
898 (*endian_fp) (msg);
899 }
900
901 void (*handler) (void *, vlib_main_t *);
902 handler = (void *) am->msg_handlers[msg_id];
903 if (!handler)
904 {
905 vlib_cli_output (vm, "no handler for msg id %d!\n", msg_id);
906 goto end;
907 }
908
909 if (!am->is_mp_safe[msg_id])
910 vl_msg_api_barrier_sync ();
911 (*handler) (msg, vm);
912 if (!am->is_mp_safe[msg_id])
913 vl_msg_api_barrier_release ();
914 }
915
916 rv = 0;
917end:
918 if (msg)
919 cJSON_free (msg);
920 return rv;
921}
922
923static void
924vl_msg_replay_json (vlib_main_t *vm, u8 *filename)
925{
926 api_main_t *am = vlibapi_get_main ();
927 cJSON *o = 0;
928 int rv = 0;
929 FILE *f = fopen ((char *) filename, "r");
930
931 if (!f)
932 {
933 vlib_cli_output (vm, "failed to open %s!\n", filename);
934 return;
935 }
936
937 char *buf = vl_msg_read_file (f);
938 fclose (f);
939
940 o = cJSON_Parse (buf);
941 vec_free (buf);
942 if (!o)
943 {
944 vlib_cli_output (vm, "%s: Failed parsing JSON input: %s\n", filename,
945 cJSON_GetErrorPtr ());
946 return;
947 }
948
949 if (cJSON_IsArray (o))
950 {
951 am->replay_in_progress = 1;
952 size_t size = cJSON_GetArraySize (o);
953 for (int i = 0; i < size; i++)
954 {
955 rv = vl_msg_exec_json_command (vm, cJSON_GetArrayItem (o, i));
956 if (rv < 0)
957 {
958 am->replay_in_progress = 0;
959 break;
960 }
961 }
962 }
963 else
964 {
965 rv = vl_msg_exec_json_command (vm, o);
966 }
967
968 if (rv < 0)
969 vlib_cli_output (vm, "error during replaying API trace");
970
971 cJSON_Delete (o);
972}
973
974static void
975vl_msg_dump_file_json (vlib_main_t *vm, u8 *filename)
976{
977 FILE *f = fopen ((char *) filename, "r");
978 char *buf;
979
980 if (!f)
981 {
982 vlib_cli_output (vm, "failed to open %s!\n", filename);
983 return;
984 }
985
986 buf = vl_msg_read_file (f);
987 fclose (f);
988
989 if (!buf)
990 {
991 vlib_cli_output (vm, "no content in %s!\n", filename);
992 return;
993 }
994
995 vlib_cli_output (vm, buf);
996 vec_free (buf);
997}
998
Dave Barachf66f8832020-01-23 08:44:40 -0500999/** api_trace_command_fn - control the binary API trace / replay feature
1000
1001 Note: this command MUST be marked thread-safe. Replay with
1002 multiple worker threads depends in many cases on worker thread
1003 graph replica maintenance. If we (implicitly) assert a worker
1004 thread barrier at the debug CLI level, all graph replica changes
1005 are deferred until the replay operation completes. If an interface
1006 is deleted, the wheels fall off.
1007 */
1008
Florin Corase86a8ed2018-01-05 03:20:25 -08001009static clib_error_t *
1010api_trace_command_fn (vlib_main_t * vm,
1011 unformat_input_t * input, vlib_cli_command_t * cmd)
1012{
Neale Rannsecff1cb2020-04-22 12:14:52 -04001013 unformat_input_t _line_input, *line_input = &_line_input;
Florin Corase86a8ed2018-01-05 03:20:25 -08001014 u32 nitems = 256 << 10;
Dave Barach39d69112019-11-27 11:42:13 -05001015 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001016 vl_api_trace_which_t which = VL_API_TRACE_RX;
Dave Barachf35a0722019-06-12 16:50:38 -04001017 u8 *filename = 0;
1018 u8 *chroot_filename = 0;
Florin Corase86a8ed2018-01-05 03:20:25 -08001019 u32 first = 0;
1020 u32 last = (u32) ~ 0;
1021 FILE *fp;
1022 int rv;
1023
Neale Rannsecff1cb2020-04-22 12:14:52 -04001024 /* Get a line of input. */
1025 if (!unformat_user (input, unformat_line_input, line_input))
1026 return 0;
1027
1028 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
Florin Corase86a8ed2018-01-05 03:20:25 -08001029 {
Neale Rannsecff1cb2020-04-22 12:14:52 -04001030 if (unformat (line_input, "on") || unformat (line_input, "enable"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001031 {
Neale Rannsecff1cb2020-04-22 12:14:52 -04001032 if (unformat (line_input, "nitems %d", &nitems))
Florin Corase86a8ed2018-01-05 03:20:25 -08001033 ;
Dave Barachf66f8832020-01-23 08:44:40 -05001034 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001035 vl_msg_api_trace_configure (am, which, nitems);
1036 vl_msg_api_trace_onoff (am, which, 1 /* on */ );
Dave Barachf66f8832020-01-23 08:44:40 -05001037 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001038 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001039 else if (unformat (line_input, "off"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001040 {
Dave Barachf66f8832020-01-23 08:44:40 -05001041 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001042 vl_msg_api_trace_onoff (am, which, 0);
Dave Barachf66f8832020-01-23 08:44:40 -05001043 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001044 }
Filip Tehlar36217e32021-07-23 08:51:10 +00001045 else if (unformat (line_input, "save-json %s", &filename))
1046 {
1047 if (strstr ((char *) filename, "..") ||
1048 index ((char *) filename, '/'))
1049 {
1050 vlib_cli_output (vm, "illegal characters in filename '%s'",
1051 filename);
1052 goto out;
1053 }
1054
1055 chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1056
1057 vec_free (filename);
1058
1059 if (file_exists (chroot_filename))
1060 {
1061 vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1062 goto out;
1063 }
1064
1065 fp = fopen ((char *) chroot_filename, "w");
1066 if (fp == NULL)
1067 {
1068 vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
1069 goto out;
1070 }
1071 vlib_worker_thread_barrier_sync (vm);
1072 rv = vl_msg_api_trace_save (am, which, fp, 1);
1073 if (rv == -1)
1074 vlib_cli_output (vm, "API Trace data not present\n");
1075 else if (rv < 0)
1076 vlib_cli_output (vm, "failed to save api trace\n");
1077 else
1078 vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
1079 vlib_worker_thread_barrier_release (vm);
1080 fclose (fp);
1081 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001082 else if (unformat (line_input, "save %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001083 {
Florin Corase86a8ed2018-01-05 03:20:25 -08001084 if (strstr ((char *) filename, "..")
1085 || index ((char *) filename, '/'))
1086 {
1087 vlib_cli_output (vm, "illegal characters in filename '%s'",
1088 filename);
Dave Barachf35a0722019-06-12 16:50:38 -04001089 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -08001090 }
1091
1092 chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1093
1094 vec_free (filename);
1095
Filip Tehlar36217e32021-07-23 08:51:10 +00001096 if (file_exists (chroot_filename))
1097 {
1098 vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1099 goto out;
1100 }
1101
Florin Corase86a8ed2018-01-05 03:20:25 -08001102 fp = fopen ((char *) chroot_filename, "w");
1103 if (fp == NULL)
1104 {
1105 vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
Dave Barachf35a0722019-06-12 16:50:38 -04001106 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -08001107 }
Dave Barachf66f8832020-01-23 08:44:40 -05001108 vlib_worker_thread_barrier_sync (vm);
Filip Tehlar36217e32021-07-23 08:51:10 +00001109 rv = vl_msg_api_trace_save (am, which, fp, 0);
Dave Barachf66f8832020-01-23 08:44:40 -05001110 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001111 fclose (fp);
1112 if (rv == -1)
1113 vlib_cli_output (vm, "API Trace data not present\n");
1114 else if (rv == -2)
1115 vlib_cli_output (vm, "File for writing is closed\n");
1116 else if (rv == -10)
1117 vlib_cli_output (vm, "Error while writing header to file\n");
1118 else if (rv == -11)
1119 vlib_cli_output (vm, "Error while writing trace to file\n");
1120 else if (rv == -12)
1121 vlib_cli_output (vm,
1122 "Error while writing end of buffer trace to file\n");
1123 else if (rv == -13)
1124 vlib_cli_output (vm,
1125 "Error while writing start of buffer trace to file\n");
1126 else if (rv < 0)
Andrey "Zed" Zaikin701625b2018-04-18 17:07:07 +03001127 vlib_cli_output (vm, "Unknown error while saving: %d", rv);
Florin Corase86a8ed2018-01-05 03:20:25 -08001128 else
1129 vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
Dave Barachf35a0722019-06-12 16:50:38 -04001130 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -08001131 }
Filip Tehlar36217e32021-07-23 08:51:10 +00001132 else if (unformat (line_input, "tojson %s", &filename))
1133 {
1134 vl_msg_api_process_file (vm, filename, first, last, DUMP_JSON);
1135 }
1136 else if (unformat (line_input, "dump-file-json %s", &filename))
1137 {
1138 vl_msg_dump_file_json (vm, filename);
1139 }
1140 else if (unformat (line_input, "dump-file %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001141 {
1142 vl_msg_api_process_file (vm, filename, first, last, DUMP);
1143 }
Filip Tehlar36217e32021-07-23 08:51:10 +00001144 else if (unformat (line_input, "dump-json"))
1145 {
1146 vl_msg_api_dump_trace (vm, which, 1);
1147 }
1148 else if (unformat (line_input, "dump"))
1149 {
1150 vl_msg_api_dump_trace (vm, which, 0);
1151 }
1152 else if (unformat (line_input, "replay-json %s", &filename))
1153 {
1154 vl_msg_replay_json (vm, filename);
1155 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001156 else if (unformat (line_input, "replay %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001157 {
1158 vl_msg_api_process_file (vm, filename, first, last, REPLAY);
1159 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001160 else if (unformat (line_input, "initializers %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001161 {
1162 vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
1163 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001164 else if (unformat (line_input, "tx"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001165 {
1166 which = VL_API_TRACE_TX;
1167 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001168 else if (unformat (line_input, "first %d", &first))
Florin Corase86a8ed2018-01-05 03:20:25 -08001169 {
1170 ;
1171 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001172 else if (unformat (line_input, "last %d", &last))
Florin Corase86a8ed2018-01-05 03:20:25 -08001173 {
1174 ;
1175 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001176 else if (unformat (line_input, "status"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001177 {
1178 vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
1179 am, which);
1180 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001181 else if (unformat (line_input, "free"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001182 {
Dave Barachf66f8832020-01-23 08:44:40 -05001183 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001184 vl_msg_api_trace_onoff (am, which, 0);
1185 vl_msg_api_trace_free (am, which);
Dave Barachf66f8832020-01-23 08:44:40 -05001186 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001187 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001188 else if (unformat (line_input, "post-mortem-on"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001189 vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
Neale Rannsecff1cb2020-04-22 12:14:52 -04001190 else if (unformat (line_input, "post-mortem-off"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001191 vl_msg_api_post_mortem_dump_enable_disable (0 /* enable */ );
1192 else
1193 return clib_error_return (0, "unknown input `%U'",
1194 format_unformat_error, input);
1195 }
Dave Barachf35a0722019-06-12 16:50:38 -04001196out:
1197 vec_free (filename);
1198 vec_free (chroot_filename);
Neale Rannsecff1cb2020-04-22 12:14:52 -04001199 unformat_free (line_input);
Florin Corase86a8ed2018-01-05 03:20:25 -08001200 return 0;
1201}
1202
1203/*?
1204 * Display, replay, or save a binary API trace
1205?*/
1206
1207/* *INDENT-OFF* */
Filip Tehlarc863a912021-06-15 10:29:54 +00001208VLIB_CLI_COMMAND (api_trace_command, static) = {
Florin Corase86a8ed2018-01-05 03:20:25 -08001209 .path = "api trace",
Filip Tehlar36217e32021-07-23 08:51:10 +00001210 .short_help = "api trace [tx][on|off][first <n>][last <n>][status][free]"
1211 "[post-mortem-on][dump|dump-file|dump-json|save|tojson|save-"
1212 "json|replay <file>|replay-json <file>][nitems <n>]"
1213 "[initializers <file>]",
Florin Corase86a8ed2018-01-05 03:20:25 -08001214 .function = api_trace_command_fn,
Dave Barachf66f8832020-01-23 08:44:40 -05001215 .is_mp_safe = 1,
Florin Corase86a8ed2018-01-05 03:20:25 -08001216};
1217/* *INDENT-ON* */
1218
1219static clib_error_t *
Florin Corase86a8ed2018-01-05 03:20:25 -08001220api_trace_config_fn (vlib_main_t * vm, unformat_input_t * input)
1221{
1222 u32 nitems = 256 << 10;
1223 vl_api_trace_which_t which = VL_API_TRACE_RX;
Dave Barach39d69112019-11-27 11:42:13 -05001224 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001225
1226 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1227 {
1228 if (unformat (input, "on") || unformat (input, "enable"))
1229 {
1230 if (unformat (input, "nitems %d", &nitems))
1231 ;
1232 vl_msg_api_trace_configure (am, which, nitems);
1233 vl_msg_api_trace_onoff (am, which, 1 /* on */ );
1234 vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1235 }
1236 else if (unformat (input, "save-api-table %s",
1237 &am->save_msg_table_filename))
1238 ;
1239 else
1240 return clib_error_return (0, "unknown input `%U'",
1241 format_unformat_error, input);
1242 }
1243 return 0;
1244}
1245
1246/*?
1247 * This module has three configuration parameters:
1248 * "on" or "enable" - enables binary api tracing
1249 * "nitems <nnn>" - sets the size of the circular buffer to <nnn>
1250 * "save-api-table <filename>" - dumps the API message table to /tmp/<filename>
1251?*/
1252VLIB_CONFIG_FUNCTION (api_trace_config_fn, "api-trace");
1253
1254static clib_error_t *
1255api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
1256{
Dave Barach39d69112019-11-27 11:42:13 -05001257 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001258 u32 nitems;
1259
1260 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1261 {
1262 if (unformat (input, "length %d", &nitems) ||
1263 (unformat (input, "len %d", &nitems)))
1264 {
1265 if (nitems >= 1024)
1266 am->vlib_input_queue_length = nitems;
1267 else
1268 clib_warning ("vlib input queue length %d too small, ignored",
1269 nitems);
1270 }
1271 else
1272 return clib_error_return (0, "unknown input `%U'",
1273 format_unformat_error, input);
1274 }
1275 return 0;
1276}
1277
1278VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
1279
1280static u8 *
1281extract_name (u8 * s)
1282{
1283 u8 *rv;
1284
1285 rv = vec_dup (s);
1286
1287 while (vec_len (rv) && rv[vec_len (rv)] != '_')
Damjan Marion8bea5892022-04-04 22:40:45 +02001288 vec_dec_len (rv, 0);
Florin Corase86a8ed2018-01-05 03:20:25 -08001289
1290 rv[vec_len (rv)] = 0;
1291
1292 return rv;
1293}
1294
1295static u8 *
1296extract_crc (u8 * s)
1297{
1298 int i;
1299 u8 *rv;
1300
1301 rv = vec_dup (s);
1302
1303 for (i = vec_len (rv) - 1; i >= 0; i--)
1304 {
1305 if (rv[i] == '_')
1306 {
1307 vec_delete (rv, i + 1, 0);
1308 break;
1309 }
1310 }
1311 return rv;
1312}
1313
1314typedef struct
1315{
1316 u8 *name_and_crc;
1317 u8 *name;
1318 u8 *crc;
1319 u32 msg_index;
1320 int which;
1321} msg_table_unserialize_t;
1322
1323static int
1324table_id_cmp (void *a1, void *a2)
1325{
1326 msg_table_unserialize_t *n1 = a1;
1327 msg_table_unserialize_t *n2 = a2;
1328
1329 return (n1->msg_index - n2->msg_index);
1330}
1331
1332static int
1333table_name_and_crc_cmp (void *a1, void *a2)
1334{
1335 msg_table_unserialize_t *n1 = a1;
1336 msg_table_unserialize_t *n2 = a2;
1337
1338 return strcmp ((char *) n1->name_and_crc, (char *) n2->name_and_crc);
1339}
1340
1341static clib_error_t *
1342dump_api_table_file_command_fn (vlib_main_t * vm,
1343 unformat_input_t * input,
1344 vlib_cli_command_t * cmd)
1345{
1346 u8 *filename = 0;
Dave Barach39d69112019-11-27 11:42:13 -05001347 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001348 serialize_main_t _sm, *sm = &_sm;
1349 clib_error_t *error;
1350 u32 nmsgs;
1351 u32 msg_index;
1352 u8 *name_and_crc;
1353 int compare_current = 0;
1354 int numeric_sort = 0;
1355 msg_table_unserialize_t *table = 0, *item;
1356 u32 i;
1357 u32 ndifferences = 0;
1358
1359 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1360 {
1361 if (unformat (input, "file %s", &filename))
1362 ;
1363 else if (unformat (input, "compare-current")
1364 || unformat (input, "compare"))
1365 compare_current = 1;
1366 else if (unformat (input, "numeric"))
1367 numeric_sort = 1;
1368 else
1369 return clib_error_return (0, "unknown input `%U'",
1370 format_unformat_error, input);
1371 }
1372
1373 if (numeric_sort && compare_current)
1374 return clib_error_return
1375 (0, "Comparison and numeric sorting are incompatible");
1376
1377 if (filename == 0)
1378 return clib_error_return (0, "File not specified");
1379
1380 /* Load the serialized message table from the table dump */
1381
1382 error = unserialize_open_clib_file (sm, (char *) filename);
1383
1384 if (error)
1385 return error;
1386
1387 unserialize_integer (sm, &nmsgs, sizeof (u32));
1388
1389 for (i = 0; i < nmsgs; i++)
1390 {
1391 msg_index = unserialize_likely_small_unsigned_integer (sm);
1392 unserialize_cstring (sm, (char **) &name_and_crc);
1393 vec_add2 (table, item, 1);
1394 item->msg_index = msg_index;
1395 item->name_and_crc = name_and_crc;
1396 item->name = extract_name (name_and_crc);
1397 item->crc = extract_crc (name_and_crc);
1398 item->which = 0; /* file */
1399 }
Ole Troanedfe2c02019-07-30 15:38:13 +02001400 unserialize_close (sm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001401
1402 /* Compare with the current image? */
1403 if (compare_current)
1404 {
1405 /* Append the current message table */
1406 u8 *tblv = vl_api_serialize_message_table (am, 0);
1407
1408 serialize_open_vector (sm, tblv);
1409 unserialize_integer (sm, &nmsgs, sizeof (u32));
1410
1411 for (i = 0; i < nmsgs; i++)
1412 {
1413 msg_index = unserialize_likely_small_unsigned_integer (sm);
1414 unserialize_cstring (sm, (char **) &name_and_crc);
1415
1416 vec_add2 (table, item, 1);
1417 item->msg_index = msg_index;
1418 item->name_and_crc = name_and_crc;
1419 item->name = extract_name (name_and_crc);
1420 item->crc = extract_crc (name_and_crc);
1421 item->which = 1; /* current_image */
1422 }
1423 vec_free (tblv);
1424 }
1425
1426 /* Sort the table. */
1427 if (numeric_sort)
1428 vec_sort_with_function (table, table_id_cmp);
1429 else
1430 vec_sort_with_function (table, table_name_and_crc_cmp);
1431
1432 if (compare_current)
1433 {
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001434 u8 *dashes = 0;
Florin Corase86a8ed2018-01-05 03:20:25 -08001435 ndifferences = 0;
1436
1437 /*
1438 * In this case, the recovered table will have two entries per
1439 * API message. So, if entries i and i+1 match, the message definitions
1440 * are identical. Otherwise, the crc is different, or a message is
1441 * present in only one of the tables.
1442 */
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001443 vlib_cli_output (vm, "%-60s | %s", "Message Name", "Result");
1444 vec_validate_init_empty (dashes, 60, '-');
1445 vec_terminate_c_string (dashes);
1446 vlib_cli_output (vm, "%60s-|-%s", dashes, "-----------------");
1447 vec_free (dashes);
Florin Corase86a8ed2018-01-05 03:20:25 -08001448 for (i = 0; i < vec_len (table);)
1449 {
1450 /* Last message lonely? */
1451 if (i == vec_len (table) - 1)
1452 {
1453 ndifferences++;
1454 goto last_unique;
1455 }
1456
1457 /* Identical pair? */
1458 if (!strncmp
1459 ((char *) table[i].name_and_crc,
1460 (char *) table[i + 1].name_and_crc,
1461 vec_len (table[i].name_and_crc)))
1462 {
1463 i += 2;
1464 continue;
1465 }
1466
1467 ndifferences++;
1468
1469 /* Only in one of two tables? */
Andrew Yourtchenkoae605b82019-01-21 16:39:33 +01001470 if (i + 1 == vec_len (table)
1471 || strcmp ((char *) table[i].name, (char *) table[i + 1].name))
Florin Corase86a8ed2018-01-05 03:20:25 -08001472 {
1473 last_unique:
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001474 vlib_cli_output (vm, "%-60s | only in %s",
Florin Corase86a8ed2018-01-05 03:20:25 -08001475 table[i].name, table[i].which ?
1476 "image" : "file");
1477 i++;
1478 continue;
1479 }
1480 /* In both tables, but with different signatures */
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001481 vlib_cli_output (vm, "%-60s | definition changed", table[i].name);
Florin Corase86a8ed2018-01-05 03:20:25 -08001482 i += 2;
1483 }
1484 if (ndifferences == 0)
1485 vlib_cli_output (vm, "No api message signature differences found.");
1486 else
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001487 vlib_cli_output (vm, "\nFound %u api message signature differences",
Florin Corase86a8ed2018-01-05 03:20:25 -08001488 ndifferences);
1489 goto cleanup;
1490 }
1491
1492 /* Dump the table, sorted as shown above */
1493 vlib_cli_output (vm, "%=60s %=8s %=10s", "Message name", "MsgID", "CRC");
1494
1495 for (i = 0; i < vec_len (table); i++)
1496 {
1497 item = table + i;
1498 vlib_cli_output (vm, "%-60s %8u %10s", item->name,
1499 item->msg_index, item->crc);
1500 }
1501
1502cleanup:
1503 for (i = 0; i < vec_len (table); i++)
1504 {
1505 vec_free (table[i].name_and_crc);
1506 vec_free (table[i].name);
1507 vec_free (table[i].crc);
1508 }
1509
1510 vec_free (table);
1511
1512 return 0;
1513}
1514
1515/*?
1516 * Displays a serialized API message decode table, sorted by message name
1517 *
1518 * @cliexpar
1519 * @cliexstart{show api dump file <filename>}
1520 * Message name MsgID CRC
1521 * accept_session 407 8e2a127e
1522 * accept_session_reply 408 67d8c22a
1523 * add_node_next 549 e4202993
1524 * add_node_next_reply 550 e89d6eed
1525 * etc.
1526 * @cliexend
1527?*/
1528
1529/*?
1530 * Compares a serialized API message decode table with the current image
1531 *
1532 * @cliexpar
1533 * @cliexstart{show api dump file <filename> compare}
1534 * ip_add_del_route definition changed
1535 * ip_table_add_del definition changed
1536 * l2_macs_event only in image
1537 * vnet_ip4_fib_counters only in file
1538 * vnet_ip4_nbr_counters only in file
1539 * @cliexend
1540?*/
1541
1542/*?
1543 * Display a serialized API message decode table, compare a saved
1544 * decode table with the current image, to establish API differences.
1545 *
1546?*/
1547/* *INDENT-OFF* */
1548VLIB_CLI_COMMAND (dump_api_table_file, static) =
1549{
1550 .path = "show api dump",
1551 .short_help = "show api dump file <filename> [numeric | compare-current]",
1552 .function = dump_api_table_file_command_fn,
1553};
1554
1555/* *INDENT-ON* */
1556/*
1557 * fd.io coding-style-patch-verification: ON
1558 *
1559 * Local Variables:
1560 * eval: (c-set-style "gnu")
1561 * End:
1562 */