blob: f9cda9b34e954f9f7bd3db94e34fb5d6a69fce7d [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
Damjan Marioncada9eb2022-05-18 22:16:11 +0200245 for (i = 1; i < vec_len (am->msg_data); i++)
Florin Corase86a8ed2018-01-05 03:20:25 -0800246 {
Damjan Marioncada9eb2022-05-18 22:16:11 +0200247 vl_api_msg_data_t *m = vl_api_get_msg_data (am, i);
Florin Corase86a8ed2018-01-05 03:20:25 -0800248 if (verbose == 0)
249 {
250 vlib_cli_output (vm, "%-4d %s", i,
Damjan Marioncada9eb2022-05-18 22:16:11 +0200251 m->name ? m->name : " [no handler]");
Florin Corase86a8ed2018-01-05 03:20:25 -0800252 }
253 else
254 {
255 vlib_cli_output (vm, "%-4d %-40s %6d %7d", i,
Damjan Marioncada9eb2022-05-18 22:16:11 +0200256 m->name ? m->name : " [no handler]", m->bounce,
257 m->is_mp_safe);
Florin Corase86a8ed2018-01-05 03:20:25 -0800258 }
259 }
260
261 return 0;
262}
263
264/*?
265 * Display the current api message decode tables
266?*/
267/* *INDENT-OFF* */
268VLIB_CLI_COMMAND (cli_show_api_message_table_command, static) =
269{
270 .path = "show api message-table",
271 .short_help = "Message Table",
272 .function = vl_api_message_table_command,
273};
274/* *INDENT-ON* */
275
276static int
277range_compare (vl_api_msg_range_t * a0, vl_api_msg_range_t * a1)
278{
279 int len0, len1, clen;
280
281 len0 = vec_len (a0->name);
282 len1 = vec_len (a1->name);
283 clen = len0 < len1 ? len0 : len1;
284 return (strncmp ((char *) a0->name, (char *) a1->name, clen));
285}
286
287static u8 *
288format_api_msg_range (u8 * s, va_list * args)
289{
290 vl_api_msg_range_t *rp = va_arg (*args, vl_api_msg_range_t *);
291
292 if (rp == 0)
293 s = format (s, "%-50s%9s%9s", "Name", "First-ID", "Last-ID");
294 else
295 s = format (s, "%-50s%9d%9d", rp->name, rp->first_msg_id,
296 rp->last_msg_id);
297
298 return s;
299}
300
301static clib_error_t *
302vl_api_show_plugin_command (vlib_main_t * vm,
303 unformat_input_t * input,
304 vlib_cli_command_t * cli_cmd)
305{
Dave Barach39d69112019-11-27 11:42:13 -0500306 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800307 vl_api_msg_range_t *rp = 0;
308 int i;
309
310 if (vec_len (am->msg_ranges) == 0)
311 {
312 vlib_cli_output (vm, "No plugin API message ranges configured...");
313 return 0;
314 }
315
316 rp = vec_dup (am->msg_ranges);
317
318 vec_sort_with_function (rp, range_compare);
319
320 vlib_cli_output (vm, "Plugin API message ID ranges...\n");
321 vlib_cli_output (vm, "%U", format_api_msg_range, 0 /* header */ );
322
323 for (i = 0; i < vec_len (rp); i++)
324 vlib_cli_output (vm, "%U", format_api_msg_range, rp + i);
325
326 vec_free (rp);
327
328 return 0;
329}
330
331/*?
332 * Display the plugin binary API message range table
333?*/
334/* *INDENT-OFF* */
335VLIB_CLI_COMMAND (cli_show_api_plugin_command, static) =
336{
337 .path = "show api plugin",
338 .short_help = "show api plugin",
339 .function = vl_api_show_plugin_command,
340};
341/* *INDENT-ON* */
342
343typedef enum
344{
345 DUMP,
Filip Tehlar36217e32021-07-23 08:51:10 +0000346 DUMP_JSON,
Florin Corase86a8ed2018-01-05 03:20:25 -0800347 REPLAY,
348 INITIALIZERS,
349} vl_api_replay_t;
350
351u8 *
352format_vl_msg_api_trace_status (u8 * s, va_list * args)
353{
354 api_main_t *am = va_arg (*args, api_main_t *);
355 vl_api_trace_which_t which = va_arg (*args, vl_api_trace_which_t);
356 vl_api_trace_t *tp;
357 char *trace_name;
358
359 switch (which)
360 {
361 case VL_API_TRACE_TX:
362 tp = am->tx_trace;
363 trace_name = "TX trace";
364 break;
365
366 case VL_API_TRACE_RX:
367 tp = am->rx_trace;
368 trace_name = "RX trace";
369 break;
370
371 default:
372 abort ();
373 }
374
375 if (tp == 0)
376 {
377 s = format (s, "%s: not yet configured.\n", trace_name);
378 return s;
379 }
380
381 s = format (s, "%s: used %d of %d items, %s enabled, %s wrapped\n",
382 trace_name, vec_len (tp->traces), tp->nitems,
383 tp->enabled ? "is" : "is not", tp->wrapped ? "has" : "has not");
384 return s;
385}
386
Florin Corase86a8ed2018-01-05 03:20:25 -0800387static void
388vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
389 u32 first_index, u32 last_index,
390 vl_api_replay_t which)
391{
392 vl_api_trace_file_header_t *hp;
393 int i, fd;
Filip Tehlar36217e32021-07-23 08:51:10 +0000394 u16 *msgid_vec = 0;
Florin Corase86a8ed2018-01-05 03:20:25 -0800395 struct stat statb;
396 size_t file_size;
397 u8 *msg;
Dave Barach39d69112019-11-27 11:42:13 -0500398 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800399 u8 *tmpbuf = 0;
Ole Troanedfe2c02019-07-30 15:38:13 +0200400 u32 nitems, nitems_msgtbl;
Florin Corase86a8ed2018-01-05 03:20:25 -0800401
402 fd = open ((char *) filename, O_RDONLY);
403
404 if (fd < 0)
405 {
406 vlib_cli_output (vm, "Couldn't open %s\n", filename);
407 return;
408 }
409
410 if (fstat (fd, &statb) < 0)
411 {
412 vlib_cli_output (vm, "Couldn't stat %s\n", filename);
413 close (fd);
414 return;
415 }
416
417 if (!(statb.st_mode & S_IFREG) || (statb.st_size < sizeof (*hp)))
418 {
419 vlib_cli_output (vm, "File not plausible: %s\n", filename);
420 close (fd);
421 return;
422 }
423
424 file_size = statb.st_size;
Benoît Ganneaba49832020-01-21 18:35:49 +0100425 file_size = (file_size + 4095) & ~(4095);
Florin Corase86a8ed2018-01-05 03:20:25 -0800426
427 hp = mmap (0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
428
429 if (hp == (vl_api_trace_file_header_t *) MAP_FAILED)
430 {
431 vlib_cli_output (vm, "mmap failed: %s\n", filename);
432 close (fd);
433 return;
434 }
435 close (fd);
436
Damjan Marion79934e82022-04-05 12:40:31 +0200437 clib_mem_unpoison (hp, file_size);
Benoît Ganneb2f09142019-12-16 15:37:28 +0100438
Ole Troanedfe2c02019-07-30 15:38:13 +0200439 nitems = ntohl (hp->nitems);
Florin Corase86a8ed2018-01-05 03:20:25 -0800440
441 if (last_index == (u32) ~ 0)
442 {
443 last_index = nitems - 1;
444 }
445
446 if (first_index >= nitems || last_index >= nitems)
447 {
448 vlib_cli_output (vm, "Range (%d, %d) outside file range (0, %d)\n",
449 first_index, last_index, nitems - 1);
450 munmap (hp, file_size);
451 return;
452 }
453 if (hp->wrapped)
454 vlib_cli_output (vm,
455 "Note: wrapped/incomplete trace, results may vary\n");
456
Filip Tehlar36217e32021-07-23 08:51:10 +0000457 size_t file_size_left = file_size;
458
459#define assert_size(size_left, s) \
460 do \
461 { \
462 if ((s) >= size_left) \
463 { \
464 vlib_cli_output (vm, "corrupted file"); \
465 munmap (hp, file_size); \
466 vec_free (msgid_vec); \
467 return; \
468 } \
469 size_left -= s; \
470 } \
471 while (0);
472
473 assert_size (file_size_left, sizeof (hp[0]));
Florin Corase86a8ed2018-01-05 03:20:25 -0800474 msg = (u8 *) (hp + 1);
475
Ole Troanedfe2c02019-07-30 15:38:13 +0200476 serialize_main_t _sm, *sm = &_sm;
477 u32 msgtbl_size = ntohl (hp->msgtbl_size);
478 u8 *name_and_crc;
479
Filip Tehlar36217e32021-07-23 08:51:10 +0000480 assert_size (file_size_left, msgtbl_size);
481
Ole Troanedfe2c02019-07-30 15:38:13 +0200482 unserialize_open_data (sm, msg, msgtbl_size);
483 unserialize_integer (sm, &nitems_msgtbl, sizeof (u32));
484
485 for (i = 0; i < nitems_msgtbl; i++)
486 {
487 u16 msg_index = unserialize_likely_small_unsigned_integer (sm);
488 unserialize_cstring (sm, (char **) &name_and_crc);
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100489 u32 msg_index2 = vl_msg_api_get_msg_index (name_and_crc);
490 ASSERT (~0 == msg_index2 || msg_index2 <= 65535);
491 if (~0 == msg_index2)
492 vlib_cli_output (vm, "warning: can't find msg index for id %d\n",
493 msg_index);
Ole Troanedfe2c02019-07-30 15:38:13 +0200494 vec_validate (msgid_vec, msg_index);
495 msgid_vec[msg_index] = msg_index2;
496 }
497
498 msg += msgtbl_size;
499
Florin Corase86a8ed2018-01-05 03:20:25 -0800500 for (i = 0; i < first_index; i++)
501 {
Florin Corase86a8ed2018-01-05 03:20:25 -0800502 int size;
503 u16 msg_id;
504
Filip Tehlar36217e32021-07-23 08:51:10 +0000505 assert_size (file_size_left, sizeof (u32));
Florin Corase86a8ed2018-01-05 03:20:25 -0800506 size = clib_host_to_net_u32 (*(u32 *) msg);
507 msg += sizeof (u32);
508
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100509 assert_size (file_size_left, clib_max (size, sizeof (u16)));
Ole Troanedfe2c02019-07-30 15:38:13 +0200510 msg_id = ntohs (*((u16 *) msg));
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100511 if (msg_id >= vec_len (msgid_vec) ||
Damjan Marioncada9eb2022-05-18 22:16:11 +0200512 msgid_vec[msg_id] >= vec_len (am->msg_data))
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100513 vlib_cli_output (vm, "warning: unknown msg id %d for msg number %d\n",
514 msg_id, i);
515
Florin Corase86a8ed2018-01-05 03:20:25 -0800516 msg += size;
517 }
518
519 if (which == REPLAY)
520 am->replay_in_progress = 1;
521
522 for (; i <= last_index; i++)
523 {
Damjan Marioncada9eb2022-05-18 22:16:11 +0200524 vl_api_msg_data_t *m;
Florin Corase86a8ed2018-01-05 03:20:25 -0800525 u16 msg_id;
526 int size;
527
528 if (which == DUMP)
529 vlib_cli_output (vm, "---------- trace %d -----------\n", i);
530
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100531 assert_size (file_size_left, sizeof (u32));
Florin Corase86a8ed2018-01-05 03:20:25 -0800532 size = clib_host_to_net_u32 (*(u32 *) msg);
533 msg += sizeof (u32);
534
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100535 assert_size (file_size_left, clib_max (size, sizeof (u16)));
Ole Troanedfe2c02019-07-30 15:38:13 +0200536 msg_id = ntohs (*((u16 *) msg));
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100537
538 if (msg_id >= vec_len (msgid_vec) ||
Damjan Marioncada9eb2022-05-18 22:16:11 +0200539 msgid_vec[msg_id] >= vec_len (am->msg_data))
Ole Troanedfe2c02019-07-30 15:38:13 +0200540 {
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100541 vlib_cli_output (
542 vm, "warning: unknown msg id %d for msg number %d, skipping\n",
543 msg_id, i);
544 msg += size;
545 continue;
Ole Troanedfe2c02019-07-30 15:38:13 +0200546 }
Florin Corase86a8ed2018-01-05 03:20:25 -0800547
Benoît Ganne7e0442a2022-01-20 13:44:12 +0100548 msg_id = msgid_vec[msg_id];
Damjan Marioncada9eb2022-05-18 22:16:11 +0200549 m = vl_api_get_msg_data (am, msg_id);
Florin Corase86a8ed2018-01-05 03:20:25 -0800550
551 /* Copy the buffer (from the read-only mmap'ed file) */
552 vec_validate (tmpbuf, size - 1 + sizeof (uword));
553 clib_memcpy (tmpbuf + sizeof (uword), msg, size);
Dave Barachb7b92992018-10-17 10:38:51 -0400554 clib_memset (tmpbuf, 0xf, sizeof (uword));
Florin Corase86a8ed2018-01-05 03:20:25 -0800555
556 /*
Ole Troanedfe2c02019-07-30 15:38:13 +0200557 * Endian swap if needed. All msg data is supposed to be in
558 * network byte order.
Florin Corase86a8ed2018-01-05 03:20:25 -0800559 */
Filip Tehlar36217e32021-07-23 08:51:10 +0000560 if (((which == DUMP || which == DUMP_JSON) &&
561 clib_arch_is_little_endian))
Florin Corase86a8ed2018-01-05 03:20:25 -0800562 {
Damjan Marioncada9eb2022-05-18 22:16:11 +0200563 if (m && m->endian_handler == 0)
Florin Corase86a8ed2018-01-05 03:20:25 -0800564 {
565 vlib_cli_output (vm, "Ugh: msg id %d no endian swap\n", msg_id);
566 munmap (hp, file_size);
567 vec_free (tmpbuf);
568 am->replay_in_progress = 0;
569 return;
570 }
Andrew Yourtchenkoa3018af2022-08-17 13:24:11 +0000571 if (m)
572 {
573 m->endian_handler (tmpbuf + sizeof (uword));
574 }
Florin Corase86a8ed2018-01-05 03:20:25 -0800575 }
576
577 /* msg_id always in network byte order */
578 if (clib_arch_is_little_endian)
579 {
Ole Troanedfe2c02019-07-30 15:38:13 +0200580 u16 *msg_idp = (u16 *) (tmpbuf + sizeof (uword));
Florin Corase86a8ed2018-01-05 03:20:25 -0800581 *msg_idp = msg_id;
582 }
583
584 switch (which)
585 {
Filip Tehlar36217e32021-07-23 08:51:10 +0000586 case DUMP_JSON:
Damjan Marionfe45f8f2022-05-20 16:01:22 +0200587 vlib_cli_output (vm, "%U", format_vl_api_msg_json, am, msg_id,
588 tmpbuf + sizeof (uword));
Filip Tehlar36217e32021-07-23 08:51:10 +0000589 break;
590
Florin Corase86a8ed2018-01-05 03:20:25 -0800591 case DUMP:
Damjan Marionfe45f8f2022-05-20 16:01:22 +0200592 vlib_cli_output (vm, "%U", format_vl_api_msg_text, am, msg_id,
593 tmpbuf + sizeof (uword));
Florin Corase86a8ed2018-01-05 03:20:25 -0800594 break;
595
596 case INITIALIZERS:
Damjan Marionfe45f8f2022-05-20 16:01:22 +0200597 if (m)
Florin Corase86a8ed2018-01-05 03:20:25 -0800598 {
599 u8 *s;
600 int j;
Florin Corase86a8ed2018-01-05 03:20:25 -0800601
Damjan Marionfe45f8f2022-05-20 16:01:22 +0200602 vlib_cli_output (vm, "/*%U*/", format_vl_api_msg_text, am,
603 msg_id, tmpbuf + sizeof (uword));
Florin Corase86a8ed2018-01-05 03:20:25 -0800604
Florin Corase86a8ed2018-01-05 03:20:25 -0800605 vlib_cli_output (vm, "*/\n");
606
Damjan Marioncada9eb2022-05-18 22:16:11 +0200607 s = format (0, "static u8 * vl_api_%s_%d[%d] = {", m->name, i,
608 m->trace_size);
Florin Corase86a8ed2018-01-05 03:20:25 -0800609
Damjan Marioncada9eb2022-05-18 22:16:11 +0200610 for (j = 0; j < m->trace_size; j++)
Florin Corase86a8ed2018-01-05 03:20:25 -0800611 {
612 if ((j & 7) == 0)
613 s = format (s, "\n ");
614 s = format (s, "0x%02x,", tmpbuf[sizeof (uword) + j]);
615 }
616 s = format (s, "\n};\n%c", 0);
617 vlib_cli_output (vm, (char *) s);
618 vec_free (s);
619 }
620 break;
621
622 case REPLAY:
Damjan Marionfe45f8f2022-05-20 16:01:22 +0200623 if (m && m->handler && m->replay_allowed)
Florin Corase86a8ed2018-01-05 03:20:25 -0800624 {
Damjan Marioncada9eb2022-05-18 22:16:11 +0200625 if (!m->is_mp_safe)
Florin Corase86a8ed2018-01-05 03:20:25 -0800626 vl_msg_api_barrier_sync ();
Damjan Marioncada9eb2022-05-18 22:16:11 +0200627 m->handler (tmpbuf + sizeof (uword));
628 if (!m->is_mp_safe)
Florin Corase86a8ed2018-01-05 03:20:25 -0800629 vl_msg_api_barrier_release ();
630 }
631 else
632 {
Andrew Yourtchenko618afb52022-08-26 13:46:44 +0000633 if (m && m->replay_allowed)
Florin Corase86a8ed2018-01-05 03:20:25 -0800634 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
635 msg_id);
636 break;
637 }
638 break;
639 }
640
Damjan Marion8bea5892022-04-04 22:40:45 +0200641 vec_set_len (tmpbuf, 0);
Florin Corase86a8ed2018-01-05 03:20:25 -0800642 msg += size;
643 }
644
Florin Corase86a8ed2018-01-05 03:20:25 -0800645 munmap (hp, file_size);
646 vec_free (tmpbuf);
Filip Tehlar36217e32021-07-23 08:51:10 +0000647 vec_free (msgid_vec);
Florin Corase86a8ed2018-01-05 03:20:25 -0800648 am->replay_in_progress = 0;
649}
650
Filip Tehlar36217e32021-07-23 08:51:10 +0000651static int
652file_exists (u8 *fname)
653{
654 FILE *fp = 0;
655 fp = fopen ((char *) fname, "r");
656 if (fp)
657 {
658 fclose (fp);
659 return 1;
660 }
661 return 0;
662}
663
664typedef struct
665{
666 vlib_main_t *vm;
667 u8 is_json;
668} vl_msg_print_args;
669
670static int
671vl_msg_print_trace (u8 *msg, void *ctx)
672{
673 vl_msg_print_args *a = ctx;
674 api_main_t *am = vlibapi_get_main ();
675 u16 msg_id = ntohs (*((u16 *) msg));
Damjan Marioncada9eb2022-05-18 22:16:11 +0200676 vl_api_msg_data_t *m = vl_api_get_msg_data (am, msg_id);
Filip Tehlar36217e32021-07-23 08:51:10 +0000677 u8 is_json = a->is_json;
678 u8 *tmpbuf = 0;
679
Damjan Marioncada9eb2022-05-18 22:16:11 +0200680 if (!m)
681 {
682 vlib_cli_output (a->vm, "Unknown msg id %d\n", msg_id);
683 return 0;
684 }
685
Matthew Smith5b32d3a2023-04-19 20:02:25 +0000686 if (clib_arch_is_little_endian && (m->endian_handler != NULL))
Filip Tehlar36217e32021-07-23 08:51:10 +0000687 {
688 u32 msg_length = vec_len (msg);
689 vec_validate (tmpbuf, msg_length - 1);
690 clib_memcpy_fast (tmpbuf, msg, msg_length);
691 msg = tmpbuf;
692
Damjan Marioncada9eb2022-05-18 22:16:11 +0200693 m->endian_handler (tmpbuf);
Filip Tehlar36217e32021-07-23 08:51:10 +0000694 }
695
Damjan Marionfe45f8f2022-05-20 16:01:22 +0200696 vlib_cli_output (a->vm, "%U\n",
697 is_json ? format_vl_api_msg_json : format_vl_api_msg_text,
698 am, msg_id, msg);
Filip Tehlar36217e32021-07-23 08:51:10 +0000699
700 vec_free (tmpbuf);
701 return 0;
702}
703
704static int
705vl_msg_api_dump_trace (vlib_main_t *vm, vl_api_trace_which_t which, u8 is_json)
706{
707 api_main_t *am = vlibapi_get_main ();
708 vl_api_trace_t *tp;
709
710 switch (which)
711 {
712 case VL_API_TRACE_TX:
713 tp = am->tx_trace;
714 break;
715 case VL_API_TRACE_RX:
716 tp = am->rx_trace;
717 break;
718 default:
719 return -1;
720 }
721
722 if (tp == 0 || tp->nitems == 0 || vec_len (tp->traces) == 0)
723 return -1;
724
725 vl_msg_print_args args;
726 clib_memset (&args, 0, sizeof (args));
727 args.is_json = is_json;
728 args.vm = vm;
729 vl_msg_traverse_trace (tp, vl_msg_print_trace, &args);
730
731 return 0;
732}
733
734static char *
735vl_msg_read_file (FILE *f)
736{
737 const size_t bufsize = 1024;
738 char *buf[bufsize], *v = 0;
739 size_t n;
740
741 while ((n = fread (buf, 1, bufsize, f)))
742 vec_add (v, buf, n);
743
Benoît Ganne2b9a4bf2021-10-11 13:49:59 +0200744 /* most callers expect a NULL-terminated C-string */
745 if (v)
746 vec_add1 (v, 0);
747
Filip Tehlar36217e32021-07-23 08:51:10 +0000748 return v;
749}
750
751static u16
752vl_msg_find_id_by_name_and_crc (vlib_main_t *vm, api_main_t *am, char *name)
753{
754 uword *p;
755 p = hash_get_mem (am->msg_index_by_name_and_crc, name);
756 if (!p)
757 return (u16) ~0;
758
759 return p[0];
760}
761
762static u16
763vl_msg_find_id_by_name (vlib_main_t *vm, api_main_t *am, char *name)
764{
765 uword *p;
766
767 if (!am->msg_id_by_name)
768 {
769 vlib_cli_output (vm, "message id table not yet initialized!\n");
770 return (u16) ~0;
771 }
772
773 p = hash_get_mem (am->msg_id_by_name, name);
774 if (!p)
775 return (u16) ~0;
776
777 return p[0];
778}
779
780static int
781vl_msg_exec_json_command (vlib_main_t *vm, cJSON *o)
782{
783 api_main_t *am = vlibapi_get_main ();
784 u16 msg_id;
Filip Tehlar36217e32021-07-23 08:51:10 +0000785 int len = 0, rv = -1;
Damjan Marioncada9eb2022-05-18 22:16:11 +0200786 vl_api_msg_data_t *m;
Filip Tehlar36217e32021-07-23 08:51:10 +0000787 u8 *msg = 0;
788
789 cJSON *msg_id_obj = cJSON_GetObjectItem (o, "_msgname");
790 if (!msg_id_obj)
791 {
792 vlib_cli_output (vm, "Missing '_msgname' element!\n");
793 return rv;
794 }
795 char *name = cJSON_GetStringValue (msg_id_obj);
796
797 cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc");
Florin Corasf6e6da92021-10-12 08:41:09 -0700798 if (!crc_obj)
Filip Tehlar36217e32021-07-23 08:51:10 +0000799 {
800 vlib_cli_output (vm, "Missing '_crc' element!\n");
801 return rv;
802 }
803 char *crc = cJSON_GetStringValue (crc_obj);
804 u8 proc_warning = 0;
805
806 u8 *name_crc = format (0, "%s_%s%c", name, crc, 0);
807 msg_id = vl_msg_find_id_by_name_and_crc (vm, am, (char *) name_crc);
Damjan Marioncada9eb2022-05-18 22:16:11 +0200808 m = vl_api_get_msg_data (am, msg_id);
Filip Tehlar36217e32021-07-23 08:51:10 +0000809 if (msg_id == (u16) ~0)
810 {
811 msg_id = vl_msg_find_id_by_name (vm, am, name);
812 if (msg_id == (u16) ~0)
813 {
814 vlib_cli_output (vm, "unknown msg id %d!\n", msg_id);
815 vec_free (name_crc);
816 return rv;
817 }
818 proc_warning = 1;
819 }
820 vec_free (name_crc);
821
Damjan Marioncada9eb2022-05-18 22:16:11 +0200822 if (m->replay_allowed)
Filip Tehlar36217e32021-07-23 08:51:10 +0000823 {
Filip Tehlar36217e32021-07-23 08:51:10 +0000824 if (proc_warning)
825 vlib_cli_output (vm, "warning: msg %d has different signature\n");
826
Damjan Marioncada9eb2022-05-18 22:16:11 +0200827 if (!m->fromjson_handler)
Filip Tehlar36217e32021-07-23 08:51:10 +0000828 {
829 vlib_cli_output (vm, "missing fromjson convert function! id %d\n",
830 msg_id);
831 return rv;
832 }
833
Damjan Marioncada9eb2022-05-18 22:16:11 +0200834 msg = (u8 *) m->fromjson_handler (o, &len);
Filip Tehlar36217e32021-07-23 08:51:10 +0000835 if (!msg)
836 {
837 vlib_cli_output (vm, "failed to convert JSON (msg id %d)!\n",
838 msg_id);
839 return rv;
840 }
841
842 if (clib_arch_is_little_endian)
Damjan Marioncada9eb2022-05-18 22:16:11 +0200843 m->endian_handler (msg);
Filip Tehlar36217e32021-07-23 08:51:10 +0000844
Damjan Marioncada9eb2022-05-18 22:16:11 +0200845 if (!m->handler)
Filip Tehlar36217e32021-07-23 08:51:10 +0000846 {
847 vlib_cli_output (vm, "no handler for msg id %d!\n", msg_id);
848 goto end;
849 }
850
Damjan Marionfe45f8f2022-05-20 16:01:22 +0200851 if (m->handler)
852 {
853 if (!m->is_mp_safe)
854 vl_msg_api_barrier_sync ();
855 m->handler (msg);
856 if (!m->is_mp_safe)
857 vl_msg_api_barrier_release ();
858 }
Filip Tehlar36217e32021-07-23 08:51:10 +0000859 }
860
861 rv = 0;
862end:
863 if (msg)
864 cJSON_free (msg);
865 return rv;
866}
867
868static void
869vl_msg_replay_json (vlib_main_t *vm, u8 *filename)
870{
871 api_main_t *am = vlibapi_get_main ();
872 cJSON *o = 0;
873 int rv = 0;
874 FILE *f = fopen ((char *) filename, "r");
875
876 if (!f)
877 {
878 vlib_cli_output (vm, "failed to open %s!\n", filename);
879 return;
880 }
881
882 char *buf = vl_msg_read_file (f);
883 fclose (f);
884
885 o = cJSON_Parse (buf);
886 vec_free (buf);
887 if (!o)
888 {
889 vlib_cli_output (vm, "%s: Failed parsing JSON input: %s\n", filename,
890 cJSON_GetErrorPtr ());
891 return;
892 }
893
894 if (cJSON_IsArray (o))
895 {
896 am->replay_in_progress = 1;
897 size_t size = cJSON_GetArraySize (o);
898 for (int i = 0; i < size; i++)
899 {
900 rv = vl_msg_exec_json_command (vm, cJSON_GetArrayItem (o, i));
901 if (rv < 0)
902 {
903 am->replay_in_progress = 0;
904 break;
905 }
906 }
907 }
908 else
909 {
910 rv = vl_msg_exec_json_command (vm, o);
911 }
912
913 if (rv < 0)
914 vlib_cli_output (vm, "error during replaying API trace");
915
916 cJSON_Delete (o);
917}
918
919static void
920vl_msg_dump_file_json (vlib_main_t *vm, u8 *filename)
921{
922 FILE *f = fopen ((char *) filename, "r");
923 char *buf;
924
925 if (!f)
926 {
927 vlib_cli_output (vm, "failed to open %s!\n", filename);
928 return;
929 }
930
931 buf = vl_msg_read_file (f);
932 fclose (f);
933
934 if (!buf)
935 {
936 vlib_cli_output (vm, "no content in %s!\n", filename);
937 return;
938 }
939
940 vlib_cli_output (vm, buf);
941 vec_free (buf);
942}
943
Dave Barachf66f8832020-01-23 08:44:40 -0500944/** api_trace_command_fn - control the binary API trace / replay feature
945
946 Note: this command MUST be marked thread-safe. Replay with
947 multiple worker threads depends in many cases on worker thread
948 graph replica maintenance. If we (implicitly) assert a worker
949 thread barrier at the debug CLI level, all graph replica changes
950 are deferred until the replay operation completes. If an interface
951 is deleted, the wheels fall off.
952 */
953
Florin Corase86a8ed2018-01-05 03:20:25 -0800954static clib_error_t *
955api_trace_command_fn (vlib_main_t * vm,
956 unformat_input_t * input, vlib_cli_command_t * cmd)
957{
Neale Rannsecff1cb2020-04-22 12:14:52 -0400958 unformat_input_t _line_input, *line_input = &_line_input;
Florin Corase86a8ed2018-01-05 03:20:25 -0800959 u32 nitems = 256 << 10;
Dave Barach39d69112019-11-27 11:42:13 -0500960 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800961 vl_api_trace_which_t which = VL_API_TRACE_RX;
Dave Barachf35a0722019-06-12 16:50:38 -0400962 u8 *filename = 0;
963 u8 *chroot_filename = 0;
Florin Corase86a8ed2018-01-05 03:20:25 -0800964 u32 first = 0;
965 u32 last = (u32) ~ 0;
966 FILE *fp;
967 int rv;
968
Neale Rannsecff1cb2020-04-22 12:14:52 -0400969 /* Get a line of input. */
970 if (!unformat_user (input, unformat_line_input, line_input))
971 return 0;
972
973 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
Florin Corase86a8ed2018-01-05 03:20:25 -0800974 {
Neale Rannsecff1cb2020-04-22 12:14:52 -0400975 if (unformat (line_input, "on") || unformat (line_input, "enable"))
Florin Corase86a8ed2018-01-05 03:20:25 -0800976 {
Neale Rannsecff1cb2020-04-22 12:14:52 -0400977 if (unformat (line_input, "nitems %d", &nitems))
Florin Corase86a8ed2018-01-05 03:20:25 -0800978 ;
Dave Barachf66f8832020-01-23 08:44:40 -0500979 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800980 vl_msg_api_trace_configure (am, which, nitems);
981 vl_msg_api_trace_onoff (am, which, 1 /* on */ );
Dave Barachf66f8832020-01-23 08:44:40 -0500982 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800983 }
Neale Rannsecff1cb2020-04-22 12:14:52 -0400984 else if (unformat (line_input, "off"))
Florin Corase86a8ed2018-01-05 03:20:25 -0800985 {
Dave Barachf66f8832020-01-23 08:44:40 -0500986 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800987 vl_msg_api_trace_onoff (am, which, 0);
Dave Barachf66f8832020-01-23 08:44:40 -0500988 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800989 }
Filip Tehlar36217e32021-07-23 08:51:10 +0000990 else if (unformat (line_input, "save-json %s", &filename))
991 {
992 if (strstr ((char *) filename, "..") ||
993 index ((char *) filename, '/'))
994 {
995 vlib_cli_output (vm, "illegal characters in filename '%s'",
996 filename);
997 goto out;
998 }
999
1000 chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1001
1002 vec_free (filename);
1003
1004 if (file_exists (chroot_filename))
1005 {
1006 vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1007 goto out;
1008 }
1009
1010 fp = fopen ((char *) chroot_filename, "w");
1011 if (fp == NULL)
1012 {
1013 vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
1014 goto out;
1015 }
1016 vlib_worker_thread_barrier_sync (vm);
1017 rv = vl_msg_api_trace_save (am, which, fp, 1);
1018 if (rv == -1)
1019 vlib_cli_output (vm, "API Trace data not present\n");
1020 else if (rv < 0)
1021 vlib_cli_output (vm, "failed to save api trace\n");
1022 else
1023 vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
1024 vlib_worker_thread_barrier_release (vm);
1025 fclose (fp);
1026 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001027 else if (unformat (line_input, "save %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001028 {
Florin Corase86a8ed2018-01-05 03:20:25 -08001029 if (strstr ((char *) filename, "..")
1030 || index ((char *) filename, '/'))
1031 {
1032 vlib_cli_output (vm, "illegal characters in filename '%s'",
1033 filename);
Dave Barachf35a0722019-06-12 16:50:38 -04001034 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -08001035 }
1036
1037 chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1038
1039 vec_free (filename);
1040
Filip Tehlar36217e32021-07-23 08:51:10 +00001041 if (file_exists (chroot_filename))
1042 {
1043 vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1044 goto out;
1045 }
1046
Florin Corase86a8ed2018-01-05 03:20:25 -08001047 fp = fopen ((char *) chroot_filename, "w");
1048 if (fp == NULL)
1049 {
1050 vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
Dave Barachf35a0722019-06-12 16:50:38 -04001051 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -08001052 }
Dave Barachf66f8832020-01-23 08:44:40 -05001053 vlib_worker_thread_barrier_sync (vm);
Filip Tehlar36217e32021-07-23 08:51:10 +00001054 rv = vl_msg_api_trace_save (am, which, fp, 0);
Dave Barachf66f8832020-01-23 08:44:40 -05001055 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001056 fclose (fp);
1057 if (rv == -1)
1058 vlib_cli_output (vm, "API Trace data not present\n");
1059 else if (rv == -2)
1060 vlib_cli_output (vm, "File for writing is closed\n");
1061 else if (rv == -10)
1062 vlib_cli_output (vm, "Error while writing header to file\n");
1063 else if (rv == -11)
1064 vlib_cli_output (vm, "Error while writing trace to file\n");
1065 else if (rv == -12)
1066 vlib_cli_output (vm,
1067 "Error while writing end of buffer trace to file\n");
1068 else if (rv == -13)
1069 vlib_cli_output (vm,
1070 "Error while writing start of buffer trace to file\n");
1071 else if (rv < 0)
Andrey "Zed" Zaikin701625b2018-04-18 17:07:07 +03001072 vlib_cli_output (vm, "Unknown error while saving: %d", rv);
Florin Corase86a8ed2018-01-05 03:20:25 -08001073 else
1074 vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
Dave Barachf35a0722019-06-12 16:50:38 -04001075 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -08001076 }
Filip Tehlar36217e32021-07-23 08:51:10 +00001077 else if (unformat (line_input, "tojson %s", &filename))
1078 {
1079 vl_msg_api_process_file (vm, filename, first, last, DUMP_JSON);
1080 }
1081 else if (unformat (line_input, "dump-file-json %s", &filename))
1082 {
1083 vl_msg_dump_file_json (vm, filename);
1084 }
1085 else if (unformat (line_input, "dump-file %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001086 {
1087 vl_msg_api_process_file (vm, filename, first, last, DUMP);
1088 }
Filip Tehlar36217e32021-07-23 08:51:10 +00001089 else if (unformat (line_input, "dump-json"))
1090 {
1091 vl_msg_api_dump_trace (vm, which, 1);
1092 }
1093 else if (unformat (line_input, "dump"))
1094 {
1095 vl_msg_api_dump_trace (vm, which, 0);
1096 }
1097 else if (unformat (line_input, "replay-json %s", &filename))
1098 {
1099 vl_msg_replay_json (vm, filename);
1100 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001101 else if (unformat (line_input, "replay %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001102 {
1103 vl_msg_api_process_file (vm, filename, first, last, REPLAY);
1104 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001105 else if (unformat (line_input, "initializers %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001106 {
1107 vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
1108 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001109 else if (unformat (line_input, "tx"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001110 {
1111 which = VL_API_TRACE_TX;
1112 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001113 else if (unformat (line_input, "first %d", &first))
Florin Corase86a8ed2018-01-05 03:20:25 -08001114 {
1115 ;
1116 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001117 else if (unformat (line_input, "last %d", &last))
Florin Corase86a8ed2018-01-05 03:20:25 -08001118 {
1119 ;
1120 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001121 else if (unformat (line_input, "status"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001122 {
1123 vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
1124 am, which);
1125 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001126 else if (unformat (line_input, "free"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001127 {
Dave Barachf66f8832020-01-23 08:44:40 -05001128 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001129 vl_msg_api_trace_onoff (am, which, 0);
1130 vl_msg_api_trace_free (am, which);
Dave Barachf66f8832020-01-23 08:44:40 -05001131 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001132 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001133 else if (unformat (line_input, "post-mortem-on"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001134 vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
Neale Rannsecff1cb2020-04-22 12:14:52 -04001135 else if (unformat (line_input, "post-mortem-off"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001136 vl_msg_api_post_mortem_dump_enable_disable (0 /* enable */ );
1137 else
1138 return clib_error_return (0, "unknown input `%U'",
1139 format_unformat_error, input);
1140 }
Dave Barachf35a0722019-06-12 16:50:38 -04001141out:
1142 vec_free (filename);
1143 vec_free (chroot_filename);
Neale Rannsecff1cb2020-04-22 12:14:52 -04001144 unformat_free (line_input);
Florin Corase86a8ed2018-01-05 03:20:25 -08001145 return 0;
1146}
1147
1148/*?
1149 * Display, replay, or save a binary API trace
1150?*/
1151
1152/* *INDENT-OFF* */
Filip Tehlarc863a912021-06-15 10:29:54 +00001153VLIB_CLI_COMMAND (api_trace_command, static) = {
Florin Corase86a8ed2018-01-05 03:20:25 -08001154 .path = "api trace",
Filip Tehlar36217e32021-07-23 08:51:10 +00001155 .short_help = "api trace [tx][on|off][first <n>][last <n>][status][free]"
1156 "[post-mortem-on][dump|dump-file|dump-json|save|tojson|save-"
1157 "json|replay <file>|replay-json <file>][nitems <n>]"
1158 "[initializers <file>]",
Florin Corase86a8ed2018-01-05 03:20:25 -08001159 .function = api_trace_command_fn,
Dave Barachf66f8832020-01-23 08:44:40 -05001160 .is_mp_safe = 1,
Florin Corase86a8ed2018-01-05 03:20:25 -08001161};
1162/* *INDENT-ON* */
1163
1164static clib_error_t *
Florin Corase86a8ed2018-01-05 03:20:25 -08001165api_trace_config_fn (vlib_main_t * vm, unformat_input_t * input)
1166{
1167 u32 nitems = 256 << 10;
1168 vl_api_trace_which_t which = VL_API_TRACE_RX;
Dave Barach39d69112019-11-27 11:42:13 -05001169 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001170
1171 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1172 {
1173 if (unformat (input, "on") || unformat (input, "enable"))
1174 {
1175 if (unformat (input, "nitems %d", &nitems))
1176 ;
1177 vl_msg_api_trace_configure (am, which, nitems);
1178 vl_msg_api_trace_onoff (am, which, 1 /* on */ );
1179 vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1180 }
1181 else if (unformat (input, "save-api-table %s",
1182 &am->save_msg_table_filename))
1183 ;
1184 else
1185 return clib_error_return (0, "unknown input `%U'",
1186 format_unformat_error, input);
1187 }
1188 return 0;
1189}
1190
1191/*?
1192 * This module has three configuration parameters:
1193 * "on" or "enable" - enables binary api tracing
1194 * "nitems <nnn>" - sets the size of the circular buffer to <nnn>
1195 * "save-api-table <filename>" - dumps the API message table to /tmp/<filename>
1196?*/
1197VLIB_CONFIG_FUNCTION (api_trace_config_fn, "api-trace");
1198
1199static clib_error_t *
1200api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
1201{
Dave Barach39d69112019-11-27 11:42:13 -05001202 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001203 u32 nitems;
1204
1205 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1206 {
1207 if (unformat (input, "length %d", &nitems) ||
1208 (unformat (input, "len %d", &nitems)))
1209 {
1210 if (nitems >= 1024)
1211 am->vlib_input_queue_length = nitems;
1212 else
1213 clib_warning ("vlib input queue length %d too small, ignored",
1214 nitems);
1215 }
1216 else
1217 return clib_error_return (0, "unknown input `%U'",
1218 format_unformat_error, input);
1219 }
1220 return 0;
1221}
1222
1223VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
1224
1225static u8 *
1226extract_name (u8 * s)
1227{
1228 u8 *rv;
1229
1230 rv = vec_dup (s);
1231
1232 while (vec_len (rv) && rv[vec_len (rv)] != '_')
Andrew Yourtchenko105cb1e2022-04-15 14:23:41 +00001233 vec_dec_len (rv, 1);
Florin Corase86a8ed2018-01-05 03:20:25 -08001234
1235 rv[vec_len (rv)] = 0;
1236
1237 return rv;
1238}
1239
1240static u8 *
1241extract_crc (u8 * s)
1242{
1243 int i;
1244 u8 *rv;
1245
1246 rv = vec_dup (s);
1247
1248 for (i = vec_len (rv) - 1; i >= 0; i--)
1249 {
1250 if (rv[i] == '_')
1251 {
1252 vec_delete (rv, i + 1, 0);
1253 break;
1254 }
1255 }
1256 return rv;
1257}
1258
1259typedef struct
1260{
1261 u8 *name_and_crc;
1262 u8 *name;
1263 u8 *crc;
1264 u32 msg_index;
1265 int which;
1266} msg_table_unserialize_t;
1267
1268static int
1269table_id_cmp (void *a1, void *a2)
1270{
1271 msg_table_unserialize_t *n1 = a1;
1272 msg_table_unserialize_t *n2 = a2;
1273
1274 return (n1->msg_index - n2->msg_index);
1275}
1276
1277static int
1278table_name_and_crc_cmp (void *a1, void *a2)
1279{
1280 msg_table_unserialize_t *n1 = a1;
1281 msg_table_unserialize_t *n2 = a2;
1282
1283 return strcmp ((char *) n1->name_and_crc, (char *) n2->name_and_crc);
1284}
1285
1286static clib_error_t *
1287dump_api_table_file_command_fn (vlib_main_t * vm,
1288 unformat_input_t * input,
1289 vlib_cli_command_t * cmd)
1290{
1291 u8 *filename = 0;
Dave Barach39d69112019-11-27 11:42:13 -05001292 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001293 serialize_main_t _sm, *sm = &_sm;
1294 clib_error_t *error;
1295 u32 nmsgs;
1296 u32 msg_index;
1297 u8 *name_and_crc;
1298 int compare_current = 0;
1299 int numeric_sort = 0;
1300 msg_table_unserialize_t *table = 0, *item;
1301 u32 i;
1302 u32 ndifferences = 0;
1303
1304 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1305 {
1306 if (unformat (input, "file %s", &filename))
1307 ;
1308 else if (unformat (input, "compare-current")
1309 || unformat (input, "compare"))
1310 compare_current = 1;
1311 else if (unformat (input, "numeric"))
1312 numeric_sort = 1;
1313 else
1314 return clib_error_return (0, "unknown input `%U'",
1315 format_unformat_error, input);
1316 }
1317
1318 if (numeric_sort && compare_current)
1319 return clib_error_return
1320 (0, "Comparison and numeric sorting are incompatible");
1321
1322 if (filename == 0)
1323 return clib_error_return (0, "File not specified");
1324
1325 /* Load the serialized message table from the table dump */
1326
1327 error = unserialize_open_clib_file (sm, (char *) filename);
1328
1329 if (error)
1330 return error;
1331
1332 unserialize_integer (sm, &nmsgs, sizeof (u32));
1333
1334 for (i = 0; i < nmsgs; i++)
1335 {
1336 msg_index = unserialize_likely_small_unsigned_integer (sm);
1337 unserialize_cstring (sm, (char **) &name_and_crc);
1338 vec_add2 (table, item, 1);
1339 item->msg_index = msg_index;
1340 item->name_and_crc = name_and_crc;
1341 item->name = extract_name (name_and_crc);
1342 item->crc = extract_crc (name_and_crc);
1343 item->which = 0; /* file */
1344 }
Ole Troanedfe2c02019-07-30 15:38:13 +02001345 unserialize_close (sm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001346
1347 /* Compare with the current image? */
1348 if (compare_current)
1349 {
1350 /* Append the current message table */
1351 u8 *tblv = vl_api_serialize_message_table (am, 0);
1352
1353 serialize_open_vector (sm, tblv);
1354 unserialize_integer (sm, &nmsgs, sizeof (u32));
1355
1356 for (i = 0; i < nmsgs; i++)
1357 {
1358 msg_index = unserialize_likely_small_unsigned_integer (sm);
1359 unserialize_cstring (sm, (char **) &name_and_crc);
1360
1361 vec_add2 (table, item, 1);
1362 item->msg_index = msg_index;
1363 item->name_and_crc = name_and_crc;
1364 item->name = extract_name (name_and_crc);
1365 item->crc = extract_crc (name_and_crc);
1366 item->which = 1; /* current_image */
1367 }
1368 vec_free (tblv);
1369 }
1370
1371 /* Sort the table. */
1372 if (numeric_sort)
1373 vec_sort_with_function (table, table_id_cmp);
1374 else
1375 vec_sort_with_function (table, table_name_and_crc_cmp);
1376
1377 if (compare_current)
1378 {
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001379 u8 *dashes = 0;
Florin Corase86a8ed2018-01-05 03:20:25 -08001380 ndifferences = 0;
1381
1382 /*
1383 * In this case, the recovered table will have two entries per
1384 * API message. So, if entries i and i+1 match, the message definitions
1385 * are identical. Otherwise, the crc is different, or a message is
1386 * present in only one of the tables.
1387 */
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001388 vlib_cli_output (vm, "%-60s | %s", "Message Name", "Result");
1389 vec_validate_init_empty (dashes, 60, '-');
1390 vec_terminate_c_string (dashes);
1391 vlib_cli_output (vm, "%60s-|-%s", dashes, "-----------------");
1392 vec_free (dashes);
Florin Corase86a8ed2018-01-05 03:20:25 -08001393 for (i = 0; i < vec_len (table);)
1394 {
1395 /* Last message lonely? */
1396 if (i == vec_len (table) - 1)
1397 {
1398 ndifferences++;
1399 goto last_unique;
1400 }
1401
1402 /* Identical pair? */
1403 if (!strncmp
1404 ((char *) table[i].name_and_crc,
1405 (char *) table[i + 1].name_and_crc,
1406 vec_len (table[i].name_and_crc)))
1407 {
1408 i += 2;
1409 continue;
1410 }
1411
1412 ndifferences++;
1413
1414 /* Only in one of two tables? */
Andrew Yourtchenkoae605b82019-01-21 16:39:33 +01001415 if (i + 1 == vec_len (table)
1416 || strcmp ((char *) table[i].name, (char *) table[i + 1].name))
Florin Corase86a8ed2018-01-05 03:20:25 -08001417 {
1418 last_unique:
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001419 vlib_cli_output (vm, "%-60s | only in %s",
Florin Corase86a8ed2018-01-05 03:20:25 -08001420 table[i].name, table[i].which ?
1421 "image" : "file");
1422 i++;
1423 continue;
1424 }
1425 /* In both tables, but with different signatures */
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001426 vlib_cli_output (vm, "%-60s | definition changed", table[i].name);
Florin Corase86a8ed2018-01-05 03:20:25 -08001427 i += 2;
1428 }
1429 if (ndifferences == 0)
1430 vlib_cli_output (vm, "No api message signature differences found.");
1431 else
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001432 vlib_cli_output (vm, "\nFound %u api message signature differences",
Florin Corase86a8ed2018-01-05 03:20:25 -08001433 ndifferences);
1434 goto cleanup;
1435 }
1436
1437 /* Dump the table, sorted as shown above */
1438 vlib_cli_output (vm, "%=60s %=8s %=10s", "Message name", "MsgID", "CRC");
1439
1440 for (i = 0; i < vec_len (table); i++)
1441 {
1442 item = table + i;
1443 vlib_cli_output (vm, "%-60s %8u %10s", item->name,
1444 item->msg_index, item->crc);
1445 }
1446
1447cleanup:
1448 for (i = 0; i < vec_len (table); i++)
1449 {
1450 vec_free (table[i].name_and_crc);
1451 vec_free (table[i].name);
1452 vec_free (table[i].crc);
1453 }
1454
1455 vec_free (table);
1456
1457 return 0;
1458}
1459
1460/*?
1461 * Displays a serialized API message decode table, sorted by message name
1462 *
1463 * @cliexpar
1464 * @cliexstart{show api dump file <filename>}
1465 * Message name MsgID CRC
1466 * accept_session 407 8e2a127e
1467 * accept_session_reply 408 67d8c22a
1468 * add_node_next 549 e4202993
1469 * add_node_next_reply 550 e89d6eed
1470 * etc.
1471 * @cliexend
1472?*/
1473
1474/*?
1475 * Compares a serialized API message decode table with the current image
1476 *
1477 * @cliexpar
1478 * @cliexstart{show api dump file <filename> compare}
1479 * ip_add_del_route definition changed
1480 * ip_table_add_del definition changed
1481 * l2_macs_event only in image
1482 * vnet_ip4_fib_counters only in file
1483 * vnet_ip4_nbr_counters only in file
1484 * @cliexend
1485?*/
1486
1487/*?
1488 * Display a serialized API message decode table, compare a saved
1489 * decode table with the current image, to establish API differences.
1490 *
1491?*/
1492/* *INDENT-OFF* */
1493VLIB_CLI_COMMAND (dump_api_table_file, static) =
1494{
1495 .path = "show api dump",
1496 .short_help = "show api dump file <filename> [numeric | compare-current]",
1497 .function = dump_api_table_file_command_fn,
1498};
1499
1500/* *INDENT-ON* */
1501/*
1502 * fd.io coding-style-patch-verification: ON
1503 *
1504 * Local Variables:
1505 * eval: (c-set-style "gnu")
1506 * End:
1507 */