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