732ced7634c05f2abec8ce8f15092d8df2a7713e
[vpp.git] / src / vlibmemory / vlib_api_cli.c
1 /*
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
26 static clib_error_t *
27 vl_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* */
65 VLIB_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
73 static clib_error_t *
74 vl_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* */
89 VLIB_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
97 static clib_error_t *
98 vl_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;
104   api_main_t *am = vlibapi_get_main ();
105   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* */
114   pool_foreach (regpp, am->vl_clients)
115    {
116     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       }
137   }
138   /* *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);
154 socket_clients:
155   vl_sock_api_dump_clients (vm, am);
156
157   return 0;
158 }
159
160 static clib_error_t *
161 vl_api_status_command (vlib_main_t * vm,
162                        unformat_input_t * input, vlib_cli_command_t * cli_cmd)
163 {
164   api_main_t *am = vlibapi_get_main ();
165
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* */
195 VLIB_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* */
206 VLIB_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* */
218 VLIB_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
226 static clib_error_t *
227 vl_api_message_table_command (vlib_main_t * vm,
228                               unformat_input_t * input,
229                               vlib_cli_command_t * cli_cmd)
230 {
231   api_main_t *am = vlibapi_get_main ();
232   int i;
233   int verbose = 0;
234
235   if (unformat (input, "verbose"))
236     verbose = 1;
237
238
239   if (verbose == 0)
240     vlib_cli_output (vm, "%-4s %s", "ID", "Name");
241   else
242     vlib_cli_output (vm, "%-4s %-40s %6s %7s", "ID", "Name", "Bounce",
243                      "MP-safe");
244
245   for (i = 1; i < vec_len (am->msg_names); i++)
246     {
247       if (verbose == 0)
248         {
249           vlib_cli_output (vm, "%-4d %s", i,
250                            am->msg_names[i] ? am->msg_names[i] :
251                            "  [no handler]");
252         }
253       else
254         {
255           vlib_cli_output (vm, "%-4d %-40s %6d %7d", i,
256                            am->msg_names[i] ? am->msg_names[i] :
257                            "  [no handler]", am->message_bounce[i],
258                            am->is_mp_safe[i]);
259         }
260     }
261
262   return 0;
263 }
264
265 /*?
266  * Display the current api message decode tables
267 ?*/
268 /* *INDENT-OFF* */
269 VLIB_CLI_COMMAND (cli_show_api_message_table_command, static) =
270 {
271   .path = "show api message-table",
272   .short_help = "Message Table",
273   .function = vl_api_message_table_command,
274 };
275 /* *INDENT-ON* */
276
277 static int
278 range_compare (vl_api_msg_range_t * a0, vl_api_msg_range_t * a1)
279 {
280   int len0, len1, clen;
281
282   len0 = vec_len (a0->name);
283   len1 = vec_len (a1->name);
284   clen = len0 < len1 ? len0 : len1;
285   return (strncmp ((char *) a0->name, (char *) a1->name, clen));
286 }
287
288 static u8 *
289 format_api_msg_range (u8 * s, va_list * args)
290 {
291   vl_api_msg_range_t *rp = va_arg (*args, vl_api_msg_range_t *);
292
293   if (rp == 0)
294     s = format (s, "%-50s%9s%9s", "Name", "First-ID", "Last-ID");
295   else
296     s = format (s, "%-50s%9d%9d", rp->name, rp->first_msg_id,
297                 rp->last_msg_id);
298
299   return s;
300 }
301
302 static clib_error_t *
303 vl_api_show_plugin_command (vlib_main_t * vm,
304                             unformat_input_t * input,
305                             vlib_cli_command_t * cli_cmd)
306 {
307   api_main_t *am = vlibapi_get_main ();
308   vl_api_msg_range_t *rp = 0;
309   int i;
310
311   if (vec_len (am->msg_ranges) == 0)
312     {
313       vlib_cli_output (vm, "No plugin API message ranges configured...");
314       return 0;
315     }
316
317   rp = vec_dup (am->msg_ranges);
318
319   vec_sort_with_function (rp, range_compare);
320
321   vlib_cli_output (vm, "Plugin API message ID ranges...\n");
322   vlib_cli_output (vm, "%U", format_api_msg_range, 0 /* header */ );
323
324   for (i = 0; i < vec_len (rp); i++)
325     vlib_cli_output (vm, "%U", format_api_msg_range, rp + i);
326
327   vec_free (rp);
328
329   return 0;
330 }
331
332 /*?
333  * Display the plugin binary API message range table
334 ?*/
335 /* *INDENT-OFF* */
336 VLIB_CLI_COMMAND (cli_show_api_plugin_command, static) =
337 {
338   .path = "show api plugin",
339   .short_help = "show api plugin",
340   .function = vl_api_show_plugin_command,
341 };
342 /* *INDENT-ON* */
343
344 typedef enum
345 {
346   DUMP,
347   DUMP_JSON,
348   REPLAY,
349   INITIALIZERS,
350 } vl_api_replay_t;
351
352 u8 *
353 format_vl_msg_api_trace_status (u8 * s, va_list * args)
354 {
355   api_main_t *am = va_arg (*args, api_main_t *);
356   vl_api_trace_which_t which = va_arg (*args, vl_api_trace_which_t);
357   vl_api_trace_t *tp;
358   char *trace_name;
359
360   switch (which)
361     {
362     case VL_API_TRACE_TX:
363       tp = am->tx_trace;
364       trace_name = "TX trace";
365       break;
366
367     case VL_API_TRACE_RX:
368       tp = am->rx_trace;
369       trace_name = "RX trace";
370       break;
371
372     default:
373       abort ();
374     }
375
376   if (tp == 0)
377     {
378       s = format (s, "%s: not yet configured.\n", trace_name);
379       return s;
380     }
381
382   s = format (s, "%s: used %d of %d items, %s enabled, %s wrapped\n",
383               trace_name, vec_len (tp->traces), tp->nitems,
384               tp->enabled ? "is" : "is not", tp->wrapped ? "has" : "has not");
385   return s;
386 }
387
388 static void
389 vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
390                          u32 first_index, u32 last_index,
391                          vl_api_replay_t which)
392 {
393   vl_api_trace_file_header_t *hp;
394   int i, fd;
395   u16 *msgid_vec = 0;
396   struct stat statb;
397   size_t file_size;
398   u8 *msg;
399   api_main_t *am = vlibapi_get_main ();
400   u8 *tmpbuf = 0;
401   u32 nitems, nitems_msgtbl;
402
403   fd = open ((char *) filename, O_RDONLY);
404
405   if (fd < 0)
406     {
407       vlib_cli_output (vm, "Couldn't open %s\n", filename);
408       return;
409     }
410
411   if (fstat (fd, &statb) < 0)
412     {
413       vlib_cli_output (vm, "Couldn't stat %s\n", filename);
414       close (fd);
415       return;
416     }
417
418   if (!(statb.st_mode & S_IFREG) || (statb.st_size < sizeof (*hp)))
419     {
420       vlib_cli_output (vm, "File not plausible: %s\n", filename);
421       close (fd);
422       return;
423     }
424
425   file_size = statb.st_size;
426   file_size = (file_size + 4095) & ~(4095);
427
428   hp = mmap (0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
429
430   if (hp == (vl_api_trace_file_header_t *) MAP_FAILED)
431     {
432       vlib_cli_output (vm, "mmap failed: %s\n", filename);
433       close (fd);
434       return;
435     }
436   close (fd);
437
438   CLIB_MEM_UNPOISON (hp, file_size);
439
440   nitems = ntohl (hp->nitems);
441
442   if (last_index == (u32) ~ 0)
443     {
444       last_index = nitems - 1;
445     }
446
447   if (first_index >= nitems || last_index >= nitems)
448     {
449       vlib_cli_output (vm, "Range (%d, %d) outside file range (0, %d)\n",
450                        first_index, last_index, nitems - 1);
451       munmap (hp, file_size);
452       return;
453     }
454   if (hp->wrapped)
455     vlib_cli_output (vm,
456                      "Note: wrapped/incomplete trace, results may vary\n");
457
458   size_t file_size_left = file_size;
459
460 #define assert_size(size_left, s)                                             \
461   do                                                                          \
462     {                                                                         \
463       if ((s) >= size_left)                                                   \
464         {                                                                     \
465           vlib_cli_output (vm, "corrupted file");                             \
466           munmap (hp, file_size);                                             \
467           vec_free (msgid_vec);                                               \
468           return;                                                             \
469         }                                                                     \
470       size_left -= s;                                                         \
471     }                                                                         \
472   while (0);
473
474   assert_size (file_size_left, sizeof (hp[0]));
475   msg = (u8 *) (hp + 1);
476
477   serialize_main_t _sm, *sm = &_sm;
478   u32 msgtbl_size = ntohl (hp->msgtbl_size);
479   u8 *name_and_crc;
480
481   assert_size (file_size_left, msgtbl_size);
482
483   unserialize_open_data (sm, msg, msgtbl_size);
484   unserialize_integer (sm, &nitems_msgtbl, sizeof (u32));
485
486   for (i = 0; i < nitems_msgtbl; i++)
487     {
488       u16 msg_index = unserialize_likely_small_unsigned_integer (sm);
489       unserialize_cstring (sm, (char **) &name_and_crc);
490       u32 msg_index2 = vl_msg_api_get_msg_index (name_and_crc);
491       ASSERT (~0 == msg_index2 || msg_index2 <= 65535);
492       if (~0 == msg_index2)
493         vlib_cli_output (vm, "warning: can't find msg index for id %d\n",
494                          msg_index);
495       vec_validate (msgid_vec, msg_index);
496       msgid_vec[msg_index] = msg_index2;
497     }
498
499   msg += msgtbl_size;
500
501   for (i = 0; i < first_index; i++)
502     {
503       int size;
504       u16 msg_id;
505
506       assert_size (file_size_left, sizeof (u32));
507       size = clib_host_to_net_u32 (*(u32 *) msg);
508       msg += sizeof (u32);
509
510       assert_size (file_size_left, clib_max (size, sizeof (u16)));
511       msg_id = ntohs (*((u16 *) msg));
512       if (msg_id >= vec_len (msgid_vec) ||
513           msgid_vec[msg_id] >= vec_len (am->api_trace_cfg))
514         vlib_cli_output (vm, "warning: unknown msg id %d for msg number %d\n",
515                          msg_id, i);
516
517       msg += size;
518     }
519
520   if (which == REPLAY)
521     am->replay_in_progress = 1;
522
523   for (; i <= last_index; i++)
524     {
525       trace_cfg_t *cfgp;
526       u16 msg_id;
527       int size;
528
529       if (which == DUMP)
530         vlib_cli_output (vm, "---------- trace %d -----------\n", i);
531
532       assert_size (file_size_left, sizeof (u32));
533       size = clib_host_to_net_u32 (*(u32 *) msg);
534       msg += sizeof (u32);
535
536       assert_size (file_size_left, clib_max (size, sizeof (u16)));
537       msg_id = ntohs (*((u16 *) msg));
538
539       if (msg_id >= vec_len (msgid_vec) ||
540           msgid_vec[msg_id] >= vec_len (am->api_trace_cfg))
541         {
542           vlib_cli_output (
543             vm, "warning: unknown msg id %d for msg number %d, skipping\n",
544             msg_id, i);
545           msg += size;
546           continue;
547         }
548
549       msg_id = msgid_vec[msg_id];
550       cfgp = am->api_trace_cfg + msg_id;
551
552       /* Copy the buffer (from the read-only mmap'ed file) */
553       vec_validate (tmpbuf, size - 1 + sizeof (uword));
554       clib_memcpy (tmpbuf + sizeof (uword), msg, size);
555       clib_memset (tmpbuf, 0xf, sizeof (uword));
556
557       /*
558        * Endian swap if needed. All msg data is supposed to be in
559        * network byte order.
560        */
561       if (((which == DUMP || which == DUMP_JSON) &&
562            clib_arch_is_little_endian))
563         {
564           void (*endian_fp) (void *);
565           if (msg_id >= vec_len (am->msg_endian_handlers)
566               || (am->msg_endian_handlers[msg_id] == 0))
567             {
568               vlib_cli_output (vm, "Ugh: msg id %d no endian swap\n", msg_id);
569               munmap (hp, file_size);
570               vec_free (tmpbuf);
571               am->replay_in_progress = 0;
572               return;
573             }
574           endian_fp = am->msg_endian_handlers[msg_id];
575           (*endian_fp) (tmpbuf + sizeof (uword));
576         }
577
578       /* msg_id always in network byte order */
579       if (clib_arch_is_little_endian)
580         {
581           u16 *msg_idp = (u16 *) (tmpbuf + sizeof (uword));
582           *msg_idp = msg_id;
583         }
584
585       switch (which)
586         {
587         case DUMP_JSON:
588           if (msg_id < vec_len (am->msg_print_json_handlers) &&
589               am->msg_print_json_handlers[msg_id])
590             {
591               u8 *(*print_fp) (void *, void *);
592
593               print_fp = (void *) am->msg_print_json_handlers[msg_id];
594               (*print_fp) (tmpbuf + sizeof (uword), vm);
595             }
596           else
597             {
598               vlib_cli_output (vm, "Skipping msg id %d: no JSON print fcn\n",
599                                msg_id);
600               break;
601             }
602           break;
603
604         case DUMP:
605           if (msg_id < vec_len (am->msg_print_handlers) &&
606               am->msg_print_handlers[msg_id])
607             {
608               u8 *(*print_fp) (void *, void *);
609
610               print_fp = (void *) am->msg_print_handlers[msg_id];
611               (*print_fp) (tmpbuf + sizeof (uword), vm);
612             }
613           else
614             {
615               vlib_cli_output (vm, "Skipping msg id %d: no print fcn\n",
616                                msg_id);
617               break;
618             }
619           break;
620
621         case INITIALIZERS:
622           if (msg_id < vec_len (am->msg_print_handlers) &&
623               am->msg_print_handlers[msg_id])
624             {
625               u8 *s;
626               int j;
627               u8 *(*print_fp) (void *, void *);
628
629               print_fp = (void *) am->msg_print_handlers[msg_id];
630
631               vlib_cli_output (vm, "/*");
632
633               (*print_fp) (tmpbuf + sizeof (uword), vm);
634               vlib_cli_output (vm, "*/\n");
635
636               s = format (0, "static u8 * vl_api_%s_%d[%d] = {",
637                           am->msg_names[msg_id], i,
638                           am->api_trace_cfg[msg_id].size);
639
640               for (j = 0; j < am->api_trace_cfg[msg_id].size; j++)
641                 {
642                   if ((j & 7) == 0)
643                     s = format (s, "\n    ");
644                   s = format (s, "0x%02x,", tmpbuf[sizeof (uword) + j]);
645                 }
646               s = format (s, "\n};\n%c", 0);
647               vlib_cli_output (vm, (char *) s);
648               vec_free (s);
649             }
650           break;
651
652         case REPLAY:
653           if (msg_id < vec_len (am->msg_print_handlers) &&
654               am->msg_print_handlers[msg_id] && cfgp->replay_enable)
655             {
656               void (*handler) (void *, vlib_main_t *);
657
658               handler = (void *) am->msg_handlers[msg_id];
659
660               if (!am->is_mp_safe[msg_id])
661                 vl_msg_api_barrier_sync ();
662               (*handler) (tmpbuf + sizeof (uword), vm);
663               if (!am->is_mp_safe[msg_id])
664                 vl_msg_api_barrier_release ();
665             }
666           else
667             {
668               if (cfgp->replay_enable)
669                 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
670                                  msg_id);
671               break;
672             }
673           break;
674         }
675
676       vec_set_len (tmpbuf, 0);
677       msg += size;
678     }
679
680   munmap (hp, file_size);
681   vec_free (tmpbuf);
682   vec_free (msgid_vec);
683   am->replay_in_progress = 0;
684 }
685
686 static int
687 file_exists (u8 *fname)
688 {
689   FILE *fp = 0;
690   fp = fopen ((char *) fname, "r");
691   if (fp)
692     {
693       fclose (fp);
694       return 1;
695     }
696   return 0;
697 }
698
699 typedef struct
700 {
701   vlib_main_t *vm;
702   u8 is_json;
703 } vl_msg_print_args;
704
705 static int
706 vl_msg_print_trace (u8 *msg, void *ctx)
707 {
708   vl_msg_print_args *a = ctx;
709   api_main_t *am = vlibapi_get_main ();
710   u16 msg_id = ntohs (*((u16 *) msg));
711   void (*print_fp) (void *, void *);
712   void (**handlers) (void *, void *);
713   u8 is_json = a->is_json;
714   u8 *tmpbuf = 0;
715
716   if (clib_arch_is_little_endian)
717     {
718       u32 msg_length = vec_len (msg);
719       vec_validate (tmpbuf, msg_length - 1);
720       clib_memcpy_fast (tmpbuf, msg, msg_length);
721       msg = tmpbuf;
722
723       void (*endian_fp) (void *);
724       endian_fp = am->msg_endian_handlers[msg_id];
725       (*endian_fp) (tmpbuf);
726     }
727
728   if (is_json)
729     handlers = am->msg_print_json_handlers;
730   else
731     handlers = am->msg_print_handlers;
732
733   if (msg_id < vec_len (handlers) && handlers[msg_id])
734     {
735       print_fp = (void *) handlers[msg_id];
736       (*print_fp) (msg, a->vm);
737     }
738   else
739     {
740       vlib_cli_output (a->vm, "Skipping msg id %d: no print fcn\n", msg_id);
741     }
742
743   vec_free (tmpbuf);
744   return 0;
745 }
746
747 static int
748 vl_msg_api_dump_trace (vlib_main_t *vm, vl_api_trace_which_t which, u8 is_json)
749 {
750   api_main_t *am = vlibapi_get_main ();
751   vl_api_trace_t *tp;
752
753   switch (which)
754     {
755     case VL_API_TRACE_TX:
756       tp = am->tx_trace;
757       break;
758     case VL_API_TRACE_RX:
759       tp = am->rx_trace;
760       break;
761     default:
762       return -1;
763     }
764
765   if (tp == 0 || tp->nitems == 0 || vec_len (tp->traces) == 0)
766     return -1;
767
768   vl_msg_print_args args;
769   clib_memset (&args, 0, sizeof (args));
770   args.is_json = is_json;
771   args.vm = vm;
772   vl_msg_traverse_trace (tp, vl_msg_print_trace, &args);
773
774   return 0;
775 }
776
777 static char *
778 vl_msg_read_file (FILE *f)
779 {
780   const size_t bufsize = 1024;
781   char *buf[bufsize], *v = 0;
782   size_t n;
783
784   while ((n = fread (buf, 1, bufsize, f)))
785     vec_add (v, buf, n);
786
787   /* most callers expect a NULL-terminated C-string */
788   if (v)
789     vec_add1 (v, 0);
790
791   return v;
792 }
793
794 static u16
795 vl_msg_find_id_by_name_and_crc (vlib_main_t *vm, api_main_t *am, char *name)
796 {
797   uword *p;
798   p = hash_get_mem (am->msg_index_by_name_and_crc, name);
799   if (!p)
800     return (u16) ~0;
801
802   return p[0];
803 }
804
805 static u16
806 vl_msg_find_id_by_name (vlib_main_t *vm, api_main_t *am, char *name)
807 {
808   uword *p;
809
810   if (!am->msg_id_by_name)
811     {
812       vlib_cli_output (vm, "message id table not yet initialized!\n");
813       return (u16) ~0;
814     }
815
816   p = hash_get_mem (am->msg_id_by_name, name);
817   if (!p)
818     return (u16) ~0;
819
820   return p[0];
821 }
822
823 static int
824 vl_msg_exec_json_command (vlib_main_t *vm, cJSON *o)
825 {
826   api_main_t *am = vlibapi_get_main ();
827   u16 msg_id;
828   void *(*fromjson) (cJSON *, int *);
829   int len = 0, rv = -1;
830   trace_cfg_t *cfgp;
831   u8 *msg = 0;
832
833   cJSON *msg_id_obj = cJSON_GetObjectItem (o, "_msgname");
834   if (!msg_id_obj)
835     {
836       vlib_cli_output (vm, "Missing '_msgname' element!\n");
837       return rv;
838     }
839   char *name = cJSON_GetStringValue (msg_id_obj);
840
841   cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc");
842   if (!crc_obj)
843     {
844       vlib_cli_output (vm, "Missing '_crc' element!\n");
845       return rv;
846     }
847   char *crc = cJSON_GetStringValue (crc_obj);
848   u8 proc_warning = 0;
849
850   u8 *name_crc = format (0, "%s_%s%c", name, crc, 0);
851   msg_id = vl_msg_find_id_by_name_and_crc (vm, am, (char *) name_crc);
852   if (msg_id == (u16) ~0)
853     {
854       msg_id = vl_msg_find_id_by_name (vm, am, name);
855       if (msg_id == (u16) ~0)
856         {
857           vlib_cli_output (vm, "unknown msg id %d!\n", msg_id);
858           vec_free (name_crc);
859           return rv;
860         }
861       proc_warning = 1;
862     }
863   vec_free (name_crc);
864
865   cfgp = am->api_trace_cfg + msg_id;
866   if (!am->api_trace_cfg)
867     {
868       vlib_cli_output (vm, "msg id %d no trace config\n", msg_id);
869       return rv;
870     }
871
872   if (cfgp->replay_enable)
873     {
874
875       if (proc_warning)
876         vlib_cli_output (vm, "warning: msg %d has different signature\n");
877
878       fromjson = am->msg_fromjson_handlers[msg_id];
879       if (!fromjson)
880         {
881           vlib_cli_output (vm, "missing fromjson convert function! id %d\n",
882                            msg_id);
883           return rv;
884         }
885
886       msg = (u8 *) fromjson (o, &len);
887       if (!msg)
888         {
889           vlib_cli_output (vm, "failed to convert JSON (msg id %d)!\n",
890                            msg_id);
891           return rv;
892         }
893
894       if (clib_arch_is_little_endian)
895         {
896           void (*endian_fp) (void *);
897           endian_fp = am->msg_endian_handlers[msg_id];
898           (*endian_fp) (msg);
899         }
900
901       void (*handler) (void *, vlib_main_t *);
902       handler = (void *) am->msg_handlers[msg_id];
903       if (!handler)
904         {
905           vlib_cli_output (vm, "no handler for msg id %d!\n", msg_id);
906           goto end;
907         }
908
909       if (!am->is_mp_safe[msg_id])
910         vl_msg_api_barrier_sync ();
911       (*handler) (msg, vm);
912       if (!am->is_mp_safe[msg_id])
913         vl_msg_api_barrier_release ();
914     }
915
916   rv = 0;
917 end:
918   if (msg)
919     cJSON_free (msg);
920   return rv;
921 }
922
923 static void
924 vl_msg_replay_json (vlib_main_t *vm, u8 *filename)
925 {
926   api_main_t *am = vlibapi_get_main ();
927   cJSON *o = 0;
928   int rv = 0;
929   FILE *f = fopen ((char *) filename, "r");
930
931   if (!f)
932     {
933       vlib_cli_output (vm, "failed to open %s!\n", filename);
934       return;
935     }
936
937   char *buf = vl_msg_read_file (f);
938   fclose (f);
939
940   o = cJSON_Parse (buf);
941   vec_free (buf);
942   if (!o)
943     {
944       vlib_cli_output (vm, "%s: Failed parsing JSON input: %s\n", filename,
945                        cJSON_GetErrorPtr ());
946       return;
947     }
948
949   if (cJSON_IsArray (o))
950     {
951       am->replay_in_progress = 1;
952       size_t size = cJSON_GetArraySize (o);
953       for (int i = 0; i < size; i++)
954         {
955           rv = vl_msg_exec_json_command (vm, cJSON_GetArrayItem (o, i));
956           if (rv < 0)
957             {
958               am->replay_in_progress = 0;
959               break;
960             }
961         }
962     }
963   else
964     {
965       rv = vl_msg_exec_json_command (vm, o);
966     }
967
968   if (rv < 0)
969     vlib_cli_output (vm, "error during replaying API trace");
970
971   cJSON_Delete (o);
972 }
973
974 static void
975 vl_msg_dump_file_json (vlib_main_t *vm, u8 *filename)
976 {
977   FILE *f = fopen ((char *) filename, "r");
978   char *buf;
979
980   if (!f)
981     {
982       vlib_cli_output (vm, "failed to open %s!\n", filename);
983       return;
984     }
985
986   buf = vl_msg_read_file (f);
987   fclose (f);
988
989   if (!buf)
990     {
991       vlib_cli_output (vm, "no content in %s!\n", filename);
992       return;
993     }
994
995   vlib_cli_output (vm, buf);
996   vec_free (buf);
997 }
998
999 /** api_trace_command_fn - control the binary API trace / replay feature
1000
1001     Note: this command MUST be marked thread-safe. Replay with
1002     multiple worker threads depends in many cases on worker thread
1003     graph replica maintenance. If we (implicitly) assert a worker
1004     thread barrier at the debug CLI level, all graph replica changes
1005     are deferred until the replay operation completes. If an interface
1006     is deleted, the wheels fall off.
1007  */
1008
1009 static clib_error_t *
1010 api_trace_command_fn (vlib_main_t * vm,
1011                       unformat_input_t * input, vlib_cli_command_t * cmd)
1012 {
1013   unformat_input_t _line_input, *line_input = &_line_input;
1014   u32 nitems = 256 << 10;
1015   api_main_t *am = vlibapi_get_main ();
1016   vl_api_trace_which_t which = VL_API_TRACE_RX;
1017   u8 *filename = 0;
1018   u8 *chroot_filename = 0;
1019   u32 first = 0;
1020   u32 last = (u32) ~ 0;
1021   FILE *fp;
1022   int rv;
1023
1024   /* Get a line of input. */
1025   if (!unformat_user (input, unformat_line_input, line_input))
1026     return 0;
1027
1028   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1029     {
1030       if (unformat (line_input, "on") || unformat (line_input, "enable"))
1031         {
1032           if (unformat (line_input, "nitems %d", &nitems))
1033             ;
1034           vlib_worker_thread_barrier_sync (vm);
1035           vl_msg_api_trace_configure (am, which, nitems);
1036           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
1037           vlib_worker_thread_barrier_release (vm);
1038         }
1039       else if (unformat (line_input, "off"))
1040         {
1041           vlib_worker_thread_barrier_sync (vm);
1042           vl_msg_api_trace_onoff (am, which, 0);
1043           vlib_worker_thread_barrier_release (vm);
1044         }
1045       else if (unformat (line_input, "save-json %s", &filename))
1046         {
1047           if (strstr ((char *) filename, "..") ||
1048               index ((char *) filename, '/'))
1049             {
1050               vlib_cli_output (vm, "illegal characters in filename '%s'",
1051                                filename);
1052               goto out;
1053             }
1054
1055           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1056
1057           vec_free (filename);
1058
1059           if (file_exists (chroot_filename))
1060             {
1061               vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1062               goto out;
1063             }
1064
1065           fp = fopen ((char *) chroot_filename, "w");
1066           if (fp == NULL)
1067             {
1068               vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
1069               goto out;
1070             }
1071           vlib_worker_thread_barrier_sync (vm);
1072           rv = vl_msg_api_trace_save (am, which, fp, 1);
1073           if (rv == -1)
1074             vlib_cli_output (vm, "API Trace data not present\n");
1075           else if (rv < 0)
1076             vlib_cli_output (vm, "failed to save api trace\n");
1077           else
1078             vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
1079           vlib_worker_thread_barrier_release (vm);
1080           fclose (fp);
1081         }
1082       else if (unformat (line_input, "save %s", &filename))
1083         {
1084           if (strstr ((char *) filename, "..")
1085               || index ((char *) filename, '/'))
1086             {
1087               vlib_cli_output (vm, "illegal characters in filename '%s'",
1088                                filename);
1089               goto out;
1090             }
1091
1092           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1093
1094           vec_free (filename);
1095
1096           if (file_exists (chroot_filename))
1097             {
1098               vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1099               goto out;
1100             }
1101
1102           fp = fopen ((char *) chroot_filename, "w");
1103           if (fp == NULL)
1104             {
1105               vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
1106               goto out;
1107             }
1108           vlib_worker_thread_barrier_sync (vm);
1109           rv = vl_msg_api_trace_save (am, which, fp, 0);
1110           vlib_worker_thread_barrier_release (vm);
1111           fclose (fp);
1112           if (rv == -1)
1113             vlib_cli_output (vm, "API Trace data not present\n");
1114           else if (rv == -2)
1115             vlib_cli_output (vm, "File for writing is closed\n");
1116           else if (rv == -10)
1117             vlib_cli_output (vm, "Error while writing header to file\n");
1118           else if (rv == -11)
1119             vlib_cli_output (vm, "Error while writing trace to file\n");
1120           else if (rv == -12)
1121             vlib_cli_output (vm,
1122                              "Error while writing end of buffer trace to file\n");
1123           else if (rv == -13)
1124             vlib_cli_output (vm,
1125                              "Error while writing start of buffer trace to file\n");
1126           else if (rv < 0)
1127             vlib_cli_output (vm, "Unknown error while saving: %d", rv);
1128           else
1129             vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
1130           goto out;
1131         }
1132       else if (unformat (line_input, "tojson %s", &filename))
1133         {
1134           vl_msg_api_process_file (vm, filename, first, last, DUMP_JSON);
1135         }
1136       else if (unformat (line_input, "dump-file-json %s", &filename))
1137         {
1138           vl_msg_dump_file_json (vm, filename);
1139         }
1140       else if (unformat (line_input, "dump-file %s", &filename))
1141         {
1142           vl_msg_api_process_file (vm, filename, first, last, DUMP);
1143         }
1144       else if (unformat (line_input, "dump-json"))
1145         {
1146           vl_msg_api_dump_trace (vm, which, 1);
1147         }
1148       else if (unformat (line_input, "dump"))
1149         {
1150           vl_msg_api_dump_trace (vm, which, 0);
1151         }
1152       else if (unformat (line_input, "replay-json %s", &filename))
1153         {
1154           vl_msg_replay_json (vm, filename);
1155         }
1156       else if (unformat (line_input, "replay %s", &filename))
1157         {
1158           vl_msg_api_process_file (vm, filename, first, last, REPLAY);
1159         }
1160       else if (unformat (line_input, "initializers %s", &filename))
1161         {
1162           vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
1163         }
1164       else if (unformat (line_input, "tx"))
1165         {
1166           which = VL_API_TRACE_TX;
1167         }
1168       else if (unformat (line_input, "first %d", &first))
1169         {
1170           ;
1171         }
1172       else if (unformat (line_input, "last %d", &last))
1173         {
1174           ;
1175         }
1176       else if (unformat (line_input, "status"))
1177         {
1178           vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
1179                            am, which);
1180         }
1181       else if (unformat (line_input, "free"))
1182         {
1183           vlib_worker_thread_barrier_sync (vm);
1184           vl_msg_api_trace_onoff (am, which, 0);
1185           vl_msg_api_trace_free (am, which);
1186           vlib_worker_thread_barrier_release (vm);
1187         }
1188       else if (unformat (line_input, "post-mortem-on"))
1189         vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1190       else if (unformat (line_input, "post-mortem-off"))
1191         vl_msg_api_post_mortem_dump_enable_disable (0 /* enable */ );
1192       else
1193         return clib_error_return (0, "unknown input `%U'",
1194                                   format_unformat_error, input);
1195     }
1196 out:
1197   vec_free (filename);
1198   vec_free (chroot_filename);
1199   unformat_free (line_input);
1200   return 0;
1201 }
1202
1203 /*?
1204  * Display, replay, or save a binary API trace
1205 ?*/
1206
1207 /* *INDENT-OFF* */
1208 VLIB_CLI_COMMAND (api_trace_command, static) = {
1209   .path = "api trace",
1210   .short_help = "api trace [tx][on|off][first <n>][last <n>][status][free]"
1211                 "[post-mortem-on][dump|dump-file|dump-json|save|tojson|save-"
1212                 "json|replay <file>|replay-json <file>][nitems <n>]"
1213                 "[initializers <file>]",
1214   .function = api_trace_command_fn,
1215   .is_mp_safe = 1,
1216 };
1217 /* *INDENT-ON* */
1218
1219 static clib_error_t *
1220 api_trace_config_fn (vlib_main_t * vm, unformat_input_t * input)
1221 {
1222   u32 nitems = 256 << 10;
1223   vl_api_trace_which_t which = VL_API_TRACE_RX;
1224   api_main_t *am = vlibapi_get_main ();
1225
1226   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1227     {
1228       if (unformat (input, "on") || unformat (input, "enable"))
1229         {
1230           if (unformat (input, "nitems %d", &nitems))
1231             ;
1232           vl_msg_api_trace_configure (am, which, nitems);
1233           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
1234           vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1235         }
1236       else if (unformat (input, "save-api-table %s",
1237                          &am->save_msg_table_filename))
1238         ;
1239       else
1240         return clib_error_return (0, "unknown input `%U'",
1241                                   format_unformat_error, input);
1242     }
1243   return 0;
1244 }
1245
1246 /*?
1247  * This module has three configuration parameters:
1248  * "on" or "enable" - enables binary api tracing
1249  * "nitems <nnn>" - sets the size of the circular buffer to <nnn>
1250  * "save-api-table <filename>" - dumps the API message table to /tmp/<filename>
1251 ?*/
1252 VLIB_CONFIG_FUNCTION (api_trace_config_fn, "api-trace");
1253
1254 static clib_error_t *
1255 api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
1256 {
1257   api_main_t *am = vlibapi_get_main ();
1258   u32 nitems;
1259
1260   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1261     {
1262       if (unformat (input, "length %d", &nitems) ||
1263           (unformat (input, "len %d", &nitems)))
1264         {
1265           if (nitems >= 1024)
1266             am->vlib_input_queue_length = nitems;
1267           else
1268             clib_warning ("vlib input queue length %d too small, ignored",
1269                           nitems);
1270         }
1271       else
1272         return clib_error_return (0, "unknown input `%U'",
1273                                   format_unformat_error, input);
1274     }
1275   return 0;
1276 }
1277
1278 VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
1279
1280 static u8 *
1281 extract_name (u8 * s)
1282 {
1283   u8 *rv;
1284
1285   rv = vec_dup (s);
1286
1287   while (vec_len (rv) && rv[vec_len (rv)] != '_')
1288     vec_dec_len (rv, 0);
1289
1290   rv[vec_len (rv)] = 0;
1291
1292   return rv;
1293 }
1294
1295 static u8 *
1296 extract_crc (u8 * s)
1297 {
1298   int i;
1299   u8 *rv;
1300
1301   rv = vec_dup (s);
1302
1303   for (i = vec_len (rv) - 1; i >= 0; i--)
1304     {
1305       if (rv[i] == '_')
1306         {
1307           vec_delete (rv, i + 1, 0);
1308           break;
1309         }
1310     }
1311   return rv;
1312 }
1313
1314 typedef struct
1315 {
1316   u8 *name_and_crc;
1317   u8 *name;
1318   u8 *crc;
1319   u32 msg_index;
1320   int which;
1321 } msg_table_unserialize_t;
1322
1323 static int
1324 table_id_cmp (void *a1, void *a2)
1325 {
1326   msg_table_unserialize_t *n1 = a1;
1327   msg_table_unserialize_t *n2 = a2;
1328
1329   return (n1->msg_index - n2->msg_index);
1330 }
1331
1332 static int
1333 table_name_and_crc_cmp (void *a1, void *a2)
1334 {
1335   msg_table_unserialize_t *n1 = a1;
1336   msg_table_unserialize_t *n2 = a2;
1337
1338   return strcmp ((char *) n1->name_and_crc, (char *) n2->name_and_crc);
1339 }
1340
1341 static clib_error_t *
1342 dump_api_table_file_command_fn (vlib_main_t * vm,
1343                                 unformat_input_t * input,
1344                                 vlib_cli_command_t * cmd)
1345 {
1346   u8 *filename = 0;
1347   api_main_t *am = vlibapi_get_main ();
1348   serialize_main_t _sm, *sm = &_sm;
1349   clib_error_t *error;
1350   u32 nmsgs;
1351   u32 msg_index;
1352   u8 *name_and_crc;
1353   int compare_current = 0;
1354   int numeric_sort = 0;
1355   msg_table_unserialize_t *table = 0, *item;
1356   u32 i;
1357   u32 ndifferences = 0;
1358
1359   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1360     {
1361       if (unformat (input, "file %s", &filename))
1362         ;
1363       else if (unformat (input, "compare-current")
1364                || unformat (input, "compare"))
1365         compare_current = 1;
1366       else if (unformat (input, "numeric"))
1367         numeric_sort = 1;
1368       else
1369         return clib_error_return (0, "unknown input `%U'",
1370                                   format_unformat_error, input);
1371     }
1372
1373   if (numeric_sort && compare_current)
1374     return clib_error_return
1375       (0, "Comparison and numeric sorting are incompatible");
1376
1377   if (filename == 0)
1378     return clib_error_return (0, "File not specified");
1379
1380   /* Load the serialized message table from the table dump */
1381
1382   error = unserialize_open_clib_file (sm, (char *) filename);
1383
1384   if (error)
1385     return error;
1386
1387   unserialize_integer (sm, &nmsgs, sizeof (u32));
1388
1389   for (i = 0; i < nmsgs; i++)
1390     {
1391       msg_index = unserialize_likely_small_unsigned_integer (sm);
1392       unserialize_cstring (sm, (char **) &name_and_crc);
1393       vec_add2 (table, item, 1);
1394       item->msg_index = msg_index;
1395       item->name_and_crc = name_and_crc;
1396       item->name = extract_name (name_and_crc);
1397       item->crc = extract_crc (name_and_crc);
1398       item->which = 0;          /* file */
1399     }
1400   unserialize_close (sm);
1401
1402   /* Compare with the current image? */
1403   if (compare_current)
1404     {
1405       /* Append the current message table */
1406       u8 *tblv = vl_api_serialize_message_table (am, 0);
1407
1408       serialize_open_vector (sm, tblv);
1409       unserialize_integer (sm, &nmsgs, sizeof (u32));
1410
1411       for (i = 0; i < nmsgs; i++)
1412         {
1413           msg_index = unserialize_likely_small_unsigned_integer (sm);
1414           unserialize_cstring (sm, (char **) &name_and_crc);
1415
1416           vec_add2 (table, item, 1);
1417           item->msg_index = msg_index;
1418           item->name_and_crc = name_and_crc;
1419           item->name = extract_name (name_and_crc);
1420           item->crc = extract_crc (name_and_crc);
1421           item->which = 1;      /* current_image */
1422         }
1423       vec_free (tblv);
1424     }
1425
1426   /* Sort the table. */
1427   if (numeric_sort)
1428     vec_sort_with_function (table, table_id_cmp);
1429   else
1430     vec_sort_with_function (table, table_name_and_crc_cmp);
1431
1432   if (compare_current)
1433     {
1434       u8 *dashes = 0;
1435       ndifferences = 0;
1436
1437       /*
1438        * In this case, the recovered table will have two entries per
1439        * API message. So, if entries i and i+1 match, the message definitions
1440        * are identical. Otherwise, the crc is different, or a message is
1441        * present in only one of the tables.
1442        */
1443       vlib_cli_output (vm, "%-60s | %s", "Message Name", "Result");
1444       vec_validate_init_empty (dashes, 60, '-');
1445       vec_terminate_c_string (dashes);
1446       vlib_cli_output (vm, "%60s-|-%s", dashes, "-----------------");
1447       vec_free (dashes);
1448       for (i = 0; i < vec_len (table);)
1449         {
1450           /* Last message lonely? */
1451           if (i == vec_len (table) - 1)
1452             {
1453               ndifferences++;
1454               goto last_unique;
1455             }
1456
1457           /* Identical pair? */
1458           if (!strncmp
1459               ((char *) table[i].name_and_crc,
1460                (char *) table[i + 1].name_and_crc,
1461                vec_len (table[i].name_and_crc)))
1462             {
1463               i += 2;
1464               continue;
1465             }
1466
1467           ndifferences++;
1468
1469           /* Only in one of two tables? */
1470           if (i + 1 == vec_len (table)
1471               || strcmp ((char *) table[i].name, (char *) table[i + 1].name))
1472             {
1473             last_unique:
1474               vlib_cli_output (vm, "%-60s | only in %s",
1475                                table[i].name, table[i].which ?
1476                                "image" : "file");
1477               i++;
1478               continue;
1479             }
1480           /* In both tables, but with different signatures */
1481           vlib_cli_output (vm, "%-60s | definition changed", table[i].name);
1482           i += 2;
1483         }
1484       if (ndifferences == 0)
1485         vlib_cli_output (vm, "No api message signature differences found.");
1486       else
1487         vlib_cli_output (vm, "\nFound %u api message signature differences",
1488                          ndifferences);
1489       goto cleanup;
1490     }
1491
1492   /* Dump the table, sorted as shown above */
1493   vlib_cli_output (vm, "%=60s %=8s %=10s", "Message name", "MsgID", "CRC");
1494
1495   for (i = 0; i < vec_len (table); i++)
1496     {
1497       item = table + i;
1498       vlib_cli_output (vm, "%-60s %8u %10s", item->name,
1499                        item->msg_index, item->crc);
1500     }
1501
1502 cleanup:
1503   for (i = 0; i < vec_len (table); i++)
1504     {
1505       vec_free (table[i].name_and_crc);
1506       vec_free (table[i].name);
1507       vec_free (table[i].crc);
1508     }
1509
1510   vec_free (table);
1511
1512   return 0;
1513 }
1514
1515 /*?
1516  * Displays a serialized API message decode table, sorted by message name
1517  *
1518  * @cliexpar
1519  * @cliexstart{show api dump file <filename>}
1520  *                                                Message name    MsgID        CRC
1521  * accept_session                                                    407   8e2a127e
1522  * accept_session_reply                                              408   67d8c22a
1523  * add_node_next                                                     549   e4202993
1524  * add_node_next_reply                                               550   e89d6eed
1525  * etc.
1526  * @cliexend
1527 ?*/
1528
1529 /*?
1530  * Compares a serialized API message decode table with the current image
1531  *
1532  * @cliexpar
1533  * @cliexstart{show api dump file <filename> compare}
1534  * ip_add_del_route                                             definition changed
1535  * ip_table_add_del                                             definition changed
1536  * l2_macs_event                                                only in image
1537  * vnet_ip4_fib_counters                                        only in file
1538  * vnet_ip4_nbr_counters                                        only in file
1539  * @cliexend
1540 ?*/
1541
1542 /*?
1543  * Display a serialized API message decode table, compare a saved
1544  * decode table with the current image, to establish API differences.
1545  *
1546 ?*/
1547 /* *INDENT-OFF* */
1548 VLIB_CLI_COMMAND (dump_api_table_file, static) =
1549 {
1550   .path = "show api dump",
1551   .short_help = "show api dump file <filename> [numeric | compare-current]",
1552   .function = dump_api_table_file_command_fn,
1553 };
1554
1555 /* *INDENT-ON* */
1556 /*
1557  * fd.io coding-style-patch-verification: ON
1558  *
1559  * Local Variables:
1560  * eval: (c-set-style "gnu")
1561  * End:
1562  */