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