blob: e53ea9598785ceff8bd55a4a83614a63d480bd9f [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 Marioncada9eb2022-05-18 22:16:11 +0200587 if (m && m->print_json_handler)
Filip Tehlar36217e32021-07-23 08:51:10 +0000588 {
Damjan Marioncada9eb2022-05-18 22:16:11 +0200589 m->print_json_handler (tmpbuf + sizeof (uword), vm);
Filip Tehlar36217e32021-07-23 08:51:10 +0000590 }
591 else
592 {
593 vlib_cli_output (vm, "Skipping msg id %d: no JSON print fcn\n",
594 msg_id);
595 break;
596 }
597 break;
598
Florin Corase86a8ed2018-01-05 03:20:25 -0800599 case DUMP:
Damjan Marioncada9eb2022-05-18 22:16:11 +0200600 if (m && m->print_handler)
Florin Corase86a8ed2018-01-05 03:20:25 -0800601 {
Damjan Marioncada9eb2022-05-18 22:16:11 +0200602 m->print_handler (tmpbuf + sizeof (uword), vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800603 }
604 else
605 {
606 vlib_cli_output (vm, "Skipping msg id %d: no print fcn\n",
607 msg_id);
608 break;
609 }
610 break;
611
612 case INITIALIZERS:
Damjan Marioncada9eb2022-05-18 22:16:11 +0200613 if (m && m->print_handler)
Florin Corase86a8ed2018-01-05 03:20:25 -0800614 {
615 u8 *s;
616 int j;
Florin Corase86a8ed2018-01-05 03:20:25 -0800617
618 vlib_cli_output (vm, "/*");
619
Damjan Marioncada9eb2022-05-18 22:16:11 +0200620 m->print_handler (tmpbuf + sizeof (uword), vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800621 vlib_cli_output (vm, "*/\n");
622
Damjan Marioncada9eb2022-05-18 22:16:11 +0200623 s = format (0, "static u8 * vl_api_%s_%d[%d] = {", m->name, i,
624 m->trace_size);
Florin Corase86a8ed2018-01-05 03:20:25 -0800625
Damjan Marioncada9eb2022-05-18 22:16:11 +0200626 for (j = 0; j < m->trace_size; j++)
Florin Corase86a8ed2018-01-05 03:20:25 -0800627 {
628 if ((j & 7) == 0)
629 s = format (s, "\n ");
630 s = format (s, "0x%02x,", tmpbuf[sizeof (uword) + j]);
631 }
632 s = format (s, "\n};\n%c", 0);
633 vlib_cli_output (vm, (char *) s);
634 vec_free (s);
635 }
636 break;
637
638 case REPLAY:
Damjan Marioncada9eb2022-05-18 22:16:11 +0200639 if (m && m->print_handler && m->replay_allowed)
Florin Corase86a8ed2018-01-05 03:20:25 -0800640 {
Damjan Marioncada9eb2022-05-18 22:16:11 +0200641 if (!m->is_mp_safe)
Florin Corase86a8ed2018-01-05 03:20:25 -0800642 vl_msg_api_barrier_sync ();
Damjan Marioncada9eb2022-05-18 22:16:11 +0200643 m->handler (tmpbuf + sizeof (uword));
644 if (!m->is_mp_safe)
Florin Corase86a8ed2018-01-05 03:20:25 -0800645 vl_msg_api_barrier_release ();
646 }
647 else
648 {
Andrew Yourtchenko618afb52022-08-26 13:46:44 +0000649 if (m && m->replay_allowed)
Florin Corase86a8ed2018-01-05 03:20:25 -0800650 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
651 msg_id);
652 break;
653 }
654 break;
655 }
656
Damjan Marion8bea5892022-04-04 22:40:45 +0200657 vec_set_len (tmpbuf, 0);
Florin Corase86a8ed2018-01-05 03:20:25 -0800658 msg += size;
659 }
660
Florin Corase86a8ed2018-01-05 03:20:25 -0800661 munmap (hp, file_size);
662 vec_free (tmpbuf);
Filip Tehlar36217e32021-07-23 08:51:10 +0000663 vec_free (msgid_vec);
Florin Corase86a8ed2018-01-05 03:20:25 -0800664 am->replay_in_progress = 0;
665}
666
Filip Tehlar36217e32021-07-23 08:51:10 +0000667static int
668file_exists (u8 *fname)
669{
670 FILE *fp = 0;
671 fp = fopen ((char *) fname, "r");
672 if (fp)
673 {
674 fclose (fp);
675 return 1;
676 }
677 return 0;
678}
679
680typedef struct
681{
682 vlib_main_t *vm;
683 u8 is_json;
684} vl_msg_print_args;
685
686static int
687vl_msg_print_trace (u8 *msg, void *ctx)
688{
689 vl_msg_print_args *a = ctx;
690 api_main_t *am = vlibapi_get_main ();
691 u16 msg_id = ntohs (*((u16 *) msg));
Damjan Marioncada9eb2022-05-18 22:16:11 +0200692 vl_api_msg_data_t *m = vl_api_get_msg_data (am, msg_id);
693 void (*handler) (void *, void *) = 0;
Filip Tehlar36217e32021-07-23 08:51:10 +0000694 u8 is_json = a->is_json;
695 u8 *tmpbuf = 0;
696
Damjan Marioncada9eb2022-05-18 22:16:11 +0200697 if (!m)
698 {
699 vlib_cli_output (a->vm, "Unknown msg id %d\n", msg_id);
700 return 0;
701 }
702
Filip Tehlar36217e32021-07-23 08:51:10 +0000703 if (clib_arch_is_little_endian)
704 {
705 u32 msg_length = vec_len (msg);
706 vec_validate (tmpbuf, msg_length - 1);
707 clib_memcpy_fast (tmpbuf, msg, msg_length);
708 msg = tmpbuf;
709
Damjan Marioncada9eb2022-05-18 22:16:11 +0200710 m->endian_handler (tmpbuf);
Filip Tehlar36217e32021-07-23 08:51:10 +0000711 }
712
Damjan Marioncada9eb2022-05-18 22:16:11 +0200713 handler = is_json ? m->print_json_handler : m->print_handler;
Filip Tehlar36217e32021-07-23 08:51:10 +0000714
Damjan Marioncada9eb2022-05-18 22:16:11 +0200715 if (handler)
716 handler (msg, a->vm);
Filip Tehlar36217e32021-07-23 08:51:10 +0000717 else
Damjan Marioncada9eb2022-05-18 22:16:11 +0200718 vlib_cli_output (a->vm, "Skipping msg id %d: no print fcn\n", msg_id);
Filip Tehlar36217e32021-07-23 08:51:10 +0000719
720 vec_free (tmpbuf);
721 return 0;
722}
723
724static int
725vl_msg_api_dump_trace (vlib_main_t *vm, vl_api_trace_which_t which, u8 is_json)
726{
727 api_main_t *am = vlibapi_get_main ();
728 vl_api_trace_t *tp;
729
730 switch (which)
731 {
732 case VL_API_TRACE_TX:
733 tp = am->tx_trace;
734 break;
735 case VL_API_TRACE_RX:
736 tp = am->rx_trace;
737 break;
738 default:
739 return -1;
740 }
741
742 if (tp == 0 || tp->nitems == 0 || vec_len (tp->traces) == 0)
743 return -1;
744
745 vl_msg_print_args args;
746 clib_memset (&args, 0, sizeof (args));
747 args.is_json = is_json;
748 args.vm = vm;
749 vl_msg_traverse_trace (tp, vl_msg_print_trace, &args);
750
751 return 0;
752}
753
754static char *
755vl_msg_read_file (FILE *f)
756{
757 const size_t bufsize = 1024;
758 char *buf[bufsize], *v = 0;
759 size_t n;
760
761 while ((n = fread (buf, 1, bufsize, f)))
762 vec_add (v, buf, n);
763
Benoît Ganne2b9a4bf2021-10-11 13:49:59 +0200764 /* most callers expect a NULL-terminated C-string */
765 if (v)
766 vec_add1 (v, 0);
767
Filip Tehlar36217e32021-07-23 08:51:10 +0000768 return v;
769}
770
771static u16
772vl_msg_find_id_by_name_and_crc (vlib_main_t *vm, api_main_t *am, char *name)
773{
774 uword *p;
775 p = hash_get_mem (am->msg_index_by_name_and_crc, name);
776 if (!p)
777 return (u16) ~0;
778
779 return p[0];
780}
781
782static u16
783vl_msg_find_id_by_name (vlib_main_t *vm, api_main_t *am, char *name)
784{
785 uword *p;
786
787 if (!am->msg_id_by_name)
788 {
789 vlib_cli_output (vm, "message id table not yet initialized!\n");
790 return (u16) ~0;
791 }
792
793 p = hash_get_mem (am->msg_id_by_name, name);
794 if (!p)
795 return (u16) ~0;
796
797 return p[0];
798}
799
800static int
801vl_msg_exec_json_command (vlib_main_t *vm, cJSON *o)
802{
803 api_main_t *am = vlibapi_get_main ();
804 u16 msg_id;
Filip Tehlar36217e32021-07-23 08:51:10 +0000805 int len = 0, rv = -1;
Damjan Marioncada9eb2022-05-18 22:16:11 +0200806 vl_api_msg_data_t *m;
Filip Tehlar36217e32021-07-23 08:51:10 +0000807 u8 *msg = 0;
808
809 cJSON *msg_id_obj = cJSON_GetObjectItem (o, "_msgname");
810 if (!msg_id_obj)
811 {
812 vlib_cli_output (vm, "Missing '_msgname' element!\n");
813 return rv;
814 }
815 char *name = cJSON_GetStringValue (msg_id_obj);
816
817 cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc");
Florin Corasf6e6da92021-10-12 08:41:09 -0700818 if (!crc_obj)
Filip Tehlar36217e32021-07-23 08:51:10 +0000819 {
820 vlib_cli_output (vm, "Missing '_crc' element!\n");
821 return rv;
822 }
823 char *crc = cJSON_GetStringValue (crc_obj);
824 u8 proc_warning = 0;
825
826 u8 *name_crc = format (0, "%s_%s%c", name, crc, 0);
827 msg_id = vl_msg_find_id_by_name_and_crc (vm, am, (char *) name_crc);
Damjan Marioncada9eb2022-05-18 22:16:11 +0200828 m = vl_api_get_msg_data (am, msg_id);
Filip Tehlar36217e32021-07-23 08:51:10 +0000829 if (msg_id == (u16) ~0)
830 {
831 msg_id = vl_msg_find_id_by_name (vm, am, name);
832 if (msg_id == (u16) ~0)
833 {
834 vlib_cli_output (vm, "unknown msg id %d!\n", msg_id);
835 vec_free (name_crc);
836 return rv;
837 }
838 proc_warning = 1;
839 }
840 vec_free (name_crc);
841
Damjan Marioncada9eb2022-05-18 22:16:11 +0200842 if (m->replay_allowed)
Filip Tehlar36217e32021-07-23 08:51:10 +0000843 {
Filip Tehlar36217e32021-07-23 08:51:10 +0000844 if (proc_warning)
845 vlib_cli_output (vm, "warning: msg %d has different signature\n");
846
Damjan Marioncada9eb2022-05-18 22:16:11 +0200847 if (!m->fromjson_handler)
Filip Tehlar36217e32021-07-23 08:51:10 +0000848 {
849 vlib_cli_output (vm, "missing fromjson convert function! id %d\n",
850 msg_id);
851 return rv;
852 }
853
Damjan Marioncada9eb2022-05-18 22:16:11 +0200854 msg = (u8 *) m->fromjson_handler (o, &len);
Filip Tehlar36217e32021-07-23 08:51:10 +0000855 if (!msg)
856 {
857 vlib_cli_output (vm, "failed to convert JSON (msg id %d)!\n",
858 msg_id);
859 return rv;
860 }
861
862 if (clib_arch_is_little_endian)
Damjan Marioncada9eb2022-05-18 22:16:11 +0200863 m->endian_handler (msg);
Filip Tehlar36217e32021-07-23 08:51:10 +0000864
Damjan Marioncada9eb2022-05-18 22:16:11 +0200865 if (!m->handler)
Filip Tehlar36217e32021-07-23 08:51:10 +0000866 {
867 vlib_cli_output (vm, "no handler for msg id %d!\n", msg_id);
868 goto end;
869 }
870
Damjan Marioncada9eb2022-05-18 22:16:11 +0200871 if (!m->is_mp_safe)
Filip Tehlar36217e32021-07-23 08:51:10 +0000872 vl_msg_api_barrier_sync ();
Damjan Marioncada9eb2022-05-18 22:16:11 +0200873 m->handler (msg);
874 if (!m->is_mp_safe)
Filip Tehlar36217e32021-07-23 08:51:10 +0000875 vl_msg_api_barrier_release ();
876 }
877
878 rv = 0;
879end:
880 if (msg)
881 cJSON_free (msg);
882 return rv;
883}
884
885static void
886vl_msg_replay_json (vlib_main_t *vm, u8 *filename)
887{
888 api_main_t *am = vlibapi_get_main ();
889 cJSON *o = 0;
890 int rv = 0;
891 FILE *f = fopen ((char *) filename, "r");
892
893 if (!f)
894 {
895 vlib_cli_output (vm, "failed to open %s!\n", filename);
896 return;
897 }
898
899 char *buf = vl_msg_read_file (f);
900 fclose (f);
901
902 o = cJSON_Parse (buf);
903 vec_free (buf);
904 if (!o)
905 {
906 vlib_cli_output (vm, "%s: Failed parsing JSON input: %s\n", filename,
907 cJSON_GetErrorPtr ());
908 return;
909 }
910
911 if (cJSON_IsArray (o))
912 {
913 am->replay_in_progress = 1;
914 size_t size = cJSON_GetArraySize (o);
915 for (int i = 0; i < size; i++)
916 {
917 rv = vl_msg_exec_json_command (vm, cJSON_GetArrayItem (o, i));
918 if (rv < 0)
919 {
920 am->replay_in_progress = 0;
921 break;
922 }
923 }
924 }
925 else
926 {
927 rv = vl_msg_exec_json_command (vm, o);
928 }
929
930 if (rv < 0)
931 vlib_cli_output (vm, "error during replaying API trace");
932
933 cJSON_Delete (o);
934}
935
936static void
937vl_msg_dump_file_json (vlib_main_t *vm, u8 *filename)
938{
939 FILE *f = fopen ((char *) filename, "r");
940 char *buf;
941
942 if (!f)
943 {
944 vlib_cli_output (vm, "failed to open %s!\n", filename);
945 return;
946 }
947
948 buf = vl_msg_read_file (f);
949 fclose (f);
950
951 if (!buf)
952 {
953 vlib_cli_output (vm, "no content in %s!\n", filename);
954 return;
955 }
956
957 vlib_cli_output (vm, buf);
958 vec_free (buf);
959}
960
Dave Barachf66f8832020-01-23 08:44:40 -0500961/** api_trace_command_fn - control the binary API trace / replay feature
962
963 Note: this command MUST be marked thread-safe. Replay with
964 multiple worker threads depends in many cases on worker thread
965 graph replica maintenance. If we (implicitly) assert a worker
966 thread barrier at the debug CLI level, all graph replica changes
967 are deferred until the replay operation completes. If an interface
968 is deleted, the wheels fall off.
969 */
970
Florin Corase86a8ed2018-01-05 03:20:25 -0800971static clib_error_t *
972api_trace_command_fn (vlib_main_t * vm,
973 unformat_input_t * input, vlib_cli_command_t * cmd)
974{
Neale Rannsecff1cb2020-04-22 12:14:52 -0400975 unformat_input_t _line_input, *line_input = &_line_input;
Florin Corase86a8ed2018-01-05 03:20:25 -0800976 u32 nitems = 256 << 10;
Dave Barach39d69112019-11-27 11:42:13 -0500977 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -0800978 vl_api_trace_which_t which = VL_API_TRACE_RX;
Dave Barachf35a0722019-06-12 16:50:38 -0400979 u8 *filename = 0;
980 u8 *chroot_filename = 0;
Florin Corase86a8ed2018-01-05 03:20:25 -0800981 u32 first = 0;
982 u32 last = (u32) ~ 0;
983 FILE *fp;
984 int rv;
985
Neale Rannsecff1cb2020-04-22 12:14:52 -0400986 /* Get a line of input. */
987 if (!unformat_user (input, unformat_line_input, line_input))
988 return 0;
989
990 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
Florin Corase86a8ed2018-01-05 03:20:25 -0800991 {
Neale Rannsecff1cb2020-04-22 12:14:52 -0400992 if (unformat (line_input, "on") || unformat (line_input, "enable"))
Florin Corase86a8ed2018-01-05 03:20:25 -0800993 {
Neale Rannsecff1cb2020-04-22 12:14:52 -0400994 if (unformat (line_input, "nitems %d", &nitems))
Florin Corase86a8ed2018-01-05 03:20:25 -0800995 ;
Dave Barachf66f8832020-01-23 08:44:40 -0500996 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -0800997 vl_msg_api_trace_configure (am, which, nitems);
998 vl_msg_api_trace_onoff (am, which, 1 /* on */ );
Dave Barachf66f8832020-01-23 08:44:40 -0500999 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001000 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001001 else if (unformat (line_input, "off"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001002 {
Dave Barachf66f8832020-01-23 08:44:40 -05001003 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001004 vl_msg_api_trace_onoff (am, which, 0);
Dave Barachf66f8832020-01-23 08:44:40 -05001005 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001006 }
Filip Tehlar36217e32021-07-23 08:51:10 +00001007 else if (unformat (line_input, "save-json %s", &filename))
1008 {
1009 if (strstr ((char *) filename, "..") ||
1010 index ((char *) filename, '/'))
1011 {
1012 vlib_cli_output (vm, "illegal characters in filename '%s'",
1013 filename);
1014 goto out;
1015 }
1016
1017 chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1018
1019 vec_free (filename);
1020
1021 if (file_exists (chroot_filename))
1022 {
1023 vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1024 goto out;
1025 }
1026
1027 fp = fopen ((char *) chroot_filename, "w");
1028 if (fp == NULL)
1029 {
1030 vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
1031 goto out;
1032 }
1033 vlib_worker_thread_barrier_sync (vm);
1034 rv = vl_msg_api_trace_save (am, which, fp, 1);
1035 if (rv == -1)
1036 vlib_cli_output (vm, "API Trace data not present\n");
1037 else if (rv < 0)
1038 vlib_cli_output (vm, "failed to save api trace\n");
1039 else
1040 vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
1041 vlib_worker_thread_barrier_release (vm);
1042 fclose (fp);
1043 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001044 else if (unformat (line_input, "save %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001045 {
Florin Corase86a8ed2018-01-05 03:20:25 -08001046 if (strstr ((char *) filename, "..")
1047 || index ((char *) filename, '/'))
1048 {
1049 vlib_cli_output (vm, "illegal characters in filename '%s'",
1050 filename);
Dave Barachf35a0722019-06-12 16:50:38 -04001051 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -08001052 }
1053
1054 chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1055
1056 vec_free (filename);
1057
Filip Tehlar36217e32021-07-23 08:51:10 +00001058 if (file_exists (chroot_filename))
1059 {
1060 vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1061 goto out;
1062 }
1063
Florin Corase86a8ed2018-01-05 03:20:25 -08001064 fp = fopen ((char *) chroot_filename, "w");
1065 if (fp == NULL)
1066 {
1067 vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
Dave Barachf35a0722019-06-12 16:50:38 -04001068 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -08001069 }
Dave Barachf66f8832020-01-23 08:44:40 -05001070 vlib_worker_thread_barrier_sync (vm);
Filip Tehlar36217e32021-07-23 08:51:10 +00001071 rv = vl_msg_api_trace_save (am, which, fp, 0);
Dave Barachf66f8832020-01-23 08:44:40 -05001072 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001073 fclose (fp);
1074 if (rv == -1)
1075 vlib_cli_output (vm, "API Trace data not present\n");
1076 else if (rv == -2)
1077 vlib_cli_output (vm, "File for writing is closed\n");
1078 else if (rv == -10)
1079 vlib_cli_output (vm, "Error while writing header to file\n");
1080 else if (rv == -11)
1081 vlib_cli_output (vm, "Error while writing trace to file\n");
1082 else if (rv == -12)
1083 vlib_cli_output (vm,
1084 "Error while writing end of buffer trace to file\n");
1085 else if (rv == -13)
1086 vlib_cli_output (vm,
1087 "Error while writing start of buffer trace to file\n");
1088 else if (rv < 0)
Andrey "Zed" Zaikin701625b2018-04-18 17:07:07 +03001089 vlib_cli_output (vm, "Unknown error while saving: %d", rv);
Florin Corase86a8ed2018-01-05 03:20:25 -08001090 else
1091 vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
Dave Barachf35a0722019-06-12 16:50:38 -04001092 goto out;
Florin Corase86a8ed2018-01-05 03:20:25 -08001093 }
Filip Tehlar36217e32021-07-23 08:51:10 +00001094 else if (unformat (line_input, "tojson %s", &filename))
1095 {
1096 vl_msg_api_process_file (vm, filename, first, last, DUMP_JSON);
1097 }
1098 else if (unformat (line_input, "dump-file-json %s", &filename))
1099 {
1100 vl_msg_dump_file_json (vm, filename);
1101 }
1102 else if (unformat (line_input, "dump-file %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001103 {
1104 vl_msg_api_process_file (vm, filename, first, last, DUMP);
1105 }
Filip Tehlar36217e32021-07-23 08:51:10 +00001106 else if (unformat (line_input, "dump-json"))
1107 {
1108 vl_msg_api_dump_trace (vm, which, 1);
1109 }
1110 else if (unformat (line_input, "dump"))
1111 {
1112 vl_msg_api_dump_trace (vm, which, 0);
1113 }
1114 else if (unformat (line_input, "replay-json %s", &filename))
1115 {
1116 vl_msg_replay_json (vm, filename);
1117 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001118 else if (unformat (line_input, "replay %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001119 {
1120 vl_msg_api_process_file (vm, filename, first, last, REPLAY);
1121 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001122 else if (unformat (line_input, "initializers %s", &filename))
Florin Corase86a8ed2018-01-05 03:20:25 -08001123 {
1124 vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
1125 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001126 else if (unformat (line_input, "tx"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001127 {
1128 which = VL_API_TRACE_TX;
1129 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001130 else if (unformat (line_input, "first %d", &first))
Florin Corase86a8ed2018-01-05 03:20:25 -08001131 {
1132 ;
1133 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001134 else if (unformat (line_input, "last %d", &last))
Florin Corase86a8ed2018-01-05 03:20:25 -08001135 {
1136 ;
1137 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001138 else if (unformat (line_input, "status"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001139 {
1140 vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
1141 am, which);
1142 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001143 else if (unformat (line_input, "free"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001144 {
Dave Barachf66f8832020-01-23 08:44:40 -05001145 vlib_worker_thread_barrier_sync (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001146 vl_msg_api_trace_onoff (am, which, 0);
1147 vl_msg_api_trace_free (am, which);
Dave Barachf66f8832020-01-23 08:44:40 -05001148 vlib_worker_thread_barrier_release (vm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001149 }
Neale Rannsecff1cb2020-04-22 12:14:52 -04001150 else if (unformat (line_input, "post-mortem-on"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001151 vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
Neale Rannsecff1cb2020-04-22 12:14:52 -04001152 else if (unformat (line_input, "post-mortem-off"))
Florin Corase86a8ed2018-01-05 03:20:25 -08001153 vl_msg_api_post_mortem_dump_enable_disable (0 /* enable */ );
1154 else
1155 return clib_error_return (0, "unknown input `%U'",
1156 format_unformat_error, input);
1157 }
Dave Barachf35a0722019-06-12 16:50:38 -04001158out:
1159 vec_free (filename);
1160 vec_free (chroot_filename);
Neale Rannsecff1cb2020-04-22 12:14:52 -04001161 unformat_free (line_input);
Florin Corase86a8ed2018-01-05 03:20:25 -08001162 return 0;
1163}
1164
1165/*?
1166 * Display, replay, or save a binary API trace
1167?*/
1168
1169/* *INDENT-OFF* */
Filip Tehlarc863a912021-06-15 10:29:54 +00001170VLIB_CLI_COMMAND (api_trace_command, static) = {
Florin Corase86a8ed2018-01-05 03:20:25 -08001171 .path = "api trace",
Filip Tehlar36217e32021-07-23 08:51:10 +00001172 .short_help = "api trace [tx][on|off][first <n>][last <n>][status][free]"
1173 "[post-mortem-on][dump|dump-file|dump-json|save|tojson|save-"
1174 "json|replay <file>|replay-json <file>][nitems <n>]"
1175 "[initializers <file>]",
Florin Corase86a8ed2018-01-05 03:20:25 -08001176 .function = api_trace_command_fn,
Dave Barachf66f8832020-01-23 08:44:40 -05001177 .is_mp_safe = 1,
Florin Corase86a8ed2018-01-05 03:20:25 -08001178};
1179/* *INDENT-ON* */
1180
1181static clib_error_t *
Florin Corase86a8ed2018-01-05 03:20:25 -08001182api_trace_config_fn (vlib_main_t * vm, unformat_input_t * input)
1183{
1184 u32 nitems = 256 << 10;
1185 vl_api_trace_which_t which = VL_API_TRACE_RX;
Dave Barach39d69112019-11-27 11:42:13 -05001186 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001187
1188 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1189 {
1190 if (unformat (input, "on") || unformat (input, "enable"))
1191 {
1192 if (unformat (input, "nitems %d", &nitems))
1193 ;
1194 vl_msg_api_trace_configure (am, which, nitems);
1195 vl_msg_api_trace_onoff (am, which, 1 /* on */ );
1196 vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1197 }
1198 else if (unformat (input, "save-api-table %s",
1199 &am->save_msg_table_filename))
1200 ;
1201 else
1202 return clib_error_return (0, "unknown input `%U'",
1203 format_unformat_error, input);
1204 }
1205 return 0;
1206}
1207
1208/*?
1209 * This module has three configuration parameters:
1210 * "on" or "enable" - enables binary api tracing
1211 * "nitems <nnn>" - sets the size of the circular buffer to <nnn>
1212 * "save-api-table <filename>" - dumps the API message table to /tmp/<filename>
1213?*/
1214VLIB_CONFIG_FUNCTION (api_trace_config_fn, "api-trace");
1215
1216static clib_error_t *
1217api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
1218{
Dave Barach39d69112019-11-27 11:42:13 -05001219 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001220 u32 nitems;
1221
1222 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1223 {
1224 if (unformat (input, "length %d", &nitems) ||
1225 (unformat (input, "len %d", &nitems)))
1226 {
1227 if (nitems >= 1024)
1228 am->vlib_input_queue_length = nitems;
1229 else
1230 clib_warning ("vlib input queue length %d too small, ignored",
1231 nitems);
1232 }
1233 else
1234 return clib_error_return (0, "unknown input `%U'",
1235 format_unformat_error, input);
1236 }
1237 return 0;
1238}
1239
1240VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
1241
1242static u8 *
1243extract_name (u8 * s)
1244{
1245 u8 *rv;
1246
1247 rv = vec_dup (s);
1248
1249 while (vec_len (rv) && rv[vec_len (rv)] != '_')
Andrew Yourtchenko105cb1e2022-04-15 14:23:41 +00001250 vec_dec_len (rv, 1);
Florin Corase86a8ed2018-01-05 03:20:25 -08001251
1252 rv[vec_len (rv)] = 0;
1253
1254 return rv;
1255}
1256
1257static u8 *
1258extract_crc (u8 * s)
1259{
1260 int i;
1261 u8 *rv;
1262
1263 rv = vec_dup (s);
1264
1265 for (i = vec_len (rv) - 1; i >= 0; i--)
1266 {
1267 if (rv[i] == '_')
1268 {
1269 vec_delete (rv, i + 1, 0);
1270 break;
1271 }
1272 }
1273 return rv;
1274}
1275
1276typedef struct
1277{
1278 u8 *name_and_crc;
1279 u8 *name;
1280 u8 *crc;
1281 u32 msg_index;
1282 int which;
1283} msg_table_unserialize_t;
1284
1285static int
1286table_id_cmp (void *a1, void *a2)
1287{
1288 msg_table_unserialize_t *n1 = a1;
1289 msg_table_unserialize_t *n2 = a2;
1290
1291 return (n1->msg_index - n2->msg_index);
1292}
1293
1294static int
1295table_name_and_crc_cmp (void *a1, void *a2)
1296{
1297 msg_table_unserialize_t *n1 = a1;
1298 msg_table_unserialize_t *n2 = a2;
1299
1300 return strcmp ((char *) n1->name_and_crc, (char *) n2->name_and_crc);
1301}
1302
1303static clib_error_t *
1304dump_api_table_file_command_fn (vlib_main_t * vm,
1305 unformat_input_t * input,
1306 vlib_cli_command_t * cmd)
1307{
1308 u8 *filename = 0;
Dave Barach39d69112019-11-27 11:42:13 -05001309 api_main_t *am = vlibapi_get_main ();
Florin Corase86a8ed2018-01-05 03:20:25 -08001310 serialize_main_t _sm, *sm = &_sm;
1311 clib_error_t *error;
1312 u32 nmsgs;
1313 u32 msg_index;
1314 u8 *name_and_crc;
1315 int compare_current = 0;
1316 int numeric_sort = 0;
1317 msg_table_unserialize_t *table = 0, *item;
1318 u32 i;
1319 u32 ndifferences = 0;
1320
1321 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1322 {
1323 if (unformat (input, "file %s", &filename))
1324 ;
1325 else if (unformat (input, "compare-current")
1326 || unformat (input, "compare"))
1327 compare_current = 1;
1328 else if (unformat (input, "numeric"))
1329 numeric_sort = 1;
1330 else
1331 return clib_error_return (0, "unknown input `%U'",
1332 format_unformat_error, input);
1333 }
1334
1335 if (numeric_sort && compare_current)
1336 return clib_error_return
1337 (0, "Comparison and numeric sorting are incompatible");
1338
1339 if (filename == 0)
1340 return clib_error_return (0, "File not specified");
1341
1342 /* Load the serialized message table from the table dump */
1343
1344 error = unserialize_open_clib_file (sm, (char *) filename);
1345
1346 if (error)
1347 return error;
1348
1349 unserialize_integer (sm, &nmsgs, sizeof (u32));
1350
1351 for (i = 0; i < nmsgs; i++)
1352 {
1353 msg_index = unserialize_likely_small_unsigned_integer (sm);
1354 unserialize_cstring (sm, (char **) &name_and_crc);
1355 vec_add2 (table, item, 1);
1356 item->msg_index = msg_index;
1357 item->name_and_crc = name_and_crc;
1358 item->name = extract_name (name_and_crc);
1359 item->crc = extract_crc (name_and_crc);
1360 item->which = 0; /* file */
1361 }
Ole Troanedfe2c02019-07-30 15:38:13 +02001362 unserialize_close (sm);
Florin Corase86a8ed2018-01-05 03:20:25 -08001363
1364 /* Compare with the current image? */
1365 if (compare_current)
1366 {
1367 /* Append the current message table */
1368 u8 *tblv = vl_api_serialize_message_table (am, 0);
1369
1370 serialize_open_vector (sm, tblv);
1371 unserialize_integer (sm, &nmsgs, sizeof (u32));
1372
1373 for (i = 0; i < nmsgs; i++)
1374 {
1375 msg_index = unserialize_likely_small_unsigned_integer (sm);
1376 unserialize_cstring (sm, (char **) &name_and_crc);
1377
1378 vec_add2 (table, item, 1);
1379 item->msg_index = msg_index;
1380 item->name_and_crc = name_and_crc;
1381 item->name = extract_name (name_and_crc);
1382 item->crc = extract_crc (name_and_crc);
1383 item->which = 1; /* current_image */
1384 }
1385 vec_free (tblv);
1386 }
1387
1388 /* Sort the table. */
1389 if (numeric_sort)
1390 vec_sort_with_function (table, table_id_cmp);
1391 else
1392 vec_sort_with_function (table, table_name_and_crc_cmp);
1393
1394 if (compare_current)
1395 {
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001396 u8 *dashes = 0;
Florin Corase86a8ed2018-01-05 03:20:25 -08001397 ndifferences = 0;
1398
1399 /*
1400 * In this case, the recovered table will have two entries per
1401 * API message. So, if entries i and i+1 match, the message definitions
1402 * are identical. Otherwise, the crc is different, or a message is
1403 * present in only one of the tables.
1404 */
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001405 vlib_cli_output (vm, "%-60s | %s", "Message Name", "Result");
1406 vec_validate_init_empty (dashes, 60, '-');
1407 vec_terminate_c_string (dashes);
1408 vlib_cli_output (vm, "%60s-|-%s", dashes, "-----------------");
1409 vec_free (dashes);
Florin Corase86a8ed2018-01-05 03:20:25 -08001410 for (i = 0; i < vec_len (table);)
1411 {
1412 /* Last message lonely? */
1413 if (i == vec_len (table) - 1)
1414 {
1415 ndifferences++;
1416 goto last_unique;
1417 }
1418
1419 /* Identical pair? */
1420 if (!strncmp
1421 ((char *) table[i].name_and_crc,
1422 (char *) table[i + 1].name_and_crc,
1423 vec_len (table[i].name_and_crc)))
1424 {
1425 i += 2;
1426 continue;
1427 }
1428
1429 ndifferences++;
1430
1431 /* Only in one of two tables? */
Andrew Yourtchenkoae605b82019-01-21 16:39:33 +01001432 if (i + 1 == vec_len (table)
1433 || strcmp ((char *) table[i].name, (char *) table[i + 1].name))
Florin Corase86a8ed2018-01-05 03:20:25 -08001434 {
1435 last_unique:
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001436 vlib_cli_output (vm, "%-60s | only in %s",
Florin Corase86a8ed2018-01-05 03:20:25 -08001437 table[i].name, table[i].which ?
1438 "image" : "file");
1439 i++;
1440 continue;
1441 }
1442 /* In both tables, but with different signatures */
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001443 vlib_cli_output (vm, "%-60s | definition changed", table[i].name);
Florin Corase86a8ed2018-01-05 03:20:25 -08001444 i += 2;
1445 }
1446 if (ndifferences == 0)
1447 vlib_cli_output (vm, "No api message signature differences found.");
1448 else
Andrew Yourtchenko05f7ca12019-01-21 16:28:48 +01001449 vlib_cli_output (vm, "\nFound %u api message signature differences",
Florin Corase86a8ed2018-01-05 03:20:25 -08001450 ndifferences);
1451 goto cleanup;
1452 }
1453
1454 /* Dump the table, sorted as shown above */
1455 vlib_cli_output (vm, "%=60s %=8s %=10s", "Message name", "MsgID", "CRC");
1456
1457 for (i = 0; i < vec_len (table); i++)
1458 {
1459 item = table + i;
1460 vlib_cli_output (vm, "%-60s %8u %10s", item->name,
1461 item->msg_index, item->crc);
1462 }
1463
1464cleanup:
1465 for (i = 0; i < vec_len (table); i++)
1466 {
1467 vec_free (table[i].name_and_crc);
1468 vec_free (table[i].name);
1469 vec_free (table[i].crc);
1470 }
1471
1472 vec_free (table);
1473
1474 return 0;
1475}
1476
1477/*?
1478 * Displays a serialized API message decode table, sorted by message name
1479 *
1480 * @cliexpar
1481 * @cliexstart{show api dump file <filename>}
1482 * Message name MsgID CRC
1483 * accept_session 407 8e2a127e
1484 * accept_session_reply 408 67d8c22a
1485 * add_node_next 549 e4202993
1486 * add_node_next_reply 550 e89d6eed
1487 * etc.
1488 * @cliexend
1489?*/
1490
1491/*?
1492 * Compares a serialized API message decode table with the current image
1493 *
1494 * @cliexpar
1495 * @cliexstart{show api dump file <filename> compare}
1496 * ip_add_del_route definition changed
1497 * ip_table_add_del definition changed
1498 * l2_macs_event only in image
1499 * vnet_ip4_fib_counters only in file
1500 * vnet_ip4_nbr_counters only in file
1501 * @cliexend
1502?*/
1503
1504/*?
1505 * Display a serialized API message decode table, compare a saved
1506 * decode table with the current image, to establish API differences.
1507 *
1508?*/
1509/* *INDENT-OFF* */
1510VLIB_CLI_COMMAND (dump_api_table_file, static) =
1511{
1512 .path = "show api dump",
1513 .short_help = "show api dump file <filename> [numeric | compare-current]",
1514 .function = dump_api_table_file_command_fn,
1515};
1516
1517/* *INDENT-ON* */
1518/*
1519 * fd.io coding-style-patch-verification: ON
1520 *
1521 * Local Variables:
1522 * eval: (c-set-style "gnu")
1523 * End:
1524 */