blob: 084080f1e6d561486151611cba29980a437eb055 [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 }
Damjan Marioncada9eb2022-05-18 22:16:11 +0200571 m->endian_handler (tmpbuf + sizeof (uword));
Florin Corase86a8ed2018-01-05 03:20:25 -0800572 }
573
574 /* msg_id always in network byte order */
575 if (clib_arch_is_little_endian)
576 {
Ole Troanedfe2c02019-07-30 15:38:13 +0200577 u16 *msg_idp = (u16 *) (tmpbuf + sizeof (uword));
Florin Corase86a8ed2018-01-05 03:20:25 -0800578 *msg_idp = msg_id;
579 }
580
581 switch (which)
582 {
Filip Tehlar36217e32021-07-23 08:51:10 +0000583 case DUMP_JSON:
Damjan Marioncada9eb2022-05-18 22:16:11 +0200584 if (m && m->print_json_handler)
Filip Tehlar36217e32021-07-23 08:51:10 +0000585 {
Damjan Marioncada9eb2022-05-18 22:16:11 +0200586 m->print_json_handler (tmpbuf + sizeof (uword), vm);
Filip Tehlar36217e32021-07-23 08:51:10 +0000587 }
588 else
589 {
590 vlib_cli_output (vm, "Skipping msg id %d: no JSON print fcn\n",
591 msg_id);
592 break;
593 }
594 break;
595
Florin Corase86a8ed2018-01-05 03:20:25 -0800596 case DUMP:
Damjan Marioncada9eb2022-05-18 22:16:11 +0200597 if (m && m->print_handler)
Florin Corase86a8ed2018-01-05 03:20:25 -0800598 {
Damjan Marioncada9eb2022-05-18 22:16:11 +0200599 m->print_handler (tmpbuf + sizeof (uword), vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800600 }
601 else
602 {
603 vlib_cli_output (vm, "Skipping msg id %d: no print fcn\n",
604 msg_id);
605 break;
606 }
607 break;
608
609 case INITIALIZERS:
Damjan Marioncada9eb2022-05-18 22:16:11 +0200610 if (m && m->print_handler)
Florin Corase86a8ed2018-01-05 03:20:25 -0800611 {
612 u8 *s;
613 int j;
Florin Corase86a8ed2018-01-05 03:20:25 -0800614
615 vlib_cli_output (vm, "/*");
616
Damjan Marioncada9eb2022-05-18 22:16:11 +0200617 m->print_handler (tmpbuf + sizeof (uword), vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800618 vlib_cli_output (vm, "*/\n");
619
Damjan Marioncada9eb2022-05-18 22:16:11 +0200620 s = format (0, "static u8 * vl_api_%s_%d[%d] = {", m->name, i,
621 m->trace_size);
Florin Corase86a8ed2018-01-05 03:20:25 -0800622
Damjan Marioncada9eb2022-05-18 22:16:11 +0200623 for (j = 0; j < m->trace_size; j++)
Florin Corase86a8ed2018-01-05 03:20:25 -0800624 {
625 if ((j & 7) == 0)
626 s = format (s, "\n ");
627 s = format (s, "0x%02x,", tmpbuf[sizeof (uword) + j]);
628 }
629 s = format (s, "\n};\n%c", 0);
630 vlib_cli_output (vm, (char *) s);
631 vec_free (s);
632 }
633 break;
634
635 case REPLAY:
Damjan Marioncada9eb2022-05-18 22:16:11 +0200636 if (m && m->print_handler && m->replay_allowed)
Florin Corase86a8ed2018-01-05 03:20:25 -0800637 {
Damjan Marioncada9eb2022-05-18 22:16:11 +0200638 if (!m->is_mp_safe)
Florin Corase86a8ed2018-01-05 03:20:25 -0800639 vl_msg_api_barrier_sync ();
Damjan Marioncada9eb2022-05-18 22:16:11 +0200640 m->handler (tmpbuf + sizeof (uword));
641 if (!m->is_mp_safe)
Florin Corase86a8ed2018-01-05 03:20:25 -0800642 vl_msg_api_barrier_release ();
643 }
644 else
645 {
Damjan Marioncada9eb2022-05-18 22:16:11 +0200646 if (m->replay_allowed)
Florin Corase86a8ed2018-01-05 03:20:25 -0800647 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
648 msg_id);
649 break;
650 }
651 break;
652 }
653
Damjan Marion8bea5892022-04-04 22:40:45 +0200654 vec_set_len (tmpbuf, 0);
Florin Corase86a8ed2018-01-05 03:20:25 -0800655 msg += size;
656 }
657
Florin Corase86a8ed2018-01-05 03:20:25 -0800658 munmap (hp, file_size);
659 vec_free (tmpbuf);
Filip Tehlar36217e32021-07-23 08:51:10 +0000660 vec_free (msgid_vec);
Florin Corase86a8ed2018-01-05 03:20:25 -0800661 am->replay_in_progress = 0;
662}
663
Filip Tehlar36217e32021-07-23 08:51:10 +0000664static int
665file_exists (u8 *fname)
666{
667 FILE *fp = 0;
668 fp = fopen ((char *) fname, "r");
669 if (fp)
670 {
671 fclose (fp);
672 return 1;
673 }
674 return 0;
675}
676
677typedef struct
678{
679 vlib_main_t *vm;
680 u8 is_json;
681} vl_msg_print_args;
682
683static int
684vl_msg_print_trace (u8 *msg, void *ctx)
685{
686 vl_msg_print_args *a = ctx;
687 api_main_t *am = vlibapi_get_main ();
688 u16 msg_id = ntohs (*((u16 *) msg));
Damjan Marioncada9eb2022-05-18 22:16:11 +0200689 vl_api_msg_data_t *m = vl_api_get_msg_data (am, msg_id);
690 void (*handler) (void *, void *) = 0;
Filip Tehlar36217e32021-07-23 08:51:10 +0000691 u8 is_json = a->is_json;
692 u8 *tmpbuf = 0;
693
Damjan Marioncada9eb2022-05-18 22:16:11 +0200694 if (!m)
695 {
696 vlib_cli_output (a->vm, "Unknown msg id %d\n", msg_id);
697 return 0;
698 }
699
Filip Tehlar36217e32021-07-23 08:51:10 +0000700 if (clib_arch_is_little_endian)
701 {
702 u32 msg_length = vec_len (msg);
703 vec_validate (tmpbuf, msg_length - 1);
704 clib_memcpy_fast (tmpbuf, msg, msg_length);
705 msg = tmpbuf;
706
Damjan Marioncada9eb2022-05-18 22:16:11 +0200707 m->endian_handler (tmpbuf);
Filip Tehlar36217e32021-07-23 08:51:10 +0000708 }
709
Damjan Marioncada9eb2022-05-18 22:16:11 +0200710 handler = is_json ? m->print_json_handler : m->print_handler;
Filip Tehlar36217e32021-07-23 08:51:10 +0000711
Damjan Marioncada9eb2022-05-18 22:16:11 +0200712 if (handler)
713 handler (msg, a->vm);
Filip Tehlar36217e32021-07-23 08:51:10 +0000714 else
Damjan Marioncada9eb2022-05-18 22:16:11 +0200715 vlib_cli_output (a->vm, "Skipping msg id %d: no print fcn\n", msg_id);
Filip Tehlar36217e32021-07-23 08:51:10 +0000716
717 vec_free (tmpbuf);
718 return 0;
719}
720
721static int
722vl_msg_api_dump_trace (vlib_main_t *vm, vl_api_trace_which_t which, u8 is_json)
723{
724 api_main_t *am = vlibapi_get_main ();
725 vl_api_trace_t *tp;
726
727 switch (which)
728 {
729 case VL_API_TRACE_TX:
730 tp = am->tx_trace;
731 break;
732 case VL_API_TRACE_RX:
733 tp = am->rx_trace;
734 break;
735 default:
736 return -1;
737 }
738
739 if (tp == 0 || tp->nitems == 0 || vec_len (tp->traces) == 0)
740 return -1;
741
742 vl_msg_print_args args;
743 clib_memset (&args, 0, sizeof (args));
744 args.is_json = is_json;
745 args.vm = vm;
746 vl_msg_traverse_trace (tp, vl_msg_print_trace, &args);
747
748 return 0;
749}
750
751static char *
752vl_msg_read_file (FILE *f)
753{
754 const size_t bufsize = 1024;
755 char *buf[bufsize], *v = 0;
756 size_t n;
757
758 while ((n = fread (buf, 1, bufsize, f)))
759 vec_add (v, buf, n);
760
Benoît Ganne2b9a4bf2021-10-11 13:49:59 +0200761 /* most callers expect a NULL-terminated C-string */
762 if (v)
763 vec_add1 (v, 0);
764
Filip Tehlar36217e32021-07-23 08:51:10 +0000765 return v;
766}
767
768static u16
769vl_msg_find_id_by_name_and_crc (vlib_main_t *vm, api_main_t *am, char *name)
770{
771 uword *p;
772 p = hash_get_mem (am->msg_index_by_name_and_crc, name);
773 if (!p)
774 return (u16) ~0;
775
776 return p[0];
777}
778
779static u16
780vl_msg_find_id_by_name (vlib_main_t *vm, api_main_t *am, char *name)
781{
782 uword *p;
783
784 if (!am->msg_id_by_name)
785 {
786 vlib_cli_output (vm, "message id table not yet initialized!\n");
787 return (u16) ~0;
788 }
789
790 p = hash_get_mem (am->msg_id_by_name, name);
791 if (!p)
792 return (u16) ~0;
793
794 return p[0];
795}
796
797static int
798vl_msg_exec_json_command (vlib_main_t *vm, cJSON *o)
799{
800 api_main_t *am = vlibapi_get_main ();
801 u16 msg_id;
Filip Tehlar36217e32021-07-23 08:51:10 +0000802 int len = 0, rv = -1;
Damjan Marioncada9eb2022-05-18 22:16:11 +0200803 vl_api_msg_data_t *m;
Filip Tehlar36217e32021-07-23 08:51:10 +0000804 u8 *msg = 0;
805
806 cJSON *msg_id_obj = cJSON_GetObjectItem (o, "_msgname");
807 if (!msg_id_obj)
808 {
809 vlib_cli_output (vm, "Missing '_msgname' element!\n");
810 return rv;
811 }
812 char *name = cJSON_GetStringValue (msg_id_obj);
813
814 cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc");
Florin Corasf6e6da92021-10-12 08:41:09 -0700815 if (!crc_obj)
Filip Tehlar36217e32021-07-23 08:51:10 +0000816 {
817 vlib_cli_output (vm, "Missing '_crc' element!\n");
818 return rv;
819 }
820 char *crc = cJSON_GetStringValue (crc_obj);
821 u8 proc_warning = 0;
822
823 u8 *name_crc = format (0, "%s_%s%c", name, crc, 0);
824 msg_id = vl_msg_find_id_by_name_and_crc (vm, am, (char *) name_crc);
Damjan Marioncada9eb2022-05-18 22:16:11 +0200825 m = vl_api_get_msg_data (am, msg_id);
Filip Tehlar36217e32021-07-23 08:51:10 +0000826 if (msg_id == (u16) ~0)
827 {
828 msg_id = vl_msg_find_id_by_name (vm, am, name);
829 if (msg_id == (u16) ~0)
830 {
831 vlib_cli_output (vm, "unknown msg id %d!\n", msg_id);
832 vec_free (name_crc);
833 return rv;
834 }
835 proc_warning = 1;
836 }
837 vec_free (name_crc);
838
Damjan Marioncada9eb2022-05-18 22:16:11 +0200839 if (m->replay_allowed)
Filip Tehlar36217e32021-07-23 08:51:10 +0000840 {
Filip Tehlar36217e32021-07-23 08:51:10 +0000841 if (proc_warning)
842 vlib_cli_output (vm, "warning: msg %d has different signature\n");
843
Damjan Marioncada9eb2022-05-18 22:16:11 +0200844 if (!m->fromjson_handler)
Filip Tehlar36217e32021-07-23 08:51:10 +0000845 {
846 vlib_cli_output (vm, "missing fromjson convert function! id %d\n",
847 msg_id);
848 return rv;
849 }
850
Damjan Marioncada9eb2022-05-18 22:16:11 +0200851 msg = (u8 *) m->fromjson_handler (o, &len);
Filip Tehlar36217e32021-07-23 08:51:10 +0000852 if (!msg)
853 {
854 vlib_cli_output (vm, "failed to convert JSON (msg id %d)!\n",
855 msg_id);
856 return rv;
857 }
858
859 if (clib_arch_is_little_endian)
Damjan Marioncada9eb2022-05-18 22:16:11 +0200860 m->endian_handler (msg);
Filip Tehlar36217e32021-07-23 08:51:10 +0000861
Damjan Marioncada9eb2022-05-18 22:16:11 +0200862 if (!m->handler)
Filip Tehlar36217e32021-07-23 08:51:10 +0000863 {
864 vlib_cli_output (vm, "no handler for msg id %d!\n", msg_id);
865 goto end;
866 }
867
Damjan Marioncada9eb2022-05-18 22:16:11 +0200868 if (!m->is_mp_safe)
Filip Tehlar36217e32021-07-23 08:51:10 +0000869 vl_msg_api_barrier_sync ();
Damjan Marioncada9eb2022-05-18 22:16:11 +0200870 m->handler (msg);
871 if (!m->is_mp_safe)
Filip Tehlar36217e32021-07-23 08:51:10 +0000872 vl_msg_api_barrier_release ();
873 }
874
875 rv = 0;
876end:
877 if (msg)
878 cJSON_free (msg);
879 return rv;
880}
881
882static void
883vl_msg_replay_json (vlib_main_t *vm, u8 *filename)
884{
885 api_main_t *am = vlibapi_get_main ();
886 cJSON *o = 0;
887 int rv = 0;
888 FILE *f = fopen ((char *) filename, "r");
889
890 if (!f)
891 {
892 vlib_cli_output (vm, "failed to open %s!\n", filename);
893 return;
894 }
895
896 char *buf = vl_msg_read_file (f);
897 fclose (f);
898
899 o = cJSON_Parse (buf);
900 vec_free (buf);
901 if (!o)
902 {
903 vlib_cli_output (vm, "%s: Failed parsing JSON input: %s\n", filename,
904 cJSON_GetErrorPtr ());
905 return;
906 }
907
908 if (cJSON_IsArray (o))
909 {
910 am->replay_in_progress = 1;
911 size_t size = cJSON_GetArraySize (o);
912 for (int i = 0; i < size; i++)
913 {
914 rv = vl_msg_exec_json_command (vm, cJSON_GetArrayItem (o, i));
915 if (rv < 0)
916 {
917 am->replay_in_progress = 0;
918 break;
919 }
920 }
921 }
922 else
923 {
924 rv = vl_msg_exec_json_command (vm, o);
925 }
926
927 if (rv < 0)
928 vlib_cli_output (vm, "error during replaying API trace");
929
930 cJSON_Delete (o);
931}
932
933static void
934vl_msg_dump_file_json (vlib_main_t *vm, u8 *filename)
935{
936 FILE *f = fopen ((char *) filename, "r");
937 char *buf;
938
939 if (!f)
940 {
941 vlib_cli_output (vm, "failed to open %s!\n", filename);
942 return;
943 }
944
945 buf = vl_msg_read_file (f);
946 fclose (f);
947
948 if (!buf)
949 {
950 vlib_cli_output (vm, "no content in %s!\n", filename);
951 return;
952 }
953
954 vlib_cli_output (vm, buf);
955 vec_free (buf);
956}
957
Dave Barachf66f8832020-01-23 08:44:40 -0500958/** api_trace_command_fn - control the binary API trace / replay feature
959
960 Note: this command MUST be marked thread-safe. Replay with
961 multiple worker threads depends in many cases on worker thread
962 graph replica maintenance. If we (implicitly) assert a worker
963 thread barrier at the debug CLI level, all graph replica changes
964 are deferred until the replay operation completes. If an interface
965 is deleted, the wheels fall off.
966 */
967
Florin Corase86a8ed2018-01-05 03:20:25 -0800968static clib_error_t *
969api_trace_command_fn (vlib_main_t * vm,
970 unformat_input_t * input, vlib_cli_command_t * cmd)
971{
Neale Rannsecff1cb2020-04-22 12:14:52 -0400972 unformat_input_t _line_input, *line_input = &_line_input;
Florin Corase86a8ed2018-01-05 03:20:25 -0800973 u32 nitems = 256 << 10;
Dave Barach39d69112019-11-27 11:42:13 -0500974 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800975 vl_api_trace_which_t which = VL_API_TRACE_RX;
Dave Barachf35a0722019-06-12 16:50:38 -0400976 u8 *filename = 0;
977 u8 *chroot_filename = 0;
Florin Corase86a8ed2018-01-05 03:20:25 -0800978 u32 first = 0;
979 u32 last = (u32) ~ 0;
980 FILE *fp;
981 int rv;
982
Neale Rannsecff1cb2020-04-22 12:14:52 -0400983 /* Get a line of input. */
984 if (!unformat_user (input, unformat_line_input, line_input))
985 return 0;
986
987 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
Florin Corase86a8ed2018-01-05 03:20:25 -0800988 {
Neale Rannsecff1cb2020-04-22 12:14:52 -0400989 if (unformat (line_input, "on") || unformat (line_input, "enable"))
Florin Corase86a8ed2018-01-05 03:20:25 -0800990 {
Neale Rannsecff1cb2020-04-22 12:14:52 -0400991 if (unformat (line_input, "nitems %d", &nitems))
Florin Corase86a8ed2018-01-05 03:20:25 -0800992 ;
Dave Barachf66f8832020-01-23 08:44:40 -0500993 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800994 vl_msg_api_trace_configure (am, which, nitems);
995 vl_msg_api_trace_onoff (am, which, 1 /* on */ );
Dave Barachf66f8832020-01-23 08:44:40 -0500996 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800997 }
Neale Rannsecff1cb2020-04-22 12:14:52 -0400998 else if (unformat (line_input, "off"))
Florin Corase86a8ed2018-01-05 03:20:25 -0800999 {
Dave Barachf66f8832020-01-23 08:44:40 -05001000 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001001 vl_msg_api_trace_onoff (am, which, 0);
Dave Barachf66f8832020-01-23 08:44:40 -05001002 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001003 }
Filip Tehlar36217e32021-07-23 08:51:10 +00001004 else if (unformat (line_input, "save-json %s", &filename))
1005 {
1006 if (strstr ((char *) filename, "..") ||
1007 index ((char *) filename, '/'))
1008 {
1009 vlib_cli_output (vm, "illegal characters in filename '%s'",
1010 filename);
1011 goto out;
1012 }
1013
1014 chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1015
1016 vec_free (filename);
1017
1018 if (file_exists (chroot_filename))
1019 {
1020 vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1021 goto out;
1022 }
1023
1024 fp = fopen ((char *) chroot_filename, "w");
1025 if (fp == NULL)
1026 {
1027 vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
1028 goto out;
1029 }
1030 vlib_worker_thread_barrier_sync (vm);
1031 rv = vl_msg_api_trace_save (am, which, fp, 1);
1032 if (rv == -1)
1033 vlib_cli_output (vm, "API Trace data not present\n");
1034 else if (rv < 0)
1035 vlib_cli_output (vm, "failed to save api trace\n");
1036 else
1037 vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
1038 vlib_worker_thread_barrier_release (vm);
1039 fclose (fp);
1040 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001041 else if (unformat (line_input, "save %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001042 {
Florin Corase86a8ed2018-01-05 03:20:25 -08001043 if (strstr ((char *) filename, "..")
1044 || index ((char *) filename, '/'))
1045 {
1046 vlib_cli_output (vm, "illegal characters in filename '%s'",
1047 filename);
Dave Barachf35a0722019-06-12 16:50:38 -04001048 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -08001049 }
1050
1051 chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1052
1053 vec_free (filename);
1054
Filip Tehlar36217e32021-07-23 08:51:10 +00001055 if (file_exists (chroot_filename))
1056 {
1057 vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1058 goto out;
1059 }
1060
Florin Corase86a8ed2018-01-05 03:20:25 -08001061 fp = fopen ((char *) chroot_filename, "w");
1062 if (fp == NULL)
1063 {
1064 vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
Dave Barachf35a0722019-06-12 16:50:38 -04001065 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -08001066 }
Dave Barachf66f8832020-01-23 08:44:40 -05001067 vlib_worker_thread_barrier_sync (vm);
Filip Tehlar36217e32021-07-23 08:51:10 +00001068 rv = vl_msg_api_trace_save (am, which, fp, 0);
Dave Barachf66f8832020-01-23 08:44:40 -05001069 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001070 fclose (fp);
1071 if (rv == -1)
1072 vlib_cli_output (vm, "API Trace data not present\n");
1073 else if (rv == -2)
1074 vlib_cli_output (vm, "File for writing is closed\n");
1075 else if (rv == -10)
1076 vlib_cli_output (vm, "Error while writing header to file\n");
1077 else if (rv == -11)
1078 vlib_cli_output (vm, "Error while writing trace to file\n");
1079 else if (rv == -12)
1080 vlib_cli_output (vm,
1081 "Error while writing end of buffer trace to file\n");
1082 else if (rv == -13)
1083 vlib_cli_output (vm,
1084 "Error while writing start of buffer trace to file\n");
1085 else if (rv < 0)
Andrey "Zed" Zaikin701625b2018-04-18 17:07:07 +03001086 vlib_cli_output (vm, "Unknown error while saving: %d", rv);
Florin Corase86a8ed2018-01-05 03:20:25 -08001087 else
1088 vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
Dave Barachf35a0722019-06-12 16:50:38 -04001089 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -08001090 }
Filip Tehlar36217e32021-07-23 08:51:10 +00001091 else if (unformat (line_input, "tojson %s", &filename))
1092 {
1093 vl_msg_api_process_file (vm, filename, first, last, DUMP_JSON);
1094 }
1095 else if (unformat (line_input, "dump-file-json %s", &filename))
1096 {
1097 vl_msg_dump_file_json (vm, filename);
1098 }
1099 else if (unformat (line_input, "dump-file %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001100 {
1101 vl_msg_api_process_file (vm, filename, first, last, DUMP);
1102 }
Filip Tehlar36217e32021-07-23 08:51:10 +00001103 else if (unformat (line_input, "dump-json"))
1104 {
1105 vl_msg_api_dump_trace (vm, which, 1);
1106 }
1107 else if (unformat (line_input, "dump"))
1108 {
1109 vl_msg_api_dump_trace (vm, which, 0);
1110 }
1111 else if (unformat (line_input, "replay-json %s", &filename))
1112 {
1113 vl_msg_replay_json (vm, filename);
1114 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001115 else if (unformat (line_input, "replay %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001116 {
1117 vl_msg_api_process_file (vm, filename, first, last, REPLAY);
1118 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001119 else if (unformat (line_input, "initializers %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001120 {
1121 vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
1122 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001123 else if (unformat (line_input, "tx"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001124 {
1125 which = VL_API_TRACE_TX;
1126 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001127 else if (unformat (line_input, "first %d", &first))
Florin Corase86a8ed2018-01-05 03:20:25 -08001128 {
1129 ;
1130 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001131 else if (unformat (line_input, "last %d", &last))
Florin Corase86a8ed2018-01-05 03:20:25 -08001132 {
1133 ;
1134 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001135 else if (unformat (line_input, "status"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001136 {
1137 vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
1138 am, which);
1139 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001140 else if (unformat (line_input, "free"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001141 {
Dave Barachf66f8832020-01-23 08:44:40 -05001142 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001143 vl_msg_api_trace_onoff (am, which, 0);
1144 vl_msg_api_trace_free (am, which);
Dave Barachf66f8832020-01-23 08:44:40 -05001145 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001146 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001147 else if (unformat (line_input, "post-mortem-on"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001148 vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
Neale Rannsecff1cb2020-04-22 12:14:52 -04001149 else if (unformat (line_input, "post-mortem-off"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001150 vl_msg_api_post_mortem_dump_enable_disable (0 /* enable */ );
1151 else
1152 return clib_error_return (0, "unknown input `%U'",
1153 format_unformat_error, input);
1154 }
Dave Barachf35a0722019-06-12 16:50:38 -04001155out:
1156 vec_free (filename);
1157 vec_free (chroot_filename);
Neale Rannsecff1cb2020-04-22 12:14:52 -04001158 unformat_free (line_input);
Florin Corase86a8ed2018-01-05 03:20:25 -08001159 return 0;
1160}
1161
1162/*?
1163 * Display, replay, or save a binary API trace
1164?*/
1165
1166/* *INDENT-OFF* */
Filip Tehlarc863a912021-06-15 10:29:54 +00001167VLIB_CLI_COMMAND (api_trace_command, static) = {
Florin Corase86a8ed2018-01-05 03:20:25 -08001168 .path = "api trace",
Filip Tehlar36217e32021-07-23 08:51:10 +00001169 .short_help = "api trace [tx][on|off][first <n>][last <n>][status][free]"
1170 "[post-mortem-on][dump|dump-file|dump-json|save|tojson|save-"
1171 "json|replay <file>|replay-json <file>][nitems <n>]"
1172 "[initializers <file>]",
Florin Corase86a8ed2018-01-05 03:20:25 -08001173 .function = api_trace_command_fn,
Dave Barachf66f8832020-01-23 08:44:40 -05001174 .is_mp_safe = 1,
Florin Corase86a8ed2018-01-05 03:20:25 -08001175};
1176/* *INDENT-ON* */
1177
1178static clib_error_t *
Florin Corase86a8ed2018-01-05 03:20:25 -08001179api_trace_config_fn (vlib_main_t * vm, unformat_input_t * input)
1180{
1181 u32 nitems = 256 << 10;
1182 vl_api_trace_which_t which = VL_API_TRACE_RX;
Dave Barach39d69112019-11-27 11:42:13 -05001183 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001184
1185 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1186 {
1187 if (unformat (input, "on") || unformat (input, "enable"))
1188 {
1189 if (unformat (input, "nitems %d", &nitems))
1190 ;
1191 vl_msg_api_trace_configure (am, which, nitems);
1192 vl_msg_api_trace_onoff (am, which, 1 /* on */ );
1193 vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1194 }
1195 else if (unformat (input, "save-api-table %s",
1196 &am->save_msg_table_filename))
1197 ;
1198 else
1199 return clib_error_return (0, "unknown input `%U'",
1200 format_unformat_error, input);
1201 }
1202 return 0;
1203}
1204
1205/*?
1206 * This module has three configuration parameters:
1207 * "on" or "enable" - enables binary api tracing
1208 * "nitems <nnn>" - sets the size of the circular buffer to <nnn>
1209 * "save-api-table <filename>" - dumps the API message table to /tmp/<filename>
1210?*/
1211VLIB_CONFIG_FUNCTION (api_trace_config_fn, "api-trace");
1212
1213static clib_error_t *
1214api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
1215{
Dave Barach39d69112019-11-27 11:42:13 -05001216 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001217 u32 nitems;
1218
1219 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1220 {
1221 if (unformat (input, "length %d", &nitems) ||
1222 (unformat (input, "len %d", &nitems)))
1223 {
1224 if (nitems >= 1024)
1225 am->vlib_input_queue_length = nitems;
1226 else
1227 clib_warning ("vlib input queue length %d too small, ignored",
1228 nitems);
1229 }
1230 else
1231 return clib_error_return (0, "unknown input `%U'",
1232 format_unformat_error, input);
1233 }
1234 return 0;
1235}
1236
1237VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
1238
1239static u8 *
1240extract_name (u8 * s)
1241{
1242 u8 *rv;
1243
1244 rv = vec_dup (s);
1245
1246 while (vec_len (rv) && rv[vec_len (rv)] != '_')
Andrew Yourtchenko105cb1e2022-04-15 14:23:41 +00001247 vec_dec_len (rv, 1);
Florin Corase86a8ed2018-01-05 03:20:25 -08001248
1249 rv[vec_len (rv)] = 0;
1250
1251 return rv;
1252}
1253
1254static u8 *
1255extract_crc (u8 * s)
1256{
1257 int i;
1258 u8 *rv;
1259
1260 rv = vec_dup (s);
1261
1262 for (i = vec_len (rv) - 1; i >= 0; i--)
1263 {
1264 if (rv[i] == '_')
1265 {
1266 vec_delete (rv, i + 1, 0);
1267 break;
1268 }
1269 }
1270 return rv;
1271}
1272
1273typedef struct
1274{
1275 u8 *name_and_crc;
1276 u8 *name;
1277 u8 *crc;
1278 u32 msg_index;
1279 int which;
1280} msg_table_unserialize_t;
1281
1282static int
1283table_id_cmp (void *a1, void *a2)
1284{
1285 msg_table_unserialize_t *n1 = a1;
1286 msg_table_unserialize_t *n2 = a2;
1287
1288 return (n1->msg_index - n2->msg_index);
1289}
1290
1291static int
1292table_name_and_crc_cmp (void *a1, void *a2)
1293{
1294 msg_table_unserialize_t *n1 = a1;
1295 msg_table_unserialize_t *n2 = a2;
1296
1297 return strcmp ((char *) n1->name_and_crc, (char *) n2->name_and_crc);
1298}
1299
1300static clib_error_t *
1301dump_api_table_file_command_fn (vlib_main_t * vm,
1302 unformat_input_t * input,
1303 vlib_cli_command_t * cmd)
1304{
1305 u8 *filename = 0;
Dave Barach39d69112019-11-27 11:42:13 -05001306 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001307 serialize_main_t _sm, *sm = &_sm;
1308 clib_error_t *error;
1309 u32 nmsgs;
1310 u32 msg_index;
1311 u8 *name_and_crc;
1312 int compare_current = 0;
1313 int numeric_sort = 0;
1314 msg_table_unserialize_t *table = 0, *item;
1315 u32 i;
1316 u32 ndifferences = 0;
1317
1318 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1319 {
1320 if (unformat (input, "file %s", &filename))
1321 ;
1322 else if (unformat (input, "compare-current")
1323 || unformat (input, "compare"))
1324 compare_current = 1;
1325 else if (unformat (input, "numeric"))
1326 numeric_sort = 1;
1327 else
1328 return clib_error_return (0, "unknown input `%U'",
1329 format_unformat_error, input);
1330 }
1331
1332 if (numeric_sort && compare_current)
1333 return clib_error_return
1334 (0, "Comparison and numeric sorting are incompatible");
1335
1336 if (filename == 0)
1337 return clib_error_return (0, "File not specified");
1338
1339 /* Load the serialized message table from the table dump */
1340
1341 error = unserialize_open_clib_file (sm, (char *) filename);
1342
1343 if (error)
1344 return error;
1345
1346 unserialize_integer (sm, &nmsgs, sizeof (u32));
1347
1348 for (i = 0; i < nmsgs; i++)
1349 {
1350 msg_index = unserialize_likely_small_unsigned_integer (sm);
1351 unserialize_cstring (sm, (char **) &name_and_crc);
1352 vec_add2 (table, item, 1);
1353 item->msg_index = msg_index;
1354 item->name_and_crc = name_and_crc;
1355 item->name = extract_name (name_and_crc);
1356 item->crc = extract_crc (name_and_crc);
1357 item->which = 0; /* file */
1358 }
Ole Troanedfe2c02019-07-30 15:38:13 +02001359 unserialize_close (sm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001360
1361 /* Compare with the current image? */
1362 if (compare_current)
1363 {
1364 /* Append the current message table */
1365 u8 *tblv = vl_api_serialize_message_table (am, 0);
1366
1367 serialize_open_vector (sm, tblv);
1368 unserialize_integer (sm, &nmsgs, sizeof (u32));
1369
1370 for (i = 0; i < nmsgs; i++)
1371 {
1372 msg_index = unserialize_likely_small_unsigned_integer (sm);
1373 unserialize_cstring (sm, (char **) &name_and_crc);
1374
1375 vec_add2 (table, item, 1);
1376 item->msg_index = msg_index;
1377 item->name_and_crc = name_and_crc;
1378 item->name = extract_name (name_and_crc);
1379 item->crc = extract_crc (name_and_crc);
1380 item->which = 1; /* current_image */
1381 }
1382 vec_free (tblv);
1383 }
1384
1385 /* Sort the table. */
1386 if (numeric_sort)
1387 vec_sort_with_function (table, table_id_cmp);
1388 else
1389 vec_sort_with_function (table, table_name_and_crc_cmp);
1390
1391 if (compare_current)
1392 {
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001393 u8 *dashes = 0;
Florin Corase86a8ed2018-01-05 03:20:25 -08001394 ndifferences = 0;
1395
1396 /*
1397 * In this case, the recovered table will have two entries per
1398 * API message. So, if entries i and i+1 match, the message definitions
1399 * are identical. Otherwise, the crc is different, or a message is
1400 * present in only one of the tables.
1401 */
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001402 vlib_cli_output (vm, "%-60s | %s", "Message Name", "Result");
1403 vec_validate_init_empty (dashes, 60, '-');
1404 vec_terminate_c_string (dashes);
1405 vlib_cli_output (vm, "%60s-|-%s", dashes, "-----------------");
1406 vec_free (dashes);
Florin Corase86a8ed2018-01-05 03:20:25 -08001407 for (i = 0; i < vec_len (table);)
1408 {
1409 /* Last message lonely? */
1410 if (i == vec_len (table) - 1)
1411 {
1412 ndifferences++;
1413 goto last_unique;
1414 }
1415
1416 /* Identical pair? */
1417 if (!strncmp
1418 ((char *) table[i].name_and_crc,
1419 (char *) table[i + 1].name_and_crc,
1420 vec_len (table[i].name_and_crc)))
1421 {
1422 i += 2;
1423 continue;
1424 }
1425
1426 ndifferences++;
1427
1428 /* Only in one of two tables? */
Andrew Yourtchenkoae605b82019-01-21 16:39:33 +01001429 if (i + 1 == vec_len (table)
1430 || strcmp ((char *) table[i].name, (char *) table[i + 1].name))
Florin Corase86a8ed2018-01-05 03:20:25 -08001431 {
1432 last_unique:
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001433 vlib_cli_output (vm, "%-60s | only in %s",
Florin Corase86a8ed2018-01-05 03:20:25 -08001434 table[i].name, table[i].which ?
1435 "image" : "file");
1436 i++;
1437 continue;
1438 }
1439 /* In both tables, but with different signatures */
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001440 vlib_cli_output (vm, "%-60s | definition changed", table[i].name);
Florin Corase86a8ed2018-01-05 03:20:25 -08001441 i += 2;
1442 }
1443 if (ndifferences == 0)
1444 vlib_cli_output (vm, "No api message signature differences found.");
1445 else
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001446 vlib_cli_output (vm, "\nFound %u api message signature differences",
Florin Corase86a8ed2018-01-05 03:20:25 -08001447 ndifferences);
1448 goto cleanup;
1449 }
1450
1451 /* Dump the table, sorted as shown above */
1452 vlib_cli_output (vm, "%=60s %=8s %=10s", "Message name", "MsgID", "CRC");
1453
1454 for (i = 0; i < vec_len (table); i++)
1455 {
1456 item = table + i;
1457 vlib_cli_output (vm, "%-60s %8u %10s", item->name,
1458 item->msg_index, item->crc);
1459 }
1460
1461cleanup:
1462 for (i = 0; i < vec_len (table); i++)
1463 {
1464 vec_free (table[i].name_and_crc);
1465 vec_free (table[i].name);
1466 vec_free (table[i].crc);
1467 }
1468
1469 vec_free (table);
1470
1471 return 0;
1472}
1473
1474/*?
1475 * Displays a serialized API message decode table, sorted by message name
1476 *
1477 * @cliexpar
1478 * @cliexstart{show api dump file <filename>}
1479 * Message name MsgID CRC
1480 * accept_session 407 8e2a127e
1481 * accept_session_reply 408 67d8c22a
1482 * add_node_next 549 e4202993
1483 * add_node_next_reply 550 e89d6eed
1484 * etc.
1485 * @cliexend
1486?*/
1487
1488/*?
1489 * Compares a serialized API message decode table with the current image
1490 *
1491 * @cliexpar
1492 * @cliexstart{show api dump file <filename> compare}
1493 * ip_add_del_route definition changed
1494 * ip_table_add_del definition changed
1495 * l2_macs_event only in image
1496 * vnet_ip4_fib_counters only in file
1497 * vnet_ip4_nbr_counters only in file
1498 * @cliexend
1499?*/
1500
1501/*?
1502 * Display a serialized API message decode table, compare a saved
1503 * decode table with the current image, to establish API differences.
1504 *
1505?*/
1506/* *INDENT-OFF* */
1507VLIB_CLI_COMMAND (dump_api_table_file, static) =
1508{
1509 .path = "show api dump",
1510 .short_help = "show api dump file <filename> [numeric | compare-current]",
1511 .function = dump_api_table_file_command_fn,
1512};
1513
1514/* *INDENT-ON* */
1515/*
1516 * fd.io coding-style-patch-verification: ON
1517 *
1518 * Local Variables:
1519 * eval: (c-set-style "gnu")
1520 * End:
1521 */