misc: asan: do not poison memory after munmap()
[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   u32 nitems = 256 << 10;
684   api_main_t *am = vlibapi_get_main ();
685   vl_api_trace_which_t which = VL_API_TRACE_RX;
686   u8 *filename = 0;
687   u8 *chroot_filename = 0;
688   u32 first = 0;
689   u32 last = (u32) ~ 0;
690   FILE *fp;
691   int rv;
692
693   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
694     {
695       if (unformat (input, "on") || unformat (input, "enable"))
696         {
697           if (unformat (input, "nitems %d", &nitems))
698             ;
699           vlib_worker_thread_barrier_sync (vm);
700           vl_msg_api_trace_configure (am, which, nitems);
701           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
702           vlib_worker_thread_barrier_release (vm);
703         }
704       else if (unformat (input, "off"))
705         {
706           vlib_worker_thread_barrier_sync (vm);
707           vl_msg_api_trace_onoff (am, which, 0);
708           vlib_worker_thread_barrier_release (vm);
709         }
710       else if (unformat (input, "save %s", &filename))
711         {
712           if (strstr ((char *) filename, "..")
713               || index ((char *) filename, '/'))
714             {
715               vlib_cli_output (vm, "illegal characters in filename '%s'",
716                                filename);
717               goto out;
718             }
719
720           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
721
722           vec_free (filename);
723
724           fp = fopen ((char *) chroot_filename, "w");
725           if (fp == NULL)
726             {
727               vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
728               goto out;
729             }
730           vlib_worker_thread_barrier_sync (vm);
731           rv = vl_msg_api_trace_save (am, which, fp);
732           vlib_worker_thread_barrier_release (vm);
733           fclose (fp);
734           if (rv == -1)
735             vlib_cli_output (vm, "API Trace data not present\n");
736           else if (rv == -2)
737             vlib_cli_output (vm, "File for writing is closed\n");
738           else if (rv == -10)
739             vlib_cli_output (vm, "Error while writing header to file\n");
740           else if (rv == -11)
741             vlib_cli_output (vm, "Error while writing trace to file\n");
742           else if (rv == -12)
743             vlib_cli_output (vm,
744                              "Error while writing end of buffer trace to file\n");
745           else if (rv == -13)
746             vlib_cli_output (vm,
747                              "Error while writing start of buffer trace to file\n");
748           else if (rv < 0)
749             vlib_cli_output (vm, "Unknown error while saving: %d", rv);
750           else
751             vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
752           goto out;
753         }
754       else if (unformat (input, "dump %s", &filename))
755         {
756           vl_msg_api_process_file (vm, filename, first, last, DUMP);
757         }
758       else if (unformat (input, "custom-dump %s", &filename))
759         {
760           vl_msg_api_process_file (vm, filename, first, last, CUSTOM_DUMP);
761         }
762       else if (unformat (input, "replay %s", &filename))
763         {
764           vl_msg_api_process_file (vm, filename, first, last, REPLAY);
765         }
766       else if (unformat (input, "initializers %s", &filename))
767         {
768           vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
769         }
770       else if (unformat (input, "tx"))
771         {
772           which = VL_API_TRACE_TX;
773         }
774       else if (unformat (input, "first %d", &first))
775         {
776           ;
777         }
778       else if (unformat (input, "last %d", &last))
779         {
780           ;
781         }
782       else if (unformat (input, "status"))
783         {
784           vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
785                            am, which);
786         }
787       else if (unformat (input, "free"))
788         {
789           vlib_worker_thread_barrier_sync (vm);
790           vl_msg_api_trace_onoff (am, which, 0);
791           vl_msg_api_trace_free (am, which);
792           vlib_worker_thread_barrier_release (vm);
793         }
794       else if (unformat (input, "post-mortem-on"))
795         vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
796       else if (unformat (input, "post-mortem-off"))
797         vl_msg_api_post_mortem_dump_enable_disable (0 /* enable */ );
798       else
799         return clib_error_return (0, "unknown input `%U'",
800                                   format_unformat_error, input);
801     }
802 out:
803   vec_free (filename);
804   vec_free (chroot_filename);
805   return 0;
806 }
807
808 /*?
809  * Display, replay, or save a binary API trace
810 ?*/
811
812 /* *INDENT-OFF* */
813 VLIB_CLI_COMMAND (api_trace_command, static) =
814 {
815   .path = "api trace",
816   .short_help = "api trace [on|off][first <n>][last <n>][status][free]"
817                 "[post-mortem-on][dump|custom-dump|save|replay <file>]",
818   .function = api_trace_command_fn,
819   .is_mp_safe = 1,
820 };
821 /* *INDENT-ON* */
822
823 static clib_error_t *
824 vl_api_trace_command (vlib_main_t * vm,
825                       unformat_input_t * input, vlib_cli_command_t * cli_cmd)
826 {
827   u32 nitems = 1024;
828   vl_api_trace_which_t which = VL_API_TRACE_RX;
829   api_main_t *am = vlibapi_get_main ();
830
831   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
832     {
833       if (unformat (input, "rx nitems %u", &nitems) || unformat (input, "rx"))
834         goto configure;
835       else if (unformat (input, "tx nitems %u", &nitems)
836                || unformat (input, "tx"))
837         {
838           which = VL_API_TRACE_RX;
839           goto configure;
840         }
841       else if (unformat (input, "on rx"))
842         {
843           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
844         }
845       else if (unformat (input, "on tx"))
846         {
847           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 1);
848         }
849       else if (unformat (input, "on"))
850         {
851           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
852         }
853       else if (unformat (input, "off"))
854         {
855           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
856           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
857         }
858       else if (unformat (input, "free"))
859         {
860           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
861           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
862           vl_msg_api_trace_free (am, VL_API_TRACE_RX);
863           vl_msg_api_trace_free (am, VL_API_TRACE_TX);
864         }
865       else if (unformat (input, "debug on"))
866         {
867           am->msg_print_flag = 1;
868         }
869       else if (unformat (input, "debug off"))
870         {
871           am->msg_print_flag = 0;
872         }
873       else
874         return clib_error_return (0, "unknown input `%U'",
875                                   format_unformat_error, input);
876     }
877   return 0;
878
879 configure:
880   if (vl_msg_api_trace_configure (am, which, nitems))
881     {
882       vlib_cli_output (vm, "warning: trace configure error (%d, %d)",
883                        which, nitems);
884     }
885
886   return 0;
887 }
888
889 /*?
890  * Control the binary API trace mechanism
891 ?*/
892 /* *INDENT-OFF* */
893 VLIB_CLI_COMMAND (trace, static) =
894 {
895   .path = "set api-trace",
896   .short_help = "API trace [on][on tx][on rx][off][free][debug on][debug off]",
897   .function = vl_api_trace_command,
898 };
899 /* *INDENT-ON* */
900
901 static clib_error_t *
902 api_trace_config_fn (vlib_main_t * vm, unformat_input_t * input)
903 {
904   u32 nitems = 256 << 10;
905   vl_api_trace_which_t which = VL_API_TRACE_RX;
906   api_main_t *am = vlibapi_get_main ();
907
908   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
909     {
910       if (unformat (input, "on") || unformat (input, "enable"))
911         {
912           if (unformat (input, "nitems %d", &nitems))
913             ;
914           vl_msg_api_trace_configure (am, which, nitems);
915           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
916           vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
917         }
918       else if (unformat (input, "save-api-table %s",
919                          &am->save_msg_table_filename))
920         ;
921       else
922         return clib_error_return (0, "unknown input `%U'",
923                                   format_unformat_error, input);
924     }
925   return 0;
926 }
927
928 /*?
929  * This module has three configuration parameters:
930  * "on" or "enable" - enables binary api tracing
931  * "nitems <nnn>" - sets the size of the circular buffer to <nnn>
932  * "save-api-table <filename>" - dumps the API message table to /tmp/<filename>
933 ?*/
934 VLIB_CONFIG_FUNCTION (api_trace_config_fn, "api-trace");
935
936 static clib_error_t *
937 api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
938 {
939   api_main_t *am = vlibapi_get_main ();
940   u32 nitems;
941
942   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
943     {
944       if (unformat (input, "length %d", &nitems) ||
945           (unformat (input, "len %d", &nitems)))
946         {
947           if (nitems >= 1024)
948             am->vlib_input_queue_length = nitems;
949           else
950             clib_warning ("vlib input queue length %d too small, ignored",
951                           nitems);
952         }
953       else
954         return clib_error_return (0, "unknown input `%U'",
955                                   format_unformat_error, input);
956     }
957   return 0;
958 }
959
960 VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
961
962 static u8 *
963 extract_name (u8 * s)
964 {
965   u8 *rv;
966
967   rv = vec_dup (s);
968
969   while (vec_len (rv) && rv[vec_len (rv)] != '_')
970     _vec_len (rv)--;
971
972   rv[vec_len (rv)] = 0;
973
974   return rv;
975 }
976
977 static u8 *
978 extract_crc (u8 * s)
979 {
980   int i;
981   u8 *rv;
982
983   rv = vec_dup (s);
984
985   for (i = vec_len (rv) - 1; i >= 0; i--)
986     {
987       if (rv[i] == '_')
988         {
989           vec_delete (rv, i + 1, 0);
990           break;
991         }
992     }
993   return rv;
994 }
995
996 typedef struct
997 {
998   u8 *name_and_crc;
999   u8 *name;
1000   u8 *crc;
1001   u32 msg_index;
1002   int which;
1003 } msg_table_unserialize_t;
1004
1005 static int
1006 table_id_cmp (void *a1, void *a2)
1007 {
1008   msg_table_unserialize_t *n1 = a1;
1009   msg_table_unserialize_t *n2 = a2;
1010
1011   return (n1->msg_index - n2->msg_index);
1012 }
1013
1014 static int
1015 table_name_and_crc_cmp (void *a1, void *a2)
1016 {
1017   msg_table_unserialize_t *n1 = a1;
1018   msg_table_unserialize_t *n2 = a2;
1019
1020   return strcmp ((char *) n1->name_and_crc, (char *) n2->name_and_crc);
1021 }
1022
1023 static clib_error_t *
1024 dump_api_table_file_command_fn (vlib_main_t * vm,
1025                                 unformat_input_t * input,
1026                                 vlib_cli_command_t * cmd)
1027 {
1028   u8 *filename = 0;
1029   api_main_t *am = vlibapi_get_main ();
1030   serialize_main_t _sm, *sm = &_sm;
1031   clib_error_t *error;
1032   u32 nmsgs;
1033   u32 msg_index;
1034   u8 *name_and_crc;
1035   int compare_current = 0;
1036   int numeric_sort = 0;
1037   msg_table_unserialize_t *table = 0, *item;
1038   u32 i;
1039   u32 ndifferences = 0;
1040
1041   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1042     {
1043       if (unformat (input, "file %s", &filename))
1044         ;
1045       else if (unformat (input, "compare-current")
1046                || unformat (input, "compare"))
1047         compare_current = 1;
1048       else if (unformat (input, "numeric"))
1049         numeric_sort = 1;
1050       else
1051         return clib_error_return (0, "unknown input `%U'",
1052                                   format_unformat_error, input);
1053     }
1054
1055   if (numeric_sort && compare_current)
1056     return clib_error_return
1057       (0, "Comparison and numeric sorting are incompatible");
1058
1059   if (filename == 0)
1060     return clib_error_return (0, "File not specified");
1061
1062   /* Load the serialized message table from the table dump */
1063
1064   error = unserialize_open_clib_file (sm, (char *) filename);
1065
1066   if (error)
1067     return error;
1068
1069   unserialize_integer (sm, &nmsgs, sizeof (u32));
1070
1071   for (i = 0; i < nmsgs; i++)
1072     {
1073       msg_index = unserialize_likely_small_unsigned_integer (sm);
1074       unserialize_cstring (sm, (char **) &name_and_crc);
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 = 0;          /* file */
1081     }
1082   unserialize_close (sm);
1083
1084   /* Compare with the current image? */
1085   if (compare_current)
1086     {
1087       /* Append the current message table */
1088       u8 *tblv = vl_api_serialize_message_table (am, 0);
1089
1090       serialize_open_vector (sm, tblv);
1091       unserialize_integer (sm, &nmsgs, sizeof (u32));
1092
1093       for (i = 0; i < nmsgs; i++)
1094         {
1095           msg_index = unserialize_likely_small_unsigned_integer (sm);
1096           unserialize_cstring (sm, (char **) &name_and_crc);
1097
1098           vec_add2 (table, item, 1);
1099           item->msg_index = msg_index;
1100           item->name_and_crc = name_and_crc;
1101           item->name = extract_name (name_and_crc);
1102           item->crc = extract_crc (name_and_crc);
1103           item->which = 1;      /* current_image */
1104         }
1105       vec_free (tblv);
1106     }
1107
1108   /* Sort the table. */
1109   if (numeric_sort)
1110     vec_sort_with_function (table, table_id_cmp);
1111   else
1112     vec_sort_with_function (table, table_name_and_crc_cmp);
1113
1114   if (compare_current)
1115     {
1116       u8 *dashes = 0;
1117       ndifferences = 0;
1118
1119       /*
1120        * In this case, the recovered table will have two entries per
1121        * API message. So, if entries i and i+1 match, the message definitions
1122        * are identical. Otherwise, the crc is different, or a message is
1123        * present in only one of the tables.
1124        */
1125       vlib_cli_output (vm, "%-60s | %s", "Message Name", "Result");
1126       vec_validate_init_empty (dashes, 60, '-');
1127       vec_terminate_c_string (dashes);
1128       vlib_cli_output (vm, "%60s-|-%s", dashes, "-----------------");
1129       vec_free (dashes);
1130       for (i = 0; i < vec_len (table);)
1131         {
1132           /* Last message lonely? */
1133           if (i == vec_len (table) - 1)
1134             {
1135               ndifferences++;
1136               goto last_unique;
1137             }
1138
1139           /* Identical pair? */
1140           if (!strncmp
1141               ((char *) table[i].name_and_crc,
1142                (char *) table[i + 1].name_and_crc,
1143                vec_len (table[i].name_and_crc)))
1144             {
1145               i += 2;
1146               continue;
1147             }
1148
1149           ndifferences++;
1150
1151           /* Only in one of two tables? */
1152           if (i + 1 == vec_len (table)
1153               || strcmp ((char *) table[i].name, (char *) table[i + 1].name))
1154             {
1155             last_unique:
1156               vlib_cli_output (vm, "%-60s | only in %s",
1157                                table[i].name, table[i].which ?
1158                                "image" : "file");
1159               i++;
1160               continue;
1161             }
1162           /* In both tables, but with different signatures */
1163           vlib_cli_output (vm, "%-60s | definition changed", table[i].name);
1164           i += 2;
1165         }
1166       if (ndifferences == 0)
1167         vlib_cli_output (vm, "No api message signature differences found.");
1168       else
1169         vlib_cli_output (vm, "\nFound %u api message signature differences",
1170                          ndifferences);
1171       goto cleanup;
1172     }
1173
1174   /* Dump the table, sorted as shown above */
1175   vlib_cli_output (vm, "%=60s %=8s %=10s", "Message name", "MsgID", "CRC");
1176
1177   for (i = 0; i < vec_len (table); i++)
1178     {
1179       item = table + i;
1180       vlib_cli_output (vm, "%-60s %8u %10s", item->name,
1181                        item->msg_index, item->crc);
1182     }
1183
1184 cleanup:
1185   for (i = 0; i < vec_len (table); i++)
1186     {
1187       vec_free (table[i].name_and_crc);
1188       vec_free (table[i].name);
1189       vec_free (table[i].crc);
1190     }
1191
1192   vec_free (table);
1193
1194   return 0;
1195 }
1196
1197 /*?
1198  * Displays a serialized API message decode table, sorted by message name
1199  *
1200  * @cliexpar
1201  * @cliexstart{show api dump file <filename>}
1202  *                                                Message name    MsgID        CRC
1203  * accept_session                                                    407   8e2a127e
1204  * accept_session_reply                                              408   67d8c22a
1205  * add_node_next                                                     549   e4202993
1206  * add_node_next_reply                                               550   e89d6eed
1207  * etc.
1208  * @cliexend
1209 ?*/
1210
1211 /*?
1212  * Compares a serialized API message decode table with the current image
1213  *
1214  * @cliexpar
1215  * @cliexstart{show api dump file <filename> compare}
1216  * ip_add_del_route                                             definition changed
1217  * ip_table_add_del                                             definition changed
1218  * l2_macs_event                                                only in image
1219  * vnet_ip4_fib_counters                                        only in file
1220  * vnet_ip4_nbr_counters                                        only in file
1221  * @cliexend
1222 ?*/
1223
1224 /*?
1225  * Display a serialized API message decode table, compare a saved
1226  * decode table with the current image, to establish API differences.
1227  *
1228 ?*/
1229 /* *INDENT-OFF* */
1230 VLIB_CLI_COMMAND (dump_api_table_file, static) =
1231 {
1232   .path = "show api dump",
1233   .short_help = "show api dump file <filename> [numeric | compare-current]",
1234   .function = dump_api_table_file_command_fn,
1235 };
1236
1237 /* *INDENT-ON* */
1238 /*
1239  * fd.io coding-style-patch-verification: ON
1240  *
1241  * Local Variables:
1242  * eval: (c-set-style "gnu")
1243  * End:
1244  */