0057c85adcf48858d0e29e2e9654443ff52eb8bf
[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   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   struct stat statb;
395   size_t file_size;
396   u8 *msg;
397   api_main_t *am = vlibapi_get_main ();
398   u8 *tmpbuf = 0;
399   u32 nitems, nitems_msgtbl;
400
401   fd = open ((char *) filename, O_RDONLY);
402
403   if (fd < 0)
404     {
405       vlib_cli_output (vm, "Couldn't open %s\n", filename);
406       return;
407     }
408
409   if (fstat (fd, &statb) < 0)
410     {
411       vlib_cli_output (vm, "Couldn't stat %s\n", filename);
412       close (fd);
413       return;
414     }
415
416   if (!(statb.st_mode & S_IFREG) || (statb.st_size < sizeof (*hp)))
417     {
418       vlib_cli_output (vm, "File not plausible: %s\n", filename);
419       close (fd);
420       return;
421     }
422
423   file_size = statb.st_size;
424   file_size = (file_size + 4095) & ~(4095);
425
426   hp = mmap (0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
427
428   if (hp == (vl_api_trace_file_header_t *) MAP_FAILED)
429     {
430       vlib_cli_output (vm, "mmap failed: %s\n", filename);
431       close (fd);
432       return;
433     }
434   close (fd);
435
436   CLIB_MEM_UNPOISON (hp, file_size);
437
438   nitems = ntohl (hp->nitems);
439
440   if (last_index == (u32) ~ 0)
441     {
442       last_index = nitems - 1;
443     }
444
445   if (first_index >= nitems || last_index >= nitems)
446     {
447       vlib_cli_output (vm, "Range (%d, %d) outside file range (0, %d)\n",
448                        first_index, last_index, nitems - 1);
449       munmap (hp, file_size);
450       return;
451     }
452   if (hp->wrapped)
453     vlib_cli_output (vm,
454                      "Note: wrapped/incomplete trace, results may vary\n");
455
456   msg = (u8 *) (hp + 1);
457
458   u16 *msgid_vec = 0;
459   serialize_main_t _sm, *sm = &_sm;
460   u32 msgtbl_size = ntohl (hp->msgtbl_size);
461   u8 *name_and_crc;
462
463   unserialize_open_data (sm, msg, msgtbl_size);
464   unserialize_integer (sm, &nitems_msgtbl, sizeof (u32));
465
466   for (i = 0; i < nitems_msgtbl; i++)
467     {
468       u16 msg_index = unserialize_likely_small_unsigned_integer (sm);
469       unserialize_cstring (sm, (char **) &name_and_crc);
470       u16 msg_index2 = vl_msg_api_get_msg_index (name_and_crc);
471       vec_validate (msgid_vec, msg_index);
472       msgid_vec[msg_index] = msg_index2;
473     }
474
475   msg += msgtbl_size;
476
477   for (i = 0; i < first_index; i++)
478     {
479       trace_cfg_t *cfgp;
480       int size;
481       u16 msg_id;
482
483       size = clib_host_to_net_u32 (*(u32 *) msg);
484       msg += sizeof (u32);
485
486       msg_id = ntohs (*((u16 *) msg));
487       if (msg_id < vec_len (msgid_vec))
488         msg_id = msgid_vec[msg_id];
489       cfgp = am->api_trace_cfg + msg_id;
490       if (!cfgp)
491         {
492           vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
493           munmap (hp, file_size);
494           return;
495         }
496       msg += size;
497     }
498
499   if (which == REPLAY)
500     am->replay_in_progress = 1;
501
502   for (; i <= last_index; i++)
503     {
504       trace_cfg_t *cfgp;
505       u16 msg_id;
506       int size;
507
508       if (which == DUMP)
509         vlib_cli_output (vm, "---------- trace %d -----------\n", i);
510
511       size = clib_host_to_net_u32 (*(u32 *) msg);
512       msg += sizeof (u32);
513
514       msg_id = ntohs (*((u16 *) msg));
515       if (msg_id < vec_len (msgid_vec))
516         {
517           msg_id = msgid_vec[msg_id];
518         }
519
520       cfgp = am->api_trace_cfg + msg_id;
521       if (!cfgp)
522         {
523           vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
524           munmap (hp, file_size);
525           vec_free (tmpbuf);
526           am->replay_in_progress = 0;
527           return;
528         }
529
530       /* Copy the buffer (from the read-only mmap'ed file) */
531       vec_validate (tmpbuf, size - 1 + sizeof (uword));
532       clib_memcpy (tmpbuf + sizeof (uword), msg, size);
533       clib_memset (tmpbuf, 0xf, sizeof (uword));
534
535       /*
536        * Endian swap if needed. All msg data is supposed to be in
537        * network byte order.
538        */
539       if (((which == DUMP) && clib_arch_is_little_endian))
540         {
541           void (*endian_fp) (void *);
542           if (msg_id >= vec_len (am->msg_endian_handlers)
543               || (am->msg_endian_handlers[msg_id] == 0))
544             {
545               vlib_cli_output (vm, "Ugh: msg id %d no endian swap\n", msg_id);
546               munmap (hp, file_size);
547               vec_free (tmpbuf);
548               am->replay_in_progress = 0;
549               return;
550             }
551           endian_fp = am->msg_endian_handlers[msg_id];
552           (*endian_fp) (tmpbuf + sizeof (uword));
553         }
554
555       /* msg_id always in network byte order */
556       if (clib_arch_is_little_endian)
557         {
558           u16 *msg_idp = (u16 *) (tmpbuf + sizeof (uword));
559           *msg_idp = msg_id;
560         }
561
562       switch (which)
563         {
564         case DUMP:
565           if (msg_id < vec_len (am->msg_print_handlers) &&
566               am->msg_print_handlers[msg_id])
567             {
568               u8 *(*print_fp) (void *, void *);
569
570               print_fp = (void *) am->msg_print_handlers[msg_id];
571               (*print_fp) (tmpbuf + sizeof (uword), vm);
572             }
573           else
574             {
575               vlib_cli_output (vm, "Skipping msg id %d: no print fcn\n",
576                                msg_id);
577               break;
578             }
579           break;
580
581         case INITIALIZERS:
582           if (msg_id < vec_len (am->msg_print_handlers) &&
583               am->msg_print_handlers[msg_id])
584             {
585               u8 *s;
586               int j;
587               u8 *(*print_fp) (void *, void *);
588
589               print_fp = (void *) am->msg_print_handlers[msg_id];
590
591               vlib_cli_output (vm, "/*");
592
593               (*print_fp) (tmpbuf + sizeof (uword), vm);
594               vlib_cli_output (vm, "*/\n");
595
596               s = format (0, "static u8 * vl_api_%s_%d[%d] = {",
597                           am->msg_names[msg_id], i,
598                           am->api_trace_cfg[msg_id].size);
599
600               for (j = 0; j < am->api_trace_cfg[msg_id].size; j++)
601                 {
602                   if ((j & 7) == 0)
603                     s = format (s, "\n    ");
604                   s = format (s, "0x%02x,", tmpbuf[sizeof (uword) + j]);
605                 }
606               s = format (s, "\n};\n%c", 0);
607               vlib_cli_output (vm, (char *) s);
608               vec_free (s);
609             }
610           break;
611
612         case REPLAY:
613           if (msg_id < vec_len (am->msg_print_handlers) &&
614               am->msg_print_handlers[msg_id] && cfgp->replay_enable)
615             {
616               void (*handler) (void *, vlib_main_t *);
617
618               handler = (void *) am->msg_handlers[msg_id];
619
620               if (!am->is_mp_safe[msg_id])
621                 vl_msg_api_barrier_sync ();
622               (*handler) (tmpbuf + sizeof (uword), vm);
623               if (!am->is_mp_safe[msg_id])
624                 vl_msg_api_barrier_release ();
625             }
626           else
627             {
628               if (cfgp->replay_enable)
629                 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
630                                  msg_id);
631               break;
632             }
633           break;
634         }
635
636       _vec_len (tmpbuf) = 0;
637       msg += size;
638     }
639
640   munmap (hp, file_size);
641   vec_free (tmpbuf);
642   am->replay_in_progress = 0;
643 }
644
645 /** api_trace_command_fn - control the binary API trace / replay feature
646
647     Note: this command MUST be marked thread-safe. Replay with
648     multiple worker threads depends in many cases on worker thread
649     graph replica maintenance. If we (implicitly) assert a worker
650     thread barrier at the debug CLI level, all graph replica changes
651     are deferred until the replay operation completes. If an interface
652     is deleted, the wheels fall off.
653  */
654
655 static clib_error_t *
656 api_trace_command_fn (vlib_main_t * vm,
657                       unformat_input_t * input, vlib_cli_command_t * cmd)
658 {
659   unformat_input_t _line_input, *line_input = &_line_input;
660   u32 nitems = 256 << 10;
661   api_main_t *am = vlibapi_get_main ();
662   vl_api_trace_which_t which = VL_API_TRACE_RX;
663   u8 *filename = 0;
664   u8 *chroot_filename = 0;
665   u32 first = 0;
666   u32 last = (u32) ~ 0;
667   FILE *fp;
668   int rv;
669
670   /* Get a line of input. */
671   if (!unformat_user (input, unformat_line_input, line_input))
672     return 0;
673
674   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
675     {
676       if (unformat (line_input, "on") || unformat (line_input, "enable"))
677         {
678           if (unformat (line_input, "nitems %d", &nitems))
679             ;
680           vlib_worker_thread_barrier_sync (vm);
681           vl_msg_api_trace_configure (am, which, nitems);
682           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
683           vlib_worker_thread_barrier_release (vm);
684         }
685       else if (unformat (line_input, "off"))
686         {
687           vlib_worker_thread_barrier_sync (vm);
688           vl_msg_api_trace_onoff (am, which, 0);
689           vlib_worker_thread_barrier_release (vm);
690         }
691       else if (unformat (line_input, "save %s", &filename))
692         {
693           if (strstr ((char *) filename, "..")
694               || index ((char *) filename, '/'))
695             {
696               vlib_cli_output (vm, "illegal characters in filename '%s'",
697                                filename);
698               goto out;
699             }
700
701           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
702
703           vec_free (filename);
704
705           fp = fopen ((char *) chroot_filename, "w");
706           if (fp == NULL)
707             {
708               vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
709               goto out;
710             }
711           vlib_worker_thread_barrier_sync (vm);
712           rv = vl_msg_api_trace_save (am, which, fp);
713           vlib_worker_thread_barrier_release (vm);
714           fclose (fp);
715           if (rv == -1)
716             vlib_cli_output (vm, "API Trace data not present\n");
717           else if (rv == -2)
718             vlib_cli_output (vm, "File for writing is closed\n");
719           else if (rv == -10)
720             vlib_cli_output (vm, "Error while writing header to file\n");
721           else if (rv == -11)
722             vlib_cli_output (vm, "Error while writing trace to file\n");
723           else if (rv == -12)
724             vlib_cli_output (vm,
725                              "Error while writing end of buffer trace to file\n");
726           else if (rv == -13)
727             vlib_cli_output (vm,
728                              "Error while writing start of buffer trace to file\n");
729           else if (rv < 0)
730             vlib_cli_output (vm, "Unknown error while saving: %d", rv);
731           else
732             vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
733           goto out;
734         }
735       else if (unformat (line_input, "dump %s", &filename))
736         {
737           vl_msg_api_process_file (vm, filename, first, last, DUMP);
738         }
739       else if (unformat (line_input, "replay %s", &filename))
740         {
741           vl_msg_api_process_file (vm, filename, first, last, REPLAY);
742         }
743       else if (unformat (line_input, "initializers %s", &filename))
744         {
745           vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
746         }
747       else if (unformat (line_input, "tx"))
748         {
749           which = VL_API_TRACE_TX;
750         }
751       else if (unformat (line_input, "first %d", &first))
752         {
753           ;
754         }
755       else if (unformat (line_input, "last %d", &last))
756         {
757           ;
758         }
759       else if (unformat (line_input, "status"))
760         {
761           vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
762                            am, which);
763         }
764       else if (unformat (line_input, "free"))
765         {
766           vlib_worker_thread_barrier_sync (vm);
767           vl_msg_api_trace_onoff (am, which, 0);
768           vl_msg_api_trace_free (am, which);
769           vlib_worker_thread_barrier_release (vm);
770         }
771       else if (unformat (line_input, "post-mortem-on"))
772         vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
773       else if (unformat (line_input, "post-mortem-off"))
774         vl_msg_api_post_mortem_dump_enable_disable (0 /* enable */ );
775       else
776         return clib_error_return (0, "unknown input `%U'",
777                                   format_unformat_error, input);
778     }
779 out:
780   vec_free (filename);
781   vec_free (chroot_filename);
782   unformat_free (line_input);
783   return 0;
784 }
785
786 /*?
787  * Display, replay, or save a binary API trace
788 ?*/
789
790 /* *INDENT-OFF* */
791 VLIB_CLI_COMMAND (api_trace_command, static) = {
792   .path = "api trace",
793   .short_help = "api trace [on|off][first <n>][last <n>][status][free]"
794                 "[post-mortem-on][dump|save|replay <file>]",
795   .function = api_trace_command_fn,
796   .is_mp_safe = 1,
797 };
798 /* *INDENT-ON* */
799
800 static clib_error_t *
801 vl_api_trace_command (vlib_main_t * vm,
802                       unformat_input_t * input, vlib_cli_command_t * cli_cmd)
803 {
804   u32 nitems = 1024;
805   vl_api_trace_which_t which = VL_API_TRACE_RX;
806   api_main_t *am = vlibapi_get_main ();
807
808   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
809     {
810       if (unformat (input, "rx nitems %u", &nitems) || unformat (input, "rx"))
811         goto configure;
812       else if (unformat (input, "tx nitems %u", &nitems)
813                || unformat (input, "tx"))
814         {
815           which = VL_API_TRACE_RX;
816           goto configure;
817         }
818       else if (unformat (input, "on rx"))
819         {
820           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
821         }
822       else if (unformat (input, "on tx"))
823         {
824           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 1);
825         }
826       else if (unformat (input, "on"))
827         {
828           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
829         }
830       else if (unformat (input, "off"))
831         {
832           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
833           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
834         }
835       else if (unformat (input, "free"))
836         {
837           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
838           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
839           vl_msg_api_trace_free (am, VL_API_TRACE_RX);
840           vl_msg_api_trace_free (am, VL_API_TRACE_TX);
841         }
842       else if (unformat (input, "debug on"))
843         {
844           am->msg_print_flag = 1;
845         }
846       else if (unformat (input, "debug off"))
847         {
848           am->msg_print_flag = 0;
849         }
850       else
851         return clib_error_return (0, "unknown input `%U'",
852                                   format_unformat_error, input);
853     }
854   return 0;
855
856 configure:
857   if (vl_msg_api_trace_configure (am, which, nitems))
858     {
859       vlib_cli_output (vm, "warning: trace configure error (%d, %d)",
860                        which, nitems);
861     }
862
863   return 0;
864 }
865
866 /*?
867  * Control the binary API trace mechanism
868 ?*/
869 /* *INDENT-OFF* */
870 VLIB_CLI_COMMAND (trace, static) =
871 {
872   .path = "set api-trace",
873   .short_help = "API trace [on][on tx][on rx][off][free][debug on][debug off]",
874   .function = vl_api_trace_command,
875 };
876 /* *INDENT-ON* */
877
878 static clib_error_t *
879 api_trace_config_fn (vlib_main_t * vm, unformat_input_t * input)
880 {
881   u32 nitems = 256 << 10;
882   vl_api_trace_which_t which = VL_API_TRACE_RX;
883   api_main_t *am = vlibapi_get_main ();
884
885   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
886     {
887       if (unformat (input, "on") || unformat (input, "enable"))
888         {
889           if (unformat (input, "nitems %d", &nitems))
890             ;
891           vl_msg_api_trace_configure (am, which, nitems);
892           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
893           vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
894         }
895       else if (unformat (input, "save-api-table %s",
896                          &am->save_msg_table_filename))
897         ;
898       else
899         return clib_error_return (0, "unknown input `%U'",
900                                   format_unformat_error, input);
901     }
902   return 0;
903 }
904
905 /*?
906  * This module has three configuration parameters:
907  * "on" or "enable" - enables binary api tracing
908  * "nitems <nnn>" - sets the size of the circular buffer to <nnn>
909  * "save-api-table <filename>" - dumps the API message table to /tmp/<filename>
910 ?*/
911 VLIB_CONFIG_FUNCTION (api_trace_config_fn, "api-trace");
912
913 static clib_error_t *
914 api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
915 {
916   api_main_t *am = vlibapi_get_main ();
917   u32 nitems;
918
919   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
920     {
921       if (unformat (input, "length %d", &nitems) ||
922           (unformat (input, "len %d", &nitems)))
923         {
924           if (nitems >= 1024)
925             am->vlib_input_queue_length = nitems;
926           else
927             clib_warning ("vlib input queue length %d too small, ignored",
928                           nitems);
929         }
930       else
931         return clib_error_return (0, "unknown input `%U'",
932                                   format_unformat_error, input);
933     }
934   return 0;
935 }
936
937 VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
938
939 static u8 *
940 extract_name (u8 * s)
941 {
942   u8 *rv;
943
944   rv = vec_dup (s);
945
946   while (vec_len (rv) && rv[vec_len (rv)] != '_')
947     _vec_len (rv)--;
948
949   rv[vec_len (rv)] = 0;
950
951   return rv;
952 }
953
954 static u8 *
955 extract_crc (u8 * s)
956 {
957   int i;
958   u8 *rv;
959
960   rv = vec_dup (s);
961
962   for (i = vec_len (rv) - 1; i >= 0; i--)
963     {
964       if (rv[i] == '_')
965         {
966           vec_delete (rv, i + 1, 0);
967           break;
968         }
969     }
970   return rv;
971 }
972
973 typedef struct
974 {
975   u8 *name_and_crc;
976   u8 *name;
977   u8 *crc;
978   u32 msg_index;
979   int which;
980 } msg_table_unserialize_t;
981
982 static int
983 table_id_cmp (void *a1, void *a2)
984 {
985   msg_table_unserialize_t *n1 = a1;
986   msg_table_unserialize_t *n2 = a2;
987
988   return (n1->msg_index - n2->msg_index);
989 }
990
991 static int
992 table_name_and_crc_cmp (void *a1, void *a2)
993 {
994   msg_table_unserialize_t *n1 = a1;
995   msg_table_unserialize_t *n2 = a2;
996
997   return strcmp ((char *) n1->name_and_crc, (char *) n2->name_and_crc);
998 }
999
1000 static clib_error_t *
1001 dump_api_table_file_command_fn (vlib_main_t * vm,
1002                                 unformat_input_t * input,
1003                                 vlib_cli_command_t * cmd)
1004 {
1005   u8 *filename = 0;
1006   api_main_t *am = vlibapi_get_main ();
1007   serialize_main_t _sm, *sm = &_sm;
1008   clib_error_t *error;
1009   u32 nmsgs;
1010   u32 msg_index;
1011   u8 *name_and_crc;
1012   int compare_current = 0;
1013   int numeric_sort = 0;
1014   msg_table_unserialize_t *table = 0, *item;
1015   u32 i;
1016   u32 ndifferences = 0;
1017
1018   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1019     {
1020       if (unformat (input, "file %s", &filename))
1021         ;
1022       else if (unformat (input, "compare-current")
1023                || unformat (input, "compare"))
1024         compare_current = 1;
1025       else if (unformat (input, "numeric"))
1026         numeric_sort = 1;
1027       else
1028         return clib_error_return (0, "unknown input `%U'",
1029                                   format_unformat_error, input);
1030     }
1031
1032   if (numeric_sort && compare_current)
1033     return clib_error_return
1034       (0, "Comparison and numeric sorting are incompatible");
1035
1036   if (filename == 0)
1037     return clib_error_return (0, "File not specified");
1038
1039   /* Load the serialized message table from the table dump */
1040
1041   error = unserialize_open_clib_file (sm, (char *) filename);
1042
1043   if (error)
1044     return error;
1045
1046   unserialize_integer (sm, &nmsgs, sizeof (u32));
1047
1048   for (i = 0; i < nmsgs; i++)
1049     {
1050       msg_index = unserialize_likely_small_unsigned_integer (sm);
1051       unserialize_cstring (sm, (char **) &name_and_crc);
1052       vec_add2 (table, item, 1);
1053       item->msg_index = msg_index;
1054       item->name_and_crc = name_and_crc;
1055       item->name = extract_name (name_and_crc);
1056       item->crc = extract_crc (name_and_crc);
1057       item->which = 0;          /* file */
1058     }
1059   unserialize_close (sm);
1060
1061   /* Compare with the current image? */
1062   if (compare_current)
1063     {
1064       /* Append the current message table */
1065       u8 *tblv = vl_api_serialize_message_table (am, 0);
1066
1067       serialize_open_vector (sm, tblv);
1068       unserialize_integer (sm, &nmsgs, sizeof (u32));
1069
1070       for (i = 0; i < nmsgs; i++)
1071         {
1072           msg_index = unserialize_likely_small_unsigned_integer (sm);
1073           unserialize_cstring (sm, (char **) &name_and_crc);
1074
1075           vec_add2 (table, item, 1);
1076           item->msg_index = msg_index;
1077           item->name_and_crc = name_and_crc;
1078           item->name = extract_name (name_and_crc);
1079           item->crc = extract_crc (name_and_crc);
1080           item->which = 1;      /* current_image */
1081         }
1082       vec_free (tblv);
1083     }
1084
1085   /* Sort the table. */
1086   if (numeric_sort)
1087     vec_sort_with_function (table, table_id_cmp);
1088   else
1089     vec_sort_with_function (table, table_name_and_crc_cmp);
1090
1091   if (compare_current)
1092     {
1093       u8 *dashes = 0;
1094       ndifferences = 0;
1095
1096       /*
1097        * In this case, the recovered table will have two entries per
1098        * API message. So, if entries i and i+1 match, the message definitions
1099        * are identical. Otherwise, the crc is different, or a message is
1100        * present in only one of the tables.
1101        */
1102       vlib_cli_output (vm, "%-60s | %s", "Message Name", "Result");
1103       vec_validate_init_empty (dashes, 60, '-');
1104       vec_terminate_c_string (dashes);
1105       vlib_cli_output (vm, "%60s-|-%s", dashes, "-----------------");
1106       vec_free (dashes);
1107       for (i = 0; i < vec_len (table);)
1108         {
1109           /* Last message lonely? */
1110           if (i == vec_len (table) - 1)
1111             {
1112               ndifferences++;
1113               goto last_unique;
1114             }
1115
1116           /* Identical pair? */
1117           if (!strncmp
1118               ((char *) table[i].name_and_crc,
1119                (char *) table[i + 1].name_and_crc,
1120                vec_len (table[i].name_and_crc)))
1121             {
1122               i += 2;
1123               continue;
1124             }
1125
1126           ndifferences++;
1127
1128           /* Only in one of two tables? */
1129           if (i + 1 == vec_len (table)
1130               || strcmp ((char *) table[i].name, (char *) table[i + 1].name))
1131             {
1132             last_unique:
1133               vlib_cli_output (vm, "%-60s | only in %s",
1134                                table[i].name, table[i].which ?
1135                                "image" : "file");
1136               i++;
1137               continue;
1138             }
1139           /* In both tables, but with different signatures */
1140           vlib_cli_output (vm, "%-60s | definition changed", table[i].name);
1141           i += 2;
1142         }
1143       if (ndifferences == 0)
1144         vlib_cli_output (vm, "No api message signature differences found.");
1145       else
1146         vlib_cli_output (vm, "\nFound %u api message signature differences",
1147                          ndifferences);
1148       goto cleanup;
1149     }
1150
1151   /* Dump the table, sorted as shown above */
1152   vlib_cli_output (vm, "%=60s %=8s %=10s", "Message name", "MsgID", "CRC");
1153
1154   for (i = 0; i < vec_len (table); i++)
1155     {
1156       item = table + i;
1157       vlib_cli_output (vm, "%-60s %8u %10s", item->name,
1158                        item->msg_index, item->crc);
1159     }
1160
1161 cleanup:
1162   for (i = 0; i < vec_len (table); i++)
1163     {
1164       vec_free (table[i].name_and_crc);
1165       vec_free (table[i].name);
1166       vec_free (table[i].crc);
1167     }
1168
1169   vec_free (table);
1170
1171   return 0;
1172 }
1173
1174 /*?
1175  * Displays a serialized API message decode table, sorted by message name
1176  *
1177  * @cliexpar
1178  * @cliexstart{show api dump file <filename>}
1179  *                                                Message name    MsgID        CRC
1180  * accept_session                                                    407   8e2a127e
1181  * accept_session_reply                                              408   67d8c22a
1182  * add_node_next                                                     549   e4202993
1183  * add_node_next_reply                                               550   e89d6eed
1184  * etc.
1185  * @cliexend
1186 ?*/
1187
1188 /*?
1189  * Compares a serialized API message decode table with the current image
1190  *
1191  * @cliexpar
1192  * @cliexstart{show api dump file <filename> compare}
1193  * ip_add_del_route                                             definition changed
1194  * ip_table_add_del                                             definition changed
1195  * l2_macs_event                                                only in image
1196  * vnet_ip4_fib_counters                                        only in file
1197  * vnet_ip4_nbr_counters                                        only in file
1198  * @cliexend
1199 ?*/
1200
1201 /*?
1202  * Display a serialized API message decode table, compare a saved
1203  * decode table with the current image, to establish API differences.
1204  *
1205 ?*/
1206 /* *INDENT-OFF* */
1207 VLIB_CLI_COMMAND (dump_api_table_file, static) =
1208 {
1209   .path = "show api dump",
1210   .short_help = "show api dump file <filename> [numeric | compare-current]",
1211   .function = dump_api_table_file_command_fn,
1212 };
1213
1214 /* *INDENT-ON* */
1215 /*
1216  * fd.io coding-style-patch-verification: ON
1217  *
1218  * Local Variables:
1219  * eval: (c-set-style "gnu")
1220  * End:
1221  */