9991a9640f1ba3495fa74bdf3fd4fc92093b286b
[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_data); i++)
246     {
247       vl_api_msg_data_t *m = vl_api_get_msg_data (am, i);
248       if (verbose == 0)
249         {
250           vlib_cli_output (vm, "%-4d %s", i,
251                            m->name ? m->name : "  [no handler]");
252         }
253       else
254         {
255           vlib_cli_output (vm, "%-4d %-40s %6d %7d", i,
256                            m->name ? m->name : "  [no handler]", m->bounce,
257                            m->is_mp_safe);
258         }
259     }
260
261   return 0;
262 }
263
264 /*?
265  * Display the current api message decode tables
266 ?*/
267 /* *INDENT-OFF* */
268 VLIB_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
276 static int
277 range_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
287 static u8 *
288 format_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
301 static clib_error_t *
302 vl_api_show_plugin_command (vlib_main_t * vm,
303                             unformat_input_t * input,
304                             vlib_cli_command_t * cli_cmd)
305 {
306   api_main_t *am = vlibapi_get_main ();
307   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* */
335 VLIB_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
343 typedef enum
344 {
345   DUMP,
346   DUMP_JSON,
347   REPLAY,
348   INITIALIZERS,
349 } vl_api_replay_t;
350
351 u8 *
352 format_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
387 static void
388 vl_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;
394   u16 *msgid_vec = 0;
395   struct stat statb;
396   size_t file_size;
397   u8 *msg;
398   api_main_t *am = vlibapi_get_main ();
399   u8 *tmpbuf = 0;
400   u32 nitems, nitems_msgtbl;
401
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;
425   file_size = (file_size + 4095) & ~(4095);
426
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
437   clib_mem_unpoison (hp, file_size);
438
439   nitems = ntohl (hp->nitems);
440
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
457   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]));
474   msg = (u8 *) (hp + 1);
475
476   serialize_main_t _sm, *sm = &_sm;
477   u32 msgtbl_size = ntohl (hp->msgtbl_size);
478   u8 *name_and_crc;
479
480   assert_size (file_size_left, msgtbl_size);
481
482   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);
489       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);
494       vec_validate (msgid_vec, msg_index);
495       msgid_vec[msg_index] = msg_index2;
496     }
497
498   msg += msgtbl_size;
499
500   for (i = 0; i < first_index; i++)
501     {
502       int size;
503       u16 msg_id;
504
505       assert_size (file_size_left, sizeof (u32));
506       size = clib_host_to_net_u32 (*(u32 *) msg);
507       msg += sizeof (u32);
508
509       assert_size (file_size_left, clib_max (size, sizeof (u16)));
510       msg_id = ntohs (*((u16 *) msg));
511       if (msg_id >= vec_len (msgid_vec) ||
512           msgid_vec[msg_id] >= vec_len (am->msg_data))
513         vlib_cli_output (vm, "warning: unknown msg id %d for msg number %d\n",
514                          msg_id, i);
515
516       msg += size;
517     }
518
519   if (which == REPLAY)
520     am->replay_in_progress = 1;
521
522   for (; i <= last_index; i++)
523     {
524       vl_api_msg_data_t *m;
525       u16 msg_id;
526       int size;
527
528       if (which == DUMP)
529         vlib_cli_output (vm, "---------- trace %d -----------\n", i);
530
531       assert_size (file_size_left, sizeof (u32));
532       size = clib_host_to_net_u32 (*(u32 *) msg);
533       msg += sizeof (u32);
534
535       assert_size (file_size_left, clib_max (size, sizeof (u16)));
536       msg_id = ntohs (*((u16 *) msg));
537
538       if (msg_id >= vec_len (msgid_vec) ||
539           msgid_vec[msg_id] >= vec_len (am->msg_data))
540         {
541           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;
546         }
547
548       msg_id = msgid_vec[msg_id];
549       m = vl_api_get_msg_data (am, msg_id);
550
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);
554       clib_memset (tmpbuf, 0xf, sizeof (uword));
555
556       /*
557        * Endian swap if needed. All msg data is supposed to be in
558        * network byte order.
559        */
560       if (((which == DUMP || which == DUMP_JSON) &&
561            clib_arch_is_little_endian))
562         {
563           if (m && m->endian_handler == 0)
564             {
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             }
571           if (m)
572             {
573               m->endian_handler (tmpbuf + sizeof (uword));
574             }
575         }
576
577       /* msg_id always in network byte order */
578       if (clib_arch_is_little_endian)
579         {
580           u16 *msg_idp = (u16 *) (tmpbuf + sizeof (uword));
581           *msg_idp = msg_id;
582         }
583
584       switch (which)
585         {
586         case DUMP_JSON:
587           if (m && m->print_json_handler)
588             {
589               m->print_json_handler (tmpbuf + sizeof (uword), vm);
590             }
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
599         case DUMP:
600           if (m && m->print_handler)
601             {
602               m->print_handler (tmpbuf + sizeof (uword), vm);
603             }
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:
613           if (m && m->print_handler)
614             {
615               u8 *s;
616               int j;
617
618               vlib_cli_output (vm, "/*");
619
620               m->print_handler (tmpbuf + sizeof (uword), vm);
621               vlib_cli_output (vm, "*/\n");
622
623               s = format (0, "static u8 * vl_api_%s_%d[%d] = {", m->name, i,
624                           m->trace_size);
625
626               for (j = 0; j < m->trace_size; j++)
627                 {
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:
639           if (m && m->print_handler && m->replay_allowed)
640             {
641               if (!m->is_mp_safe)
642                 vl_msg_api_barrier_sync ();
643               m->handler (tmpbuf + sizeof (uword));
644               if (!m->is_mp_safe)
645                 vl_msg_api_barrier_release ();
646             }
647           else
648             {
649               if (m->replay_allowed)
650                 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
651                                  msg_id);
652               break;
653             }
654           break;
655         }
656
657       vec_set_len (tmpbuf, 0);
658       msg += size;
659     }
660
661   munmap (hp, file_size);
662   vec_free (tmpbuf);
663   vec_free (msgid_vec);
664   am->replay_in_progress = 0;
665 }
666
667 static int
668 file_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
680 typedef struct
681 {
682   vlib_main_t *vm;
683   u8 is_json;
684 } vl_msg_print_args;
685
686 static int
687 vl_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));
692   vl_api_msg_data_t *m = vl_api_get_msg_data (am, msg_id);
693   void (*handler) (void *, void *) = 0;
694   u8 is_json = a->is_json;
695   u8 *tmpbuf = 0;
696
697   if (!m)
698     {
699       vlib_cli_output (a->vm, "Unknown msg id %d\n", msg_id);
700       return 0;
701     }
702
703   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
710       m->endian_handler (tmpbuf);
711     }
712
713   handler = is_json ? m->print_json_handler : m->print_handler;
714
715   if (handler)
716     handler (msg, a->vm);
717   else
718     vlib_cli_output (a->vm, "Skipping msg id %d: no print fcn\n", msg_id);
719
720   vec_free (tmpbuf);
721   return 0;
722 }
723
724 static int
725 vl_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
754 static char *
755 vl_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
764   /* most callers expect a NULL-terminated C-string */
765   if (v)
766     vec_add1 (v, 0);
767
768   return v;
769 }
770
771 static u16
772 vl_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
782 static u16
783 vl_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
800 static int
801 vl_msg_exec_json_command (vlib_main_t *vm, cJSON *o)
802 {
803   api_main_t *am = vlibapi_get_main ();
804   u16 msg_id;
805   int len = 0, rv = -1;
806   vl_api_msg_data_t *m;
807   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");
818   if (!crc_obj)
819     {
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);
828   m = vl_api_get_msg_data (am, msg_id);
829   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
842   if (m->replay_allowed)
843     {
844       if (proc_warning)
845         vlib_cli_output (vm, "warning: msg %d has different signature\n");
846
847       if (!m->fromjson_handler)
848         {
849           vlib_cli_output (vm, "missing fromjson convert function! id %d\n",
850                            msg_id);
851           return rv;
852         }
853
854       msg = (u8 *) m->fromjson_handler (o, &len);
855       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)
863         m->endian_handler (msg);
864
865       if (!m->handler)
866         {
867           vlib_cli_output (vm, "no handler for msg id %d!\n", msg_id);
868           goto end;
869         }
870
871       if (!m->is_mp_safe)
872         vl_msg_api_barrier_sync ();
873       m->handler (msg);
874       if (!m->is_mp_safe)
875         vl_msg_api_barrier_release ();
876     }
877
878   rv = 0;
879 end:
880   if (msg)
881     cJSON_free (msg);
882   return rv;
883 }
884
885 static void
886 vl_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
936 static void
937 vl_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
961 /** 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
971 static clib_error_t *
972 api_trace_command_fn (vlib_main_t * vm,
973                       unformat_input_t * input, vlib_cli_command_t * cmd)
974 {
975   unformat_input_t _line_input, *line_input = &_line_input;
976   u32 nitems = 256 << 10;
977   api_main_t *am = vlibapi_get_main ();
978   vl_api_trace_which_t which = VL_API_TRACE_RX;
979   u8 *filename = 0;
980   u8 *chroot_filename = 0;
981   u32 first = 0;
982   u32 last = (u32) ~ 0;
983   FILE *fp;
984   int rv;
985
986   /* 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)
991     {
992       if (unformat (line_input, "on") || unformat (line_input, "enable"))
993         {
994           if (unformat (line_input, "nitems %d", &nitems))
995             ;
996           vlib_worker_thread_barrier_sync (vm);
997           vl_msg_api_trace_configure (am, which, nitems);
998           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
999           vlib_worker_thread_barrier_release (vm);
1000         }
1001       else if (unformat (line_input, "off"))
1002         {
1003           vlib_worker_thread_barrier_sync (vm);
1004           vl_msg_api_trace_onoff (am, which, 0);
1005           vlib_worker_thread_barrier_release (vm);
1006         }
1007       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         }
1044       else if (unformat (line_input, "save %s", &filename))
1045         {
1046           if (strstr ((char *) filename, "..")
1047               || index ((char *) filename, '/'))
1048             {
1049               vlib_cli_output (vm, "illegal characters in filename '%s'",
1050                                filename);
1051               goto out;
1052             }
1053
1054           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1055
1056           vec_free (filename);
1057
1058           if (file_exists (chroot_filename))
1059             {
1060               vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1061               goto out;
1062             }
1063
1064           fp = fopen ((char *) chroot_filename, "w");
1065           if (fp == NULL)
1066             {
1067               vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
1068               goto out;
1069             }
1070           vlib_worker_thread_barrier_sync (vm);
1071           rv = vl_msg_api_trace_save (am, which, fp, 0);
1072           vlib_worker_thread_barrier_release (vm);
1073           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)
1089             vlib_cli_output (vm, "Unknown error while saving: %d", rv);
1090           else
1091             vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
1092           goto out;
1093         }
1094       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))
1103         {
1104           vl_msg_api_process_file (vm, filename, first, last, DUMP);
1105         }
1106       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         }
1118       else if (unformat (line_input, "replay %s", &filename))
1119         {
1120           vl_msg_api_process_file (vm, filename, first, last, REPLAY);
1121         }
1122       else if (unformat (line_input, "initializers %s", &filename))
1123         {
1124           vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
1125         }
1126       else if (unformat (line_input, "tx"))
1127         {
1128           which = VL_API_TRACE_TX;
1129         }
1130       else if (unformat (line_input, "first %d", &first))
1131         {
1132           ;
1133         }
1134       else if (unformat (line_input, "last %d", &last))
1135         {
1136           ;
1137         }
1138       else if (unformat (line_input, "status"))
1139         {
1140           vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
1141                            am, which);
1142         }
1143       else if (unformat (line_input, "free"))
1144         {
1145           vlib_worker_thread_barrier_sync (vm);
1146           vl_msg_api_trace_onoff (am, which, 0);
1147           vl_msg_api_trace_free (am, which);
1148           vlib_worker_thread_barrier_release (vm);
1149         }
1150       else if (unformat (line_input, "post-mortem-on"))
1151         vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1152       else if (unformat (line_input, "post-mortem-off"))
1153         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     }
1158 out:
1159   vec_free (filename);
1160   vec_free (chroot_filename);
1161   unformat_free (line_input);
1162   return 0;
1163 }
1164
1165 /*?
1166  * Display, replay, or save a binary API trace
1167 ?*/
1168
1169 /* *INDENT-OFF* */
1170 VLIB_CLI_COMMAND (api_trace_command, static) = {
1171   .path = "api trace",
1172   .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>]",
1176   .function = api_trace_command_fn,
1177   .is_mp_safe = 1,
1178 };
1179 /* *INDENT-ON* */
1180
1181 static clib_error_t *
1182 api_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;
1186   api_main_t *am = vlibapi_get_main ();
1187
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 ?*/
1214 VLIB_CONFIG_FUNCTION (api_trace_config_fn, "api-trace");
1215
1216 static clib_error_t *
1217 api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
1218 {
1219   api_main_t *am = vlibapi_get_main ();
1220   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
1240 VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
1241
1242 static u8 *
1243 extract_name (u8 * s)
1244 {
1245   u8 *rv;
1246
1247   rv = vec_dup (s);
1248
1249   while (vec_len (rv) && rv[vec_len (rv)] != '_')
1250     vec_dec_len (rv, 1);
1251
1252   rv[vec_len (rv)] = 0;
1253
1254   return rv;
1255 }
1256
1257 static u8 *
1258 extract_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
1276 typedef 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
1285 static int
1286 table_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
1294 static int
1295 table_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
1303 static clib_error_t *
1304 dump_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;
1309   api_main_t *am = vlibapi_get_main ();
1310   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     }
1362   unserialize_close (sm);
1363
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     {
1396       u8 *dashes = 0;
1397       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        */
1405       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);
1410       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? */
1432           if (i + 1 == vec_len (table)
1433               || strcmp ((char *) table[i].name, (char *) table[i + 1].name))
1434             {
1435             last_unique:
1436               vlib_cli_output (vm, "%-60s | only in %s",
1437                                table[i].name, table[i].which ?
1438                                "image" : "file");
1439               i++;
1440               continue;
1441             }
1442           /* In both tables, but with different signatures */
1443           vlib_cli_output (vm, "%-60s | definition changed", table[i].name);
1444           i += 2;
1445         }
1446       if (ndifferences == 0)
1447         vlib_cli_output (vm, "No api message signature differences found.");
1448       else
1449         vlib_cli_output (vm, "\nFound %u api message signature differences",
1450                          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
1464 cleanup:
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* */
1510 VLIB_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  */