api: fix trace_plugin_msg_ids segv in trace dump
[vpp.git] / src / vlibmemory / vlib_api_cli.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2018 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22
23 #include <vlibapi/api.h>
24 #include <vlibmemory/api.h>
25
26 static clib_error_t *
27 vl_api_show_histogram_command (vlib_main_t * vm,
28                                unformat_input_t * input,
29                                vlib_cli_command_t * cli_cmd)
30 {
31   u64 total_counts = 0;
32   int i;
33
34   for (i = 0; i < SLEEP_N_BUCKETS; i++)
35     {
36       total_counts += vector_rate_histogram[i];
37     }
38
39   if (total_counts == 0)
40     {
41       vlib_cli_output (vm, "No control-plane activity.");
42       return 0;
43     }
44
45 #define _(n)                                                    \
46     do {                                                        \
47         f64 percent;                                            \
48         percent = ((f64) vector_rate_histogram[SLEEP_##n##_US]) \
49             / (f64) total_counts;                               \
50         percent *= 100.0;                                       \
51         vlib_cli_output (vm, "Sleep %3d us: %llu, %.2f%%",n,    \
52                          vector_rate_histogram[SLEEP_##n##_US], \
53                          percent);                              \
54     } while (0);
55   foreach_histogram_bucket;
56 #undef _
57
58   return 0;
59 }
60
61 /*?
62  * Display the binary api sleep-time histogram
63 ?*/
64 /* *INDENT-OFF* */
65 VLIB_CLI_COMMAND (cli_show_api_histogram_command, static) =
66 {
67   .path = "show api histogram",
68   .short_help = "show api histogram",
69   .function = vl_api_show_histogram_command,
70 };
71 /* *INDENT-ON* */
72
73 static clib_error_t *
74 vl_api_clear_histogram_command (vlib_main_t * vm,
75                                 unformat_input_t * input,
76                                 vlib_cli_command_t * cli_cmd)
77 {
78   int i;
79
80   for (i = 0; i < SLEEP_N_BUCKETS; i++)
81     vector_rate_histogram[i] = 0;
82   return 0;
83 }
84
85 /*?
86  * Clear the binary api sleep-time histogram
87 ?*/
88 /* *INDENT-OFF* */
89 VLIB_CLI_COMMAND (cli_clear_api_histogram_command, static) =
90 {
91   .path = "clear api histogram",
92   .short_help = "clear api histogram",
93   .function = vl_api_clear_histogram_command,
94 };
95 /* *INDENT-ON* */
96
97 static clib_error_t *
98 vl_api_client_command (vlib_main_t * vm,
99                        unformat_input_t * input, vlib_cli_command_t * cli_cmd)
100 {
101   vl_api_registration_t **regpp, *regp;
102   svm_queue_t *q;
103   char *health;
104   api_main_t *am = vlibapi_get_main ();
105   u32 *confused_indices = 0;
106
107   if (!pool_elts (am->vl_clients))
108     goto socket_clients;
109   vlib_cli_output (vm, "Shared memory clients");
110   vlib_cli_output (vm, "%20s %8s %14s %18s %s",
111                    "Name", "PID", "Queue Length", "Queue VA", "Health");
112
113   /* *INDENT-OFF* */
114   pool_foreach (regpp, am->vl_clients)
115    {
116     regp = *regpp;
117
118     if (regp)
119       {
120         if (regp->unanswered_pings > 0)
121           health = "questionable";
122         else
123           health = "OK";
124
125         q = regp->vl_input_queue;
126
127         vlib_cli_output (vm, "%20s %8d %14d 0x%016llx %s\n",
128                          regp->name, q->consumer_pid, q->cursize,
129                          q, health);
130       }
131     else
132       {
133         clib_warning ("NULL client registration index %d",
134                       regpp - am->vl_clients);
135         vec_add1 (confused_indices, regpp - am->vl_clients);
136       }
137   }
138   /* *INDENT-ON* */
139
140   /* This should "never happen," but if it does, fix it... */
141   if (PREDICT_FALSE (vec_len (confused_indices) > 0))
142     {
143       int i;
144       for (i = 0; i < vec_len (confused_indices); i++)
145         {
146           pool_put_index (am->vl_clients, confused_indices[i]);
147         }
148     }
149   vec_free (confused_indices);
150
151   if (am->missing_clients)
152     vlib_cli_output (vm, "%u messages with missing clients",
153                      am->missing_clients);
154 socket_clients:
155   vl_sock_api_dump_clients (vm, am);
156
157   return 0;
158 }
159
160 static clib_error_t *
161 vl_api_status_command (vlib_main_t * vm,
162                        unformat_input_t * input, vlib_cli_command_t * cli_cmd)
163 {
164   api_main_t *am = vlibapi_get_main ();
165
166   /* check if rx_trace and tx_trace are not null pointers */
167   if (am->rx_trace == 0)
168     {
169       vlib_cli_output (vm, "RX Trace disabled\n");
170     }
171   else
172     {
173       if (am->rx_trace->enabled == 0)
174         vlib_cli_output (vm, "RX Trace disabled\n");
175       else
176         vlib_cli_output (vm, "RX Trace enabled\n");
177     }
178
179   if (am->tx_trace == 0)
180     {
181       vlib_cli_output (vm, "TX Trace disabled\n");
182     }
183   else
184     {
185       if (am->tx_trace->enabled == 0)
186         vlib_cli_output (vm, "TX Trace disabled\n");
187       else
188         vlib_cli_output (vm, "TX Trace enabled\n");
189     }
190
191   return 0;
192 }
193
194 /* *INDENT-OFF* */
195 VLIB_CLI_COMMAND (cli_show_api_command, static) =
196 {
197   .path = "show api",
198   .short_help = "Show API information",
199 };
200 /* *INDENT-ON* */
201
202 /*?
203  * Display current api client connections
204 ?*/
205 /* *INDENT-OFF* */
206 VLIB_CLI_COMMAND (cli_show_api_clients_command, static) =
207 {
208   .path = "show api clients",
209   .short_help = "Client information",
210   .function = vl_api_client_command,
211 };
212 /* *INDENT-ON* */
213
214 /*?
215  * Display the current api message tracing status
216 ?*/
217 /* *INDENT-OFF* */
218 VLIB_CLI_COMMAND (cli_show_api_status_command, static) =
219 {
220   .path = "show api trace-status",
221   .short_help = "Display API trace status",
222   .function = vl_api_status_command,
223 };
224 /* *INDENT-ON* */
225
226 static clib_error_t *
227 vl_api_message_table_command (vlib_main_t * vm,
228                               unformat_input_t * input,
229                               vlib_cli_command_t * cli_cmd)
230 {
231   api_main_t *am = vlibapi_get_main ();
232   int i;
233   int verbose = 0;
234
235   if (unformat (input, "verbose"))
236     verbose = 1;
237
238
239   if (verbose == 0)
240     vlib_cli_output (vm, "%-4s %s", "ID", "Name");
241   else
242     vlib_cli_output (vm, "%-4s %-40s %6s %7s", "ID", "Name", "Bounce",
243                      "MP-safe");
244
245   for (i = 1; i < vec_len (am->msg_data); i++)
246     {
247       vl_api_msg_data_t *m = vl_api_get_msg_data (am, i);
248       if (verbose == 0)
249         {
250           vlib_cli_output (vm, "%-4d %s", i,
251                            m->name ? m->name : "  [no handler]");
252         }
253       else
254         {
255           vlib_cli_output (vm, "%-4d %-40s %6d %7d", i,
256                            m->name ? m->name : "  [no handler]", m->bounce,
257                            m->is_mp_safe);
258         }
259     }
260
261   return 0;
262 }
263
264 /*?
265  * Display the current api message decode tables
266 ?*/
267 /* *INDENT-OFF* */
268 VLIB_CLI_COMMAND (cli_show_api_message_table_command, static) =
269 {
270   .path = "show api message-table",
271   .short_help = "Message Table",
272   .function = vl_api_message_table_command,
273 };
274 /* *INDENT-ON* */
275
276 static int
277 range_compare (vl_api_msg_range_t * a0, vl_api_msg_range_t * a1)
278 {
279   int len0, len1, clen;
280
281   len0 = vec_len (a0->name);
282   len1 = vec_len (a1->name);
283   clen = len0 < len1 ? len0 : len1;
284   return (strncmp ((char *) a0->name, (char *) a1->name, clen));
285 }
286
287 static u8 *
288 format_api_msg_range (u8 * s, va_list * args)
289 {
290   vl_api_msg_range_t *rp = va_arg (*args, vl_api_msg_range_t *);
291
292   if (rp == 0)
293     s = format (s, "%-50s%9s%9s", "Name", "First-ID", "Last-ID");
294   else
295     s = format (s, "%-50s%9d%9d", rp->name, rp->first_msg_id,
296                 rp->last_msg_id);
297
298   return s;
299 }
300
301 static clib_error_t *
302 vl_api_show_plugin_command (vlib_main_t * vm,
303                             unformat_input_t * input,
304                             vlib_cli_command_t * cli_cmd)
305 {
306   api_main_t *am = vlibapi_get_main ();
307   vl_api_msg_range_t *rp = 0;
308   int i;
309
310   if (vec_len (am->msg_ranges) == 0)
311     {
312       vlib_cli_output (vm, "No plugin API message ranges configured...");
313       return 0;
314     }
315
316   rp = vec_dup (am->msg_ranges);
317
318   vec_sort_with_function (rp, range_compare);
319
320   vlib_cli_output (vm, "Plugin API message ID ranges...\n");
321   vlib_cli_output (vm, "%U", format_api_msg_range, 0 /* header */ );
322
323   for (i = 0; i < vec_len (rp); i++)
324     vlib_cli_output (vm, "%U", format_api_msg_range, rp + i);
325
326   vec_free (rp);
327
328   return 0;
329 }
330
331 /*?
332  * Display the plugin binary API message range table
333 ?*/
334 /* *INDENT-OFF* */
335 VLIB_CLI_COMMAND (cli_show_api_plugin_command, static) =
336 {
337   .path = "show api plugin",
338   .short_help = "show api plugin",
339   .function = vl_api_show_plugin_command,
340 };
341 /* *INDENT-ON* */
342
343 typedef enum
344 {
345   DUMP,
346   DUMP_JSON,
347   REPLAY,
348   INITIALIZERS,
349 } vl_api_replay_t;
350
351 u8 *
352 format_vl_msg_api_trace_status (u8 * s, va_list * args)
353 {
354   api_main_t *am = va_arg (*args, api_main_t *);
355   vl_api_trace_which_t which = va_arg (*args, vl_api_trace_which_t);
356   vl_api_trace_t *tp;
357   char *trace_name;
358
359   switch (which)
360     {
361     case VL_API_TRACE_TX:
362       tp = am->tx_trace;
363       trace_name = "TX trace";
364       break;
365
366     case VL_API_TRACE_RX:
367       tp = am->rx_trace;
368       trace_name = "RX trace";
369       break;
370
371     default:
372       abort ();
373     }
374
375   if (tp == 0)
376     {
377       s = format (s, "%s: not yet configured.\n", trace_name);
378       return s;
379     }
380
381   s = format (s, "%s: used %d of %d items, %s enabled, %s wrapped\n",
382               trace_name, vec_len (tp->traces), tp->nitems,
383               tp->enabled ? "is" : "is not", tp->wrapped ? "has" : "has not");
384   return s;
385 }
386
387 static void
388 vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
389                          u32 first_index, u32 last_index,
390                          vl_api_replay_t which)
391 {
392   vl_api_trace_file_header_t *hp;
393   int i, fd;
394   u16 *msgid_vec = 0;
395   struct stat statb;
396   size_t file_size;
397   u8 *msg;
398   api_main_t *am = vlibapi_get_main ();
399   u8 *tmpbuf = 0;
400   u32 nitems, nitems_msgtbl;
401
402   fd = open ((char *) filename, O_RDONLY);
403
404   if (fd < 0)
405     {
406       vlib_cli_output (vm, "Couldn't open %s\n", filename);
407       return;
408     }
409
410   if (fstat (fd, &statb) < 0)
411     {
412       vlib_cli_output (vm, "Couldn't stat %s\n", filename);
413       close (fd);
414       return;
415     }
416
417   if (!(statb.st_mode & S_IFREG) || (statb.st_size < sizeof (*hp)))
418     {
419       vlib_cli_output (vm, "File not plausible: %s\n", filename);
420       close (fd);
421       return;
422     }
423
424   file_size = statb.st_size;
425   file_size = (file_size + 4095) & ~(4095);
426
427   hp = mmap (0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
428
429   if (hp == (vl_api_trace_file_header_t *) MAP_FAILED)
430     {
431       vlib_cli_output (vm, "mmap failed: %s\n", filename);
432       close (fd);
433       return;
434     }
435   close (fd);
436
437   clib_mem_unpoison (hp, file_size);
438
439   nitems = ntohl (hp->nitems);
440
441   if (last_index == (u32) ~ 0)
442     {
443       last_index = nitems - 1;
444     }
445
446   if (first_index >= nitems || last_index >= nitems)
447     {
448       vlib_cli_output (vm, "Range (%d, %d) outside file range (0, %d)\n",
449                        first_index, last_index, nitems - 1);
450       munmap (hp, file_size);
451       return;
452     }
453   if (hp->wrapped)
454     vlib_cli_output (vm,
455                      "Note: wrapped/incomplete trace, results may vary\n");
456
457   size_t file_size_left = file_size;
458
459 #define assert_size(size_left, s)                                             \
460   do                                                                          \
461     {                                                                         \
462       if ((s) >= size_left)                                                   \
463         {                                                                     \
464           vlib_cli_output (vm, "corrupted file");                             \
465           munmap (hp, file_size);                                             \
466           vec_free (msgid_vec);                                               \
467           return;                                                             \
468         }                                                                     \
469       size_left -= s;                                                         \
470     }                                                                         \
471   while (0);
472
473   assert_size (file_size_left, sizeof (hp[0]));
474   msg = (u8 *) (hp + 1);
475
476   serialize_main_t _sm, *sm = &_sm;
477   u32 msgtbl_size = ntohl (hp->msgtbl_size);
478   u8 *name_and_crc;
479
480   assert_size (file_size_left, msgtbl_size);
481
482   unserialize_open_data (sm, msg, msgtbl_size);
483   unserialize_integer (sm, &nitems_msgtbl, sizeof (u32));
484
485   for (i = 0; i < nitems_msgtbl; i++)
486     {
487       u16 msg_index = unserialize_likely_small_unsigned_integer (sm);
488       unserialize_cstring (sm, (char **) &name_and_crc);
489       u32 msg_index2 = vl_msg_api_get_msg_index (name_and_crc);
490       ASSERT (~0 == msg_index2 || msg_index2 <= 65535);
491       if (~0 == msg_index2)
492         vlib_cli_output (vm, "warning: can't find msg index for id %d\n",
493                          msg_index);
494       vec_validate (msgid_vec, msg_index);
495       msgid_vec[msg_index] = msg_index2;
496     }
497
498   msg += msgtbl_size;
499
500   for (i = 0; i < first_index; i++)
501     {
502       int size;
503       u16 msg_id;
504
505       assert_size (file_size_left, sizeof (u32));
506       size = clib_host_to_net_u32 (*(u32 *) msg);
507       msg += sizeof (u32);
508
509       assert_size (file_size_left, clib_max (size, sizeof (u16)));
510       msg_id = ntohs (*((u16 *) msg));
511       if (msg_id >= vec_len (msgid_vec) ||
512           msgid_vec[msg_id] >= vec_len (am->msg_data))
513         vlib_cli_output (vm, "warning: unknown msg id %d for msg number %d\n",
514                          msg_id, i);
515
516       msg += size;
517     }
518
519   if (which == REPLAY)
520     am->replay_in_progress = 1;
521
522   for (; i <= last_index; i++)
523     {
524       vl_api_msg_data_t *m;
525       u16 msg_id;
526       int size;
527
528       if (which == DUMP)
529         vlib_cli_output (vm, "---------- trace %d -----------\n", i);
530
531       assert_size (file_size_left, sizeof (u32));
532       size = clib_host_to_net_u32 (*(u32 *) msg);
533       msg += sizeof (u32);
534
535       assert_size (file_size_left, clib_max (size, sizeof (u16)));
536       msg_id = ntohs (*((u16 *) msg));
537
538       if (msg_id >= vec_len (msgid_vec) ||
539           msgid_vec[msg_id] >= vec_len (am->msg_data))
540         {
541           vlib_cli_output (
542             vm, "warning: unknown msg id %d for msg number %d, skipping\n",
543             msg_id, i);
544           msg += size;
545           continue;
546         }
547
548       msg_id = msgid_vec[msg_id];
549       m = vl_api_get_msg_data (am, msg_id);
550
551       /* Copy the buffer (from the read-only mmap'ed file) */
552       vec_validate (tmpbuf, size - 1 + sizeof (uword));
553       clib_memcpy (tmpbuf + sizeof (uword), msg, size);
554       clib_memset (tmpbuf, 0xf, sizeof (uword));
555
556       /*
557        * Endian swap if needed. All msg data is supposed to be in
558        * network byte order.
559        */
560       if (((which == DUMP || which == DUMP_JSON) &&
561            clib_arch_is_little_endian))
562         {
563           if (m && m->endian_handler == 0)
564             {
565               vlib_cli_output (vm, "Ugh: msg id %d no endian swap\n", msg_id);
566               munmap (hp, file_size);
567               vec_free (tmpbuf);
568               am->replay_in_progress = 0;
569               return;
570             }
571           if (m)
572             {
573               m->endian_handler (tmpbuf + sizeof (uword));
574             }
575         }
576
577       /* msg_id always in network byte order */
578       if (clib_arch_is_little_endian)
579         {
580           u16 *msg_idp = (u16 *) (tmpbuf + sizeof (uword));
581           *msg_idp = msg_id;
582         }
583
584       switch (which)
585         {
586         case DUMP_JSON:
587           vlib_cli_output (vm, "%U", format_vl_api_msg_json, am, msg_id,
588                            tmpbuf + sizeof (uword));
589           break;
590
591         case DUMP:
592           vlib_cli_output (vm, "%U", format_vl_api_msg_text, am, msg_id,
593                            tmpbuf + sizeof (uword));
594           break;
595
596         case INITIALIZERS:
597           if (m)
598             {
599               u8 *s;
600               int j;
601
602               vlib_cli_output (vm, "/*%U*/", format_vl_api_msg_text, am,
603                                msg_id, tmpbuf + sizeof (uword));
604
605               vlib_cli_output (vm, "*/\n");
606
607               s = format (0, "static u8 * vl_api_%s_%d[%d] = {", m->name, i,
608                           m->trace_size);
609
610               for (j = 0; j < m->trace_size; j++)
611                 {
612                   if ((j & 7) == 0)
613                     s = format (s, "\n    ");
614                   s = format (s, "0x%02x,", tmpbuf[sizeof (uword) + j]);
615                 }
616               s = format (s, "\n};\n%c", 0);
617               vlib_cli_output (vm, (char *) s);
618               vec_free (s);
619             }
620           break;
621
622         case REPLAY:
623           if (m && m->handler && m->replay_allowed)
624             {
625               if (!m->is_mp_safe)
626                 vl_msg_api_barrier_sync ();
627               m->handler (tmpbuf + sizeof (uword));
628               if (!m->is_mp_safe)
629                 vl_msg_api_barrier_release ();
630             }
631           else
632             {
633               if (m && m->replay_allowed)
634                 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
635                                  msg_id);
636               break;
637             }
638           break;
639         }
640
641       vec_set_len (tmpbuf, 0);
642       msg += size;
643     }
644
645   munmap (hp, file_size);
646   vec_free (tmpbuf);
647   vec_free (msgid_vec);
648   am->replay_in_progress = 0;
649 }
650
651 static int
652 file_exists (u8 *fname)
653 {
654   FILE *fp = 0;
655   fp = fopen ((char *) fname, "r");
656   if (fp)
657     {
658       fclose (fp);
659       return 1;
660     }
661   return 0;
662 }
663
664 typedef struct
665 {
666   vlib_main_t *vm;
667   u8 is_json;
668 } vl_msg_print_args;
669
670 static int
671 vl_msg_print_trace (u8 *msg, void *ctx)
672 {
673   vl_msg_print_args *a = ctx;
674   api_main_t *am = vlibapi_get_main ();
675   u16 msg_id = ntohs (*((u16 *) msg));
676   vl_api_msg_data_t *m = vl_api_get_msg_data (am, msg_id);
677   u8 is_json = a->is_json;
678   u8 *tmpbuf = 0;
679
680   if (!m)
681     {
682       vlib_cli_output (a->vm, "Unknown msg id %d\n", msg_id);
683       return 0;
684     }
685
686   if (clib_arch_is_little_endian && (m->endian_handler != NULL))
687     {
688       u32 msg_length = vec_len (msg);
689       vec_validate (tmpbuf, msg_length - 1);
690       clib_memcpy_fast (tmpbuf, msg, msg_length);
691       msg = tmpbuf;
692
693       m->endian_handler (tmpbuf);
694     }
695
696   vlib_cli_output (a->vm, "%U\n",
697                    is_json ? format_vl_api_msg_json : format_vl_api_msg_text,
698                    am, msg_id, msg);
699
700   vec_free (tmpbuf);
701   return 0;
702 }
703
704 static int
705 vl_msg_api_dump_trace (vlib_main_t *vm, vl_api_trace_which_t which, u8 is_json)
706 {
707   api_main_t *am = vlibapi_get_main ();
708   vl_api_trace_t *tp;
709
710   switch (which)
711     {
712     case VL_API_TRACE_TX:
713       tp = am->tx_trace;
714       break;
715     case VL_API_TRACE_RX:
716       tp = am->rx_trace;
717       break;
718     default:
719       return -1;
720     }
721
722   if (tp == 0 || tp->nitems == 0 || vec_len (tp->traces) == 0)
723     return -1;
724
725   vl_msg_print_args args;
726   clib_memset (&args, 0, sizeof (args));
727   args.is_json = is_json;
728   args.vm = vm;
729   vl_msg_traverse_trace (tp, vl_msg_print_trace, &args);
730
731   return 0;
732 }
733
734 static char *
735 vl_msg_read_file (FILE *f)
736 {
737   const size_t bufsize = 1024;
738   char *buf[bufsize], *v = 0;
739   size_t n;
740
741   while ((n = fread (buf, 1, bufsize, f)))
742     vec_add (v, buf, n);
743
744   /* most callers expect a NULL-terminated C-string */
745   if (v)
746     vec_add1 (v, 0);
747
748   return v;
749 }
750
751 static u16
752 vl_msg_find_id_by_name_and_crc (vlib_main_t *vm, api_main_t *am, char *name)
753 {
754   uword *p;
755   p = hash_get_mem (am->msg_index_by_name_and_crc, name);
756   if (!p)
757     return (u16) ~0;
758
759   return p[0];
760 }
761
762 static u16
763 vl_msg_find_id_by_name (vlib_main_t *vm, api_main_t *am, char *name)
764 {
765   uword *p;
766
767   if (!am->msg_id_by_name)
768     {
769       vlib_cli_output (vm, "message id table not yet initialized!\n");
770       return (u16) ~0;
771     }
772
773   p = hash_get_mem (am->msg_id_by_name, name);
774   if (!p)
775     return (u16) ~0;
776
777   return p[0];
778 }
779
780 static int
781 vl_msg_exec_json_command (vlib_main_t *vm, cJSON *o)
782 {
783   api_main_t *am = vlibapi_get_main ();
784   u16 msg_id;
785   int len = 0, rv = -1;
786   vl_api_msg_data_t *m;
787   u8 *msg = 0;
788
789   cJSON *msg_id_obj = cJSON_GetObjectItem (o, "_msgname");
790   if (!msg_id_obj)
791     {
792       vlib_cli_output (vm, "Missing '_msgname' element!\n");
793       return rv;
794     }
795   char *name = cJSON_GetStringValue (msg_id_obj);
796
797   cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc");
798   if (!crc_obj)
799     {
800       vlib_cli_output (vm, "Missing '_crc' element!\n");
801       return rv;
802     }
803   char *crc = cJSON_GetStringValue (crc_obj);
804   u8 proc_warning = 0;
805
806   u8 *name_crc = format (0, "%s_%s%c", name, crc, 0);
807   msg_id = vl_msg_find_id_by_name_and_crc (vm, am, (char *) name_crc);
808   m = vl_api_get_msg_data (am, msg_id);
809   if (msg_id == (u16) ~0)
810     {
811       msg_id = vl_msg_find_id_by_name (vm, am, name);
812       if (msg_id == (u16) ~0)
813         {
814           vlib_cli_output (vm, "unknown msg id %d!\n", msg_id);
815           vec_free (name_crc);
816           return rv;
817         }
818       proc_warning = 1;
819     }
820   vec_free (name_crc);
821
822   if (m->replay_allowed)
823     {
824       if (proc_warning)
825         vlib_cli_output (vm, "warning: msg %d has different signature\n");
826
827       if (!m->fromjson_handler)
828         {
829           vlib_cli_output (vm, "missing fromjson convert function! id %d\n",
830                            msg_id);
831           return rv;
832         }
833
834       msg = (u8 *) m->fromjson_handler (o, &len);
835       if (!msg)
836         {
837           vlib_cli_output (vm, "failed to convert JSON (msg id %d)!\n",
838                            msg_id);
839           return rv;
840         }
841
842       if (clib_arch_is_little_endian)
843         m->endian_handler (msg);
844
845       if (!m->handler)
846         {
847           vlib_cli_output (vm, "no handler for msg id %d!\n", msg_id);
848           goto end;
849         }
850
851       if (m->handler)
852         {
853           if (!m->is_mp_safe)
854             vl_msg_api_barrier_sync ();
855           m->handler (msg);
856           if (!m->is_mp_safe)
857             vl_msg_api_barrier_release ();
858         }
859     }
860
861   rv = 0;
862 end:
863   if (msg)
864     cJSON_free (msg);
865   return rv;
866 }
867
868 static void
869 vl_msg_replay_json (vlib_main_t *vm, u8 *filename)
870 {
871   api_main_t *am = vlibapi_get_main ();
872   cJSON *o = 0;
873   int rv = 0;
874   FILE *f = fopen ((char *) filename, "r");
875
876   if (!f)
877     {
878       vlib_cli_output (vm, "failed to open %s!\n", filename);
879       return;
880     }
881
882   char *buf = vl_msg_read_file (f);
883   fclose (f);
884
885   o = cJSON_Parse (buf);
886   vec_free (buf);
887   if (!o)
888     {
889       vlib_cli_output (vm, "%s: Failed parsing JSON input: %s\n", filename,
890                        cJSON_GetErrorPtr ());
891       return;
892     }
893
894   if (cJSON_IsArray (o))
895     {
896       am->replay_in_progress = 1;
897       size_t size = cJSON_GetArraySize (o);
898       for (int i = 0; i < size; i++)
899         {
900           rv = vl_msg_exec_json_command (vm, cJSON_GetArrayItem (o, i));
901           if (rv < 0)
902             {
903               am->replay_in_progress = 0;
904               break;
905             }
906         }
907     }
908   else
909     {
910       rv = vl_msg_exec_json_command (vm, o);
911     }
912
913   if (rv < 0)
914     vlib_cli_output (vm, "error during replaying API trace");
915
916   cJSON_Delete (o);
917 }
918
919 static void
920 vl_msg_dump_file_json (vlib_main_t *vm, u8 *filename)
921 {
922   FILE *f = fopen ((char *) filename, "r");
923   char *buf;
924
925   if (!f)
926     {
927       vlib_cli_output (vm, "failed to open %s!\n", filename);
928       return;
929     }
930
931   buf = vl_msg_read_file (f);
932   fclose (f);
933
934   if (!buf)
935     {
936       vlib_cli_output (vm, "no content in %s!\n", filename);
937       return;
938     }
939
940   vlib_cli_output (vm, buf);
941   vec_free (buf);
942 }
943
944 /** api_trace_command_fn - control the binary API trace / replay feature
945
946     Note: this command MUST be marked thread-safe. Replay with
947     multiple worker threads depends in many cases on worker thread
948     graph replica maintenance. If we (implicitly) assert a worker
949     thread barrier at the debug CLI level, all graph replica changes
950     are deferred until the replay operation completes. If an interface
951     is deleted, the wheels fall off.
952  */
953
954 static clib_error_t *
955 api_trace_command_fn (vlib_main_t * vm,
956                       unformat_input_t * input, vlib_cli_command_t * cmd)
957 {
958   unformat_input_t _line_input, *line_input = &_line_input;
959   u32 nitems = 256 << 10;
960   api_main_t *am = vlibapi_get_main ();
961   vl_api_trace_which_t which = VL_API_TRACE_RX;
962   u8 *filename = 0;
963   u8 *chroot_filename = 0;
964   u32 first = 0;
965   u32 last = (u32) ~ 0;
966   FILE *fp;
967   int rv;
968
969   /* Get a line of input. */
970   if (!unformat_user (input, unformat_line_input, line_input))
971     return 0;
972
973   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
974     {
975       if (unformat (line_input, "on") || unformat (line_input, "enable"))
976         {
977           if (unformat (line_input, "nitems %d", &nitems))
978             ;
979           vlib_worker_thread_barrier_sync (vm);
980           vl_msg_api_trace_configure (am, which, nitems);
981           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
982           vlib_worker_thread_barrier_release (vm);
983         }
984       else if (unformat (line_input, "off"))
985         {
986           vlib_worker_thread_barrier_sync (vm);
987           vl_msg_api_trace_onoff (am, which, 0);
988           vlib_worker_thread_barrier_release (vm);
989         }
990       else if (unformat (line_input, "save-json %s", &filename))
991         {
992           if (strstr ((char *) filename, "..") ||
993               index ((char *) filename, '/'))
994             {
995               vlib_cli_output (vm, "illegal characters in filename '%s'",
996                                filename);
997               goto out;
998             }
999
1000           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1001
1002           vec_free (filename);
1003
1004           if (file_exists (chroot_filename))
1005             {
1006               vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1007               goto out;
1008             }
1009
1010           fp = fopen ((char *) chroot_filename, "w");
1011           if (fp == NULL)
1012             {
1013               vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
1014               goto out;
1015             }
1016           vlib_worker_thread_barrier_sync (vm);
1017           rv = vl_msg_api_trace_save (am, which, fp, 1);
1018           if (rv == -1)
1019             vlib_cli_output (vm, "API Trace data not present\n");
1020           else if (rv < 0)
1021             vlib_cli_output (vm, "failed to save api trace\n");
1022           else
1023             vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
1024           vlib_worker_thread_barrier_release (vm);
1025           fclose (fp);
1026         }
1027       else if (unformat (line_input, "save %s", &filename))
1028         {
1029           if (strstr ((char *) filename, "..")
1030               || index ((char *) filename, '/'))
1031             {
1032               vlib_cli_output (vm, "illegal characters in filename '%s'",
1033                                filename);
1034               goto out;
1035             }
1036
1037           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1038
1039           vec_free (filename);
1040
1041           if (file_exists (chroot_filename))
1042             {
1043               vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1044               goto out;
1045             }
1046
1047           fp = fopen ((char *) chroot_filename, "w");
1048           if (fp == NULL)
1049             {
1050               vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
1051               goto out;
1052             }
1053           vlib_worker_thread_barrier_sync (vm);
1054           rv = vl_msg_api_trace_save (am, which, fp, 0);
1055           vlib_worker_thread_barrier_release (vm);
1056           fclose (fp);
1057           if (rv == -1)
1058             vlib_cli_output (vm, "API Trace data not present\n");
1059           else if (rv == -2)
1060             vlib_cli_output (vm, "File for writing is closed\n");
1061           else if (rv == -10)
1062             vlib_cli_output (vm, "Error while writing header to file\n");
1063           else if (rv == -11)
1064             vlib_cli_output (vm, "Error while writing trace to file\n");
1065           else if (rv == -12)
1066             vlib_cli_output (vm,
1067                              "Error while writing end of buffer trace to file\n");
1068           else if (rv == -13)
1069             vlib_cli_output (vm,
1070                              "Error while writing start of buffer trace to file\n");
1071           else if (rv < 0)
1072             vlib_cli_output (vm, "Unknown error while saving: %d", rv);
1073           else
1074             vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
1075           goto out;
1076         }
1077       else if (unformat (line_input, "tojson %s", &filename))
1078         {
1079           vl_msg_api_process_file (vm, filename, first, last, DUMP_JSON);
1080         }
1081       else if (unformat (line_input, "dump-file-json %s", &filename))
1082         {
1083           vl_msg_dump_file_json (vm, filename);
1084         }
1085       else if (unformat (line_input, "dump-file %s", &filename))
1086         {
1087           vl_msg_api_process_file (vm, filename, first, last, DUMP);
1088         }
1089       else if (unformat (line_input, "dump-json"))
1090         {
1091           vl_msg_api_dump_trace (vm, which, 1);
1092         }
1093       else if (unformat (line_input, "dump"))
1094         {
1095           vl_msg_api_dump_trace (vm, which, 0);
1096         }
1097       else if (unformat (line_input, "replay-json %s", &filename))
1098         {
1099           vl_msg_replay_json (vm, filename);
1100         }
1101       else if (unformat (line_input, "replay %s", &filename))
1102         {
1103           vl_msg_api_process_file (vm, filename, first, last, REPLAY);
1104         }
1105       else if (unformat (line_input, "initializers %s", &filename))
1106         {
1107           vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
1108         }
1109       else if (unformat (line_input, "tx"))
1110         {
1111           which = VL_API_TRACE_TX;
1112         }
1113       else if (unformat (line_input, "first %d", &first))
1114         {
1115           ;
1116         }
1117       else if (unformat (line_input, "last %d", &last))
1118         {
1119           ;
1120         }
1121       else if (unformat (line_input, "status"))
1122         {
1123           vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
1124                            am, which);
1125         }
1126       else if (unformat (line_input, "free"))
1127         {
1128           vlib_worker_thread_barrier_sync (vm);
1129           vl_msg_api_trace_onoff (am, which, 0);
1130           vl_msg_api_trace_free (am, which);
1131           vlib_worker_thread_barrier_release (vm);
1132         }
1133       else if (unformat (line_input, "post-mortem-on"))
1134         vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1135       else if (unformat (line_input, "post-mortem-off"))
1136         vl_msg_api_post_mortem_dump_enable_disable (0 /* enable */ );
1137       else
1138         return clib_error_return (0, "unknown input `%U'",
1139                                   format_unformat_error, input);
1140     }
1141 out:
1142   vec_free (filename);
1143   vec_free (chroot_filename);
1144   unformat_free (line_input);
1145   return 0;
1146 }
1147
1148 /*?
1149  * Display, replay, or save a binary API trace
1150 ?*/
1151
1152 /* *INDENT-OFF* */
1153 VLIB_CLI_COMMAND (api_trace_command, static) = {
1154   .path = "api trace",
1155   .short_help = "api trace [tx][on|off][first <n>][last <n>][status][free]"
1156                 "[post-mortem-on][dump|dump-file|dump-json|save|tojson|save-"
1157                 "json|replay <file>|replay-json <file>][nitems <n>]"
1158                 "[initializers <file>]",
1159   .function = api_trace_command_fn,
1160   .is_mp_safe = 1,
1161 };
1162 /* *INDENT-ON* */
1163
1164 static clib_error_t *
1165 api_trace_config_fn (vlib_main_t * vm, unformat_input_t * input)
1166 {
1167   u32 nitems = 256 << 10;
1168   vl_api_trace_which_t which = VL_API_TRACE_RX;
1169   api_main_t *am = vlibapi_get_main ();
1170
1171   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1172     {
1173       if (unformat (input, "on") || unformat (input, "enable"))
1174         {
1175           if (unformat (input, "nitems %d", &nitems))
1176             ;
1177           vl_msg_api_trace_configure (am, which, nitems);
1178           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
1179           vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1180         }
1181       else if (unformat (input, "save-api-table %s",
1182                          &am->save_msg_table_filename))
1183         ;
1184       else
1185         return clib_error_return (0, "unknown input `%U'",
1186                                   format_unformat_error, input);
1187     }
1188   return 0;
1189 }
1190
1191 /*?
1192  * This module has three configuration parameters:
1193  * "on" or "enable" - enables binary api tracing
1194  * "nitems <nnn>" - sets the size of the circular buffer to <nnn>
1195  * "save-api-table <filename>" - dumps the API message table to /tmp/<filename>
1196 ?*/
1197 VLIB_CONFIG_FUNCTION (api_trace_config_fn, "api-trace");
1198
1199 static clib_error_t *
1200 api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
1201 {
1202   api_main_t *am = vlibapi_get_main ();
1203   u32 nitems;
1204
1205   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1206     {
1207       if (unformat (input, "length %d", &nitems) ||
1208           (unformat (input, "len %d", &nitems)))
1209         {
1210           if (nitems >= 1024)
1211             am->vlib_input_queue_length = nitems;
1212           else
1213             clib_warning ("vlib input queue length %d too small, ignored",
1214                           nitems);
1215         }
1216       else
1217         return clib_error_return (0, "unknown input `%U'",
1218                                   format_unformat_error, input);
1219     }
1220   return 0;
1221 }
1222
1223 VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
1224
1225 static u8 *
1226 extract_name (u8 * s)
1227 {
1228   u8 *rv;
1229
1230   rv = vec_dup (s);
1231
1232   while (vec_len (rv) && rv[vec_len (rv)] != '_')
1233     vec_dec_len (rv, 1);
1234
1235   rv[vec_len (rv)] = 0;
1236
1237   return rv;
1238 }
1239
1240 static u8 *
1241 extract_crc (u8 * s)
1242 {
1243   int i;
1244   u8 *rv;
1245
1246   rv = vec_dup (s);
1247
1248   for (i = vec_len (rv) - 1; i >= 0; i--)
1249     {
1250       if (rv[i] == '_')
1251         {
1252           vec_delete (rv, i + 1, 0);
1253           break;
1254         }
1255     }
1256   return rv;
1257 }
1258
1259 typedef struct
1260 {
1261   u8 *name_and_crc;
1262   u8 *name;
1263   u8 *crc;
1264   u32 msg_index;
1265   int which;
1266 } msg_table_unserialize_t;
1267
1268 static int
1269 table_id_cmp (void *a1, void *a2)
1270 {
1271   msg_table_unserialize_t *n1 = a1;
1272   msg_table_unserialize_t *n2 = a2;
1273
1274   return (n1->msg_index - n2->msg_index);
1275 }
1276
1277 static int
1278 table_name_and_crc_cmp (void *a1, void *a2)
1279 {
1280   msg_table_unserialize_t *n1 = a1;
1281   msg_table_unserialize_t *n2 = a2;
1282
1283   return strcmp ((char *) n1->name_and_crc, (char *) n2->name_and_crc);
1284 }
1285
1286 static clib_error_t *
1287 dump_api_table_file_command_fn (vlib_main_t * vm,
1288                                 unformat_input_t * input,
1289                                 vlib_cli_command_t * cmd)
1290 {
1291   u8 *filename = 0;
1292   api_main_t *am = vlibapi_get_main ();
1293   serialize_main_t _sm, *sm = &_sm;
1294   clib_error_t *error;
1295   u32 nmsgs;
1296   u32 msg_index;
1297   u8 *name_and_crc;
1298   int compare_current = 0;
1299   int numeric_sort = 0;
1300   msg_table_unserialize_t *table = 0, *item;
1301   u32 i;
1302   u32 ndifferences = 0;
1303
1304   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1305     {
1306       if (unformat (input, "file %s", &filename))
1307         ;
1308       else if (unformat (input, "compare-current")
1309                || unformat (input, "compare"))
1310         compare_current = 1;
1311       else if (unformat (input, "numeric"))
1312         numeric_sort = 1;
1313       else
1314         return clib_error_return (0, "unknown input `%U'",
1315                                   format_unformat_error, input);
1316     }
1317
1318   if (numeric_sort && compare_current)
1319     return clib_error_return
1320       (0, "Comparison and numeric sorting are incompatible");
1321
1322   if (filename == 0)
1323     return clib_error_return (0, "File not specified");
1324
1325   /* Load the serialized message table from the table dump */
1326
1327   error = unserialize_open_clib_file (sm, (char *) filename);
1328
1329   if (error)
1330     return error;
1331
1332   unserialize_integer (sm, &nmsgs, sizeof (u32));
1333
1334   for (i = 0; i < nmsgs; i++)
1335     {
1336       msg_index = unserialize_likely_small_unsigned_integer (sm);
1337       unserialize_cstring (sm, (char **) &name_and_crc);
1338       vec_add2 (table, item, 1);
1339       item->msg_index = msg_index;
1340       item->name_and_crc = name_and_crc;
1341       item->name = extract_name (name_and_crc);
1342       item->crc = extract_crc (name_and_crc);
1343       item->which = 0;          /* file */
1344     }
1345   unserialize_close (sm);
1346
1347   /* Compare with the current image? */
1348   if (compare_current)
1349     {
1350       /* Append the current message table */
1351       u8 *tblv = vl_api_serialize_message_table (am, 0);
1352
1353       serialize_open_vector (sm, tblv);
1354       unserialize_integer (sm, &nmsgs, sizeof (u32));
1355
1356       for (i = 0; i < nmsgs; i++)
1357         {
1358           msg_index = unserialize_likely_small_unsigned_integer (sm);
1359           unserialize_cstring (sm, (char **) &name_and_crc);
1360
1361           vec_add2 (table, item, 1);
1362           item->msg_index = msg_index;
1363           item->name_and_crc = name_and_crc;
1364           item->name = extract_name (name_and_crc);
1365           item->crc = extract_crc (name_and_crc);
1366           item->which = 1;      /* current_image */
1367         }
1368       vec_free (tblv);
1369     }
1370
1371   /* Sort the table. */
1372   if (numeric_sort)
1373     vec_sort_with_function (table, table_id_cmp);
1374   else
1375     vec_sort_with_function (table, table_name_and_crc_cmp);
1376
1377   if (compare_current)
1378     {
1379       u8 *dashes = 0;
1380       ndifferences = 0;
1381
1382       /*
1383        * In this case, the recovered table will have two entries per
1384        * API message. So, if entries i and i+1 match, the message definitions
1385        * are identical. Otherwise, the crc is different, or a message is
1386        * present in only one of the tables.
1387        */
1388       vlib_cli_output (vm, "%-60s | %s", "Message Name", "Result");
1389       vec_validate_init_empty (dashes, 60, '-');
1390       vec_terminate_c_string (dashes);
1391       vlib_cli_output (vm, "%60s-|-%s", dashes, "-----------------");
1392       vec_free (dashes);
1393       for (i = 0; i < vec_len (table);)
1394         {
1395           /* Last message lonely? */
1396           if (i == vec_len (table) - 1)
1397             {
1398               ndifferences++;
1399               goto last_unique;
1400             }
1401
1402           /* Identical pair? */
1403           if (!strncmp
1404               ((char *) table[i].name_and_crc,
1405                (char *) table[i + 1].name_and_crc,
1406                vec_len (table[i].name_and_crc)))
1407             {
1408               i += 2;
1409               continue;
1410             }
1411
1412           ndifferences++;
1413
1414           /* Only in one of two tables? */
1415           if (i + 1 == vec_len (table)
1416               || strcmp ((char *) table[i].name, (char *) table[i + 1].name))
1417             {
1418             last_unique:
1419               vlib_cli_output (vm, "%-60s | only in %s",
1420                                table[i].name, table[i].which ?
1421                                "image" : "file");
1422               i++;
1423               continue;
1424             }
1425           /* In both tables, but with different signatures */
1426           vlib_cli_output (vm, "%-60s | definition changed", table[i].name);
1427           i += 2;
1428         }
1429       if (ndifferences == 0)
1430         vlib_cli_output (vm, "No api message signature differences found.");
1431       else
1432         vlib_cli_output (vm, "\nFound %u api message signature differences",
1433                          ndifferences);
1434       goto cleanup;
1435     }
1436
1437   /* Dump the table, sorted as shown above */
1438   vlib_cli_output (vm, "%=60s %=8s %=10s", "Message name", "MsgID", "CRC");
1439
1440   for (i = 0; i < vec_len (table); i++)
1441     {
1442       item = table + i;
1443       vlib_cli_output (vm, "%-60s %8u %10s", item->name,
1444                        item->msg_index, item->crc);
1445     }
1446
1447 cleanup:
1448   for (i = 0; i < vec_len (table); i++)
1449     {
1450       vec_free (table[i].name_and_crc);
1451       vec_free (table[i].name);
1452       vec_free (table[i].crc);
1453     }
1454
1455   vec_free (table);
1456
1457   return 0;
1458 }
1459
1460 /*?
1461  * Displays a serialized API message decode table, sorted by message name
1462  *
1463  * @cliexpar
1464  * @cliexstart{show api dump file <filename>}
1465  *                                                Message name    MsgID        CRC
1466  * accept_session                                                    407   8e2a127e
1467  * accept_session_reply                                              408   67d8c22a
1468  * add_node_next                                                     549   e4202993
1469  * add_node_next_reply                                               550   e89d6eed
1470  * etc.
1471  * @cliexend
1472 ?*/
1473
1474 /*?
1475  * Compares a serialized API message decode table with the current image
1476  *
1477  * @cliexpar
1478  * @cliexstart{show api dump file <filename> compare}
1479  * ip_add_del_route                                             definition changed
1480  * ip_table_add_del                                             definition changed
1481  * l2_macs_event                                                only in image
1482  * vnet_ip4_fib_counters                                        only in file
1483  * vnet_ip4_nbr_counters                                        only in file
1484  * @cliexend
1485 ?*/
1486
1487 /*?
1488  * Display a serialized API message decode table, compare a saved
1489  * decode table with the current image, to establish API differences.
1490  *
1491 ?*/
1492 /* *INDENT-OFF* */
1493 VLIB_CLI_COMMAND (dump_api_table_file, static) =
1494 {
1495   .path = "show api dump",
1496   .short_help = "show api dump file <filename> [numeric | compare-current]",
1497   .function = dump_api_table_file_command_fn,
1498 };
1499
1500 /* *INDENT-ON* */
1501 /*
1502  * fd.io coding-style-patch-verification: ON
1503  *
1504  * Local Variables:
1505  * eval: (c-set-style "gnu")
1506  * End:
1507  */