api: refactor api data storage
[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           m->endian_handler (tmpbuf + sizeof (uword));
572         }
573
574       /* msg_id always in network byte order */
575       if (clib_arch_is_little_endian)
576         {
577           u16 *msg_idp = (u16 *) (tmpbuf + sizeof (uword));
578           *msg_idp = msg_id;
579         }
580
581       switch (which)
582         {
583         case DUMP_JSON:
584           if (m && m->print_json_handler)
585             {
586               m->print_json_handler (tmpbuf + sizeof (uword), vm);
587             }
588           else
589             {
590               vlib_cli_output (vm, "Skipping msg id %d: no JSON print fcn\n",
591                                msg_id);
592               break;
593             }
594           break;
595
596         case DUMP:
597           if (m && m->print_handler)
598             {
599               m->print_handler (tmpbuf + sizeof (uword), vm);
600             }
601           else
602             {
603               vlib_cli_output (vm, "Skipping msg id %d: no print fcn\n",
604                                msg_id);
605               break;
606             }
607           break;
608
609         case INITIALIZERS:
610           if (m && m->print_handler)
611             {
612               u8 *s;
613               int j;
614
615               vlib_cli_output (vm, "/*");
616
617               m->print_handler (tmpbuf + sizeof (uword), vm);
618               vlib_cli_output (vm, "*/\n");
619
620               s = format (0, "static u8 * vl_api_%s_%d[%d] = {", m->name, i,
621                           m->trace_size);
622
623               for (j = 0; j < m->trace_size; j++)
624                 {
625                   if ((j & 7) == 0)
626                     s = format (s, "\n    ");
627                   s = format (s, "0x%02x,", tmpbuf[sizeof (uword) + j]);
628                 }
629               s = format (s, "\n};\n%c", 0);
630               vlib_cli_output (vm, (char *) s);
631               vec_free (s);
632             }
633           break;
634
635         case REPLAY:
636           if (m && m->print_handler && m->replay_allowed)
637             {
638               if (!m->is_mp_safe)
639                 vl_msg_api_barrier_sync ();
640               m->handler (tmpbuf + sizeof (uword));
641               if (!m->is_mp_safe)
642                 vl_msg_api_barrier_release ();
643             }
644           else
645             {
646               if (m->replay_allowed)
647                 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
648                                  msg_id);
649               break;
650             }
651           break;
652         }
653
654       vec_set_len (tmpbuf, 0);
655       msg += size;
656     }
657
658   munmap (hp, file_size);
659   vec_free (tmpbuf);
660   vec_free (msgid_vec);
661   am->replay_in_progress = 0;
662 }
663
664 static int
665 file_exists (u8 *fname)
666 {
667   FILE *fp = 0;
668   fp = fopen ((char *) fname, "r");
669   if (fp)
670     {
671       fclose (fp);
672       return 1;
673     }
674   return 0;
675 }
676
677 typedef struct
678 {
679   vlib_main_t *vm;
680   u8 is_json;
681 } vl_msg_print_args;
682
683 static int
684 vl_msg_print_trace (u8 *msg, void *ctx)
685 {
686   vl_msg_print_args *a = ctx;
687   api_main_t *am = vlibapi_get_main ();
688   u16 msg_id = ntohs (*((u16 *) msg));
689   vl_api_msg_data_t *m = vl_api_get_msg_data (am, msg_id);
690   void (*handler) (void *, void *) = 0;
691   u8 is_json = a->is_json;
692   u8 *tmpbuf = 0;
693
694   if (!m)
695     {
696       vlib_cli_output (a->vm, "Unknown msg id %d\n", msg_id);
697       return 0;
698     }
699
700   if (clib_arch_is_little_endian)
701     {
702       u32 msg_length = vec_len (msg);
703       vec_validate (tmpbuf, msg_length - 1);
704       clib_memcpy_fast (tmpbuf, msg, msg_length);
705       msg = tmpbuf;
706
707       m->endian_handler (tmpbuf);
708     }
709
710   handler = is_json ? m->print_json_handler : m->print_handler;
711
712   if (handler)
713     handler (msg, a->vm);
714   else
715     vlib_cli_output (a->vm, "Skipping msg id %d: no print fcn\n", msg_id);
716
717   vec_free (tmpbuf);
718   return 0;
719 }
720
721 static int
722 vl_msg_api_dump_trace (vlib_main_t *vm, vl_api_trace_which_t which, u8 is_json)
723 {
724   api_main_t *am = vlibapi_get_main ();
725   vl_api_trace_t *tp;
726
727   switch (which)
728     {
729     case VL_API_TRACE_TX:
730       tp = am->tx_trace;
731       break;
732     case VL_API_TRACE_RX:
733       tp = am->rx_trace;
734       break;
735     default:
736       return -1;
737     }
738
739   if (tp == 0 || tp->nitems == 0 || vec_len (tp->traces) == 0)
740     return -1;
741
742   vl_msg_print_args args;
743   clib_memset (&args, 0, sizeof (args));
744   args.is_json = is_json;
745   args.vm = vm;
746   vl_msg_traverse_trace (tp, vl_msg_print_trace, &args);
747
748   return 0;
749 }
750
751 static char *
752 vl_msg_read_file (FILE *f)
753 {
754   const size_t bufsize = 1024;
755   char *buf[bufsize], *v = 0;
756   size_t n;
757
758   while ((n = fread (buf, 1, bufsize, f)))
759     vec_add (v, buf, n);
760
761   /* most callers expect a NULL-terminated C-string */
762   if (v)
763     vec_add1 (v, 0);
764
765   return v;
766 }
767
768 static u16
769 vl_msg_find_id_by_name_and_crc (vlib_main_t *vm, api_main_t *am, char *name)
770 {
771   uword *p;
772   p = hash_get_mem (am->msg_index_by_name_and_crc, name);
773   if (!p)
774     return (u16) ~0;
775
776   return p[0];
777 }
778
779 static u16
780 vl_msg_find_id_by_name (vlib_main_t *vm, api_main_t *am, char *name)
781 {
782   uword *p;
783
784   if (!am->msg_id_by_name)
785     {
786       vlib_cli_output (vm, "message id table not yet initialized!\n");
787       return (u16) ~0;
788     }
789
790   p = hash_get_mem (am->msg_id_by_name, name);
791   if (!p)
792     return (u16) ~0;
793
794   return p[0];
795 }
796
797 static int
798 vl_msg_exec_json_command (vlib_main_t *vm, cJSON *o)
799 {
800   api_main_t *am = vlibapi_get_main ();
801   u16 msg_id;
802   int len = 0, rv = -1;
803   vl_api_msg_data_t *m;
804   u8 *msg = 0;
805
806   cJSON *msg_id_obj = cJSON_GetObjectItem (o, "_msgname");
807   if (!msg_id_obj)
808     {
809       vlib_cli_output (vm, "Missing '_msgname' element!\n");
810       return rv;
811     }
812   char *name = cJSON_GetStringValue (msg_id_obj);
813
814   cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc");
815   if (!crc_obj)
816     {
817       vlib_cli_output (vm, "Missing '_crc' element!\n");
818       return rv;
819     }
820   char *crc = cJSON_GetStringValue (crc_obj);
821   u8 proc_warning = 0;
822
823   u8 *name_crc = format (0, "%s_%s%c", name, crc, 0);
824   msg_id = vl_msg_find_id_by_name_and_crc (vm, am, (char *) name_crc);
825   m = vl_api_get_msg_data (am, msg_id);
826   if (msg_id == (u16) ~0)
827     {
828       msg_id = vl_msg_find_id_by_name (vm, am, name);
829       if (msg_id == (u16) ~0)
830         {
831           vlib_cli_output (vm, "unknown msg id %d!\n", msg_id);
832           vec_free (name_crc);
833           return rv;
834         }
835       proc_warning = 1;
836     }
837   vec_free (name_crc);
838
839   if (m->replay_allowed)
840     {
841       if (proc_warning)
842         vlib_cli_output (vm, "warning: msg %d has different signature\n");
843
844       if (!m->fromjson_handler)
845         {
846           vlib_cli_output (vm, "missing fromjson convert function! id %d\n",
847                            msg_id);
848           return rv;
849         }
850
851       msg = (u8 *) m->fromjson_handler (o, &len);
852       if (!msg)
853         {
854           vlib_cli_output (vm, "failed to convert JSON (msg id %d)!\n",
855                            msg_id);
856           return rv;
857         }
858
859       if (clib_arch_is_little_endian)
860         m->endian_handler (msg);
861
862       if (!m->handler)
863         {
864           vlib_cli_output (vm, "no handler for msg id %d!\n", msg_id);
865           goto end;
866         }
867
868       if (!m->is_mp_safe)
869         vl_msg_api_barrier_sync ();
870       m->handler (msg);
871       if (!m->is_mp_safe)
872         vl_msg_api_barrier_release ();
873     }
874
875   rv = 0;
876 end:
877   if (msg)
878     cJSON_free (msg);
879   return rv;
880 }
881
882 static void
883 vl_msg_replay_json (vlib_main_t *vm, u8 *filename)
884 {
885   api_main_t *am = vlibapi_get_main ();
886   cJSON *o = 0;
887   int rv = 0;
888   FILE *f = fopen ((char *) filename, "r");
889
890   if (!f)
891     {
892       vlib_cli_output (vm, "failed to open %s!\n", filename);
893       return;
894     }
895
896   char *buf = vl_msg_read_file (f);
897   fclose (f);
898
899   o = cJSON_Parse (buf);
900   vec_free (buf);
901   if (!o)
902     {
903       vlib_cli_output (vm, "%s: Failed parsing JSON input: %s\n", filename,
904                        cJSON_GetErrorPtr ());
905       return;
906     }
907
908   if (cJSON_IsArray (o))
909     {
910       am->replay_in_progress = 1;
911       size_t size = cJSON_GetArraySize (o);
912       for (int i = 0; i < size; i++)
913         {
914           rv = vl_msg_exec_json_command (vm, cJSON_GetArrayItem (o, i));
915           if (rv < 0)
916             {
917               am->replay_in_progress = 0;
918               break;
919             }
920         }
921     }
922   else
923     {
924       rv = vl_msg_exec_json_command (vm, o);
925     }
926
927   if (rv < 0)
928     vlib_cli_output (vm, "error during replaying API trace");
929
930   cJSON_Delete (o);
931 }
932
933 static void
934 vl_msg_dump_file_json (vlib_main_t *vm, u8 *filename)
935 {
936   FILE *f = fopen ((char *) filename, "r");
937   char *buf;
938
939   if (!f)
940     {
941       vlib_cli_output (vm, "failed to open %s!\n", filename);
942       return;
943     }
944
945   buf = vl_msg_read_file (f);
946   fclose (f);
947
948   if (!buf)
949     {
950       vlib_cli_output (vm, "no content in %s!\n", filename);
951       return;
952     }
953
954   vlib_cli_output (vm, buf);
955   vec_free (buf);
956 }
957
958 /** api_trace_command_fn - control the binary API trace / replay feature
959
960     Note: this command MUST be marked thread-safe. Replay with
961     multiple worker threads depends in many cases on worker thread
962     graph replica maintenance. If we (implicitly) assert a worker
963     thread barrier at the debug CLI level, all graph replica changes
964     are deferred until the replay operation completes. If an interface
965     is deleted, the wheels fall off.
966  */
967
968 static clib_error_t *
969 api_trace_command_fn (vlib_main_t * vm,
970                       unformat_input_t * input, vlib_cli_command_t * cmd)
971 {
972   unformat_input_t _line_input, *line_input = &_line_input;
973   u32 nitems = 256 << 10;
974   api_main_t *am = vlibapi_get_main ();
975   vl_api_trace_which_t which = VL_API_TRACE_RX;
976   u8 *filename = 0;
977   u8 *chroot_filename = 0;
978   u32 first = 0;
979   u32 last = (u32) ~ 0;
980   FILE *fp;
981   int rv;
982
983   /* Get a line of input. */
984   if (!unformat_user (input, unformat_line_input, line_input))
985     return 0;
986
987   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
988     {
989       if (unformat (line_input, "on") || unformat (line_input, "enable"))
990         {
991           if (unformat (line_input, "nitems %d", &nitems))
992             ;
993           vlib_worker_thread_barrier_sync (vm);
994           vl_msg_api_trace_configure (am, which, nitems);
995           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
996           vlib_worker_thread_barrier_release (vm);
997         }
998       else if (unformat (line_input, "off"))
999         {
1000           vlib_worker_thread_barrier_sync (vm);
1001           vl_msg_api_trace_onoff (am, which, 0);
1002           vlib_worker_thread_barrier_release (vm);
1003         }
1004       else if (unformat (line_input, "save-json %s", &filename))
1005         {
1006           if (strstr ((char *) filename, "..") ||
1007               index ((char *) filename, '/'))
1008             {
1009               vlib_cli_output (vm, "illegal characters in filename '%s'",
1010                                filename);
1011               goto out;
1012             }
1013
1014           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1015
1016           vec_free (filename);
1017
1018           if (file_exists (chroot_filename))
1019             {
1020               vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1021               goto out;
1022             }
1023
1024           fp = fopen ((char *) chroot_filename, "w");
1025           if (fp == NULL)
1026             {
1027               vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
1028               goto out;
1029             }
1030           vlib_worker_thread_barrier_sync (vm);
1031           rv = vl_msg_api_trace_save (am, which, fp, 1);
1032           if (rv == -1)
1033             vlib_cli_output (vm, "API Trace data not present\n");
1034           else if (rv < 0)
1035             vlib_cli_output (vm, "failed to save api trace\n");
1036           else
1037             vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
1038           vlib_worker_thread_barrier_release (vm);
1039           fclose (fp);
1040         }
1041       else if (unformat (line_input, "save %s", &filename))
1042         {
1043           if (strstr ((char *) filename, "..")
1044               || index ((char *) filename, '/'))
1045             {
1046               vlib_cli_output (vm, "illegal characters in filename '%s'",
1047                                filename);
1048               goto out;
1049             }
1050
1051           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1052
1053           vec_free (filename);
1054
1055           if (file_exists (chroot_filename))
1056             {
1057               vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
1058               goto out;
1059             }
1060
1061           fp = fopen ((char *) chroot_filename, "w");
1062           if (fp == NULL)
1063             {
1064               vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
1065               goto out;
1066             }
1067           vlib_worker_thread_barrier_sync (vm);
1068           rv = vl_msg_api_trace_save (am, which, fp, 0);
1069           vlib_worker_thread_barrier_release (vm);
1070           fclose (fp);
1071           if (rv == -1)
1072             vlib_cli_output (vm, "API Trace data not present\n");
1073           else if (rv == -2)
1074             vlib_cli_output (vm, "File for writing is closed\n");
1075           else if (rv == -10)
1076             vlib_cli_output (vm, "Error while writing header to file\n");
1077           else if (rv == -11)
1078             vlib_cli_output (vm, "Error while writing trace to file\n");
1079           else if (rv == -12)
1080             vlib_cli_output (vm,
1081                              "Error while writing end of buffer trace to file\n");
1082           else if (rv == -13)
1083             vlib_cli_output (vm,
1084                              "Error while writing start of buffer trace to file\n");
1085           else if (rv < 0)
1086             vlib_cli_output (vm, "Unknown error while saving: %d", rv);
1087           else
1088             vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
1089           goto out;
1090         }
1091       else if (unformat (line_input, "tojson %s", &filename))
1092         {
1093           vl_msg_api_process_file (vm, filename, first, last, DUMP_JSON);
1094         }
1095       else if (unformat (line_input, "dump-file-json %s", &filename))
1096         {
1097           vl_msg_dump_file_json (vm, filename);
1098         }
1099       else if (unformat (line_input, "dump-file %s", &filename))
1100         {
1101           vl_msg_api_process_file (vm, filename, first, last, DUMP);
1102         }
1103       else if (unformat (line_input, "dump-json"))
1104         {
1105           vl_msg_api_dump_trace (vm, which, 1);
1106         }
1107       else if (unformat (line_input, "dump"))
1108         {
1109           vl_msg_api_dump_trace (vm, which, 0);
1110         }
1111       else if (unformat (line_input, "replay-json %s", &filename))
1112         {
1113           vl_msg_replay_json (vm, filename);
1114         }
1115       else if (unformat (line_input, "replay %s", &filename))
1116         {
1117           vl_msg_api_process_file (vm, filename, first, last, REPLAY);
1118         }
1119       else if (unformat (line_input, "initializers %s", &filename))
1120         {
1121           vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
1122         }
1123       else if (unformat (line_input, "tx"))
1124         {
1125           which = VL_API_TRACE_TX;
1126         }
1127       else if (unformat (line_input, "first %d", &first))
1128         {
1129           ;
1130         }
1131       else if (unformat (line_input, "last %d", &last))
1132         {
1133           ;
1134         }
1135       else if (unformat (line_input, "status"))
1136         {
1137           vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
1138                            am, which);
1139         }
1140       else if (unformat (line_input, "free"))
1141         {
1142           vlib_worker_thread_barrier_sync (vm);
1143           vl_msg_api_trace_onoff (am, which, 0);
1144           vl_msg_api_trace_free (am, which);
1145           vlib_worker_thread_barrier_release (vm);
1146         }
1147       else if (unformat (line_input, "post-mortem-on"))
1148         vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1149       else if (unformat (line_input, "post-mortem-off"))
1150         vl_msg_api_post_mortem_dump_enable_disable (0 /* enable */ );
1151       else
1152         return clib_error_return (0, "unknown input `%U'",
1153                                   format_unformat_error, input);
1154     }
1155 out:
1156   vec_free (filename);
1157   vec_free (chroot_filename);
1158   unformat_free (line_input);
1159   return 0;
1160 }
1161
1162 /*?
1163  * Display, replay, or save a binary API trace
1164 ?*/
1165
1166 /* *INDENT-OFF* */
1167 VLIB_CLI_COMMAND (api_trace_command, static) = {
1168   .path = "api trace",
1169   .short_help = "api trace [tx][on|off][first <n>][last <n>][status][free]"
1170                 "[post-mortem-on][dump|dump-file|dump-json|save|tojson|save-"
1171                 "json|replay <file>|replay-json <file>][nitems <n>]"
1172                 "[initializers <file>]",
1173   .function = api_trace_command_fn,
1174   .is_mp_safe = 1,
1175 };
1176 /* *INDENT-ON* */
1177
1178 static clib_error_t *
1179 api_trace_config_fn (vlib_main_t * vm, unformat_input_t * input)
1180 {
1181   u32 nitems = 256 << 10;
1182   vl_api_trace_which_t which = VL_API_TRACE_RX;
1183   api_main_t *am = vlibapi_get_main ();
1184
1185   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1186     {
1187       if (unformat (input, "on") || unformat (input, "enable"))
1188         {
1189           if (unformat (input, "nitems %d", &nitems))
1190             ;
1191           vl_msg_api_trace_configure (am, which, nitems);
1192           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
1193           vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1194         }
1195       else if (unformat (input, "save-api-table %s",
1196                          &am->save_msg_table_filename))
1197         ;
1198       else
1199         return clib_error_return (0, "unknown input `%U'",
1200                                   format_unformat_error, input);
1201     }
1202   return 0;
1203 }
1204
1205 /*?
1206  * This module has three configuration parameters:
1207  * "on" or "enable" - enables binary api tracing
1208  * "nitems <nnn>" - sets the size of the circular buffer to <nnn>
1209  * "save-api-table <filename>" - dumps the API message table to /tmp/<filename>
1210 ?*/
1211 VLIB_CONFIG_FUNCTION (api_trace_config_fn, "api-trace");
1212
1213 static clib_error_t *
1214 api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
1215 {
1216   api_main_t *am = vlibapi_get_main ();
1217   u32 nitems;
1218
1219   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1220     {
1221       if (unformat (input, "length %d", &nitems) ||
1222           (unformat (input, "len %d", &nitems)))
1223         {
1224           if (nitems >= 1024)
1225             am->vlib_input_queue_length = nitems;
1226           else
1227             clib_warning ("vlib input queue length %d too small, ignored",
1228                           nitems);
1229         }
1230       else
1231         return clib_error_return (0, "unknown input `%U'",
1232                                   format_unformat_error, input);
1233     }
1234   return 0;
1235 }
1236
1237 VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
1238
1239 static u8 *
1240 extract_name (u8 * s)
1241 {
1242   u8 *rv;
1243
1244   rv = vec_dup (s);
1245
1246   while (vec_len (rv) && rv[vec_len (rv)] != '_')
1247     vec_dec_len (rv, 1);
1248
1249   rv[vec_len (rv)] = 0;
1250
1251   return rv;
1252 }
1253
1254 static u8 *
1255 extract_crc (u8 * s)
1256 {
1257   int i;
1258   u8 *rv;
1259
1260   rv = vec_dup (s);
1261
1262   for (i = vec_len (rv) - 1; i >= 0; i--)
1263     {
1264       if (rv[i] == '_')
1265         {
1266           vec_delete (rv, i + 1, 0);
1267           break;
1268         }
1269     }
1270   return rv;
1271 }
1272
1273 typedef struct
1274 {
1275   u8 *name_and_crc;
1276   u8 *name;
1277   u8 *crc;
1278   u32 msg_index;
1279   int which;
1280 } msg_table_unserialize_t;
1281
1282 static int
1283 table_id_cmp (void *a1, void *a2)
1284 {
1285   msg_table_unserialize_t *n1 = a1;
1286   msg_table_unserialize_t *n2 = a2;
1287
1288   return (n1->msg_index - n2->msg_index);
1289 }
1290
1291 static int
1292 table_name_and_crc_cmp (void *a1, void *a2)
1293 {
1294   msg_table_unserialize_t *n1 = a1;
1295   msg_table_unserialize_t *n2 = a2;
1296
1297   return strcmp ((char *) n1->name_and_crc, (char *) n2->name_and_crc);
1298 }
1299
1300 static clib_error_t *
1301 dump_api_table_file_command_fn (vlib_main_t * vm,
1302                                 unformat_input_t * input,
1303                                 vlib_cli_command_t * cmd)
1304 {
1305   u8 *filename = 0;
1306   api_main_t *am = vlibapi_get_main ();
1307   serialize_main_t _sm, *sm = &_sm;
1308   clib_error_t *error;
1309   u32 nmsgs;
1310   u32 msg_index;
1311   u8 *name_and_crc;
1312   int compare_current = 0;
1313   int numeric_sort = 0;
1314   msg_table_unserialize_t *table = 0, *item;
1315   u32 i;
1316   u32 ndifferences = 0;
1317
1318   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1319     {
1320       if (unformat (input, "file %s", &filename))
1321         ;
1322       else if (unformat (input, "compare-current")
1323                || unformat (input, "compare"))
1324         compare_current = 1;
1325       else if (unformat (input, "numeric"))
1326         numeric_sort = 1;
1327       else
1328         return clib_error_return (0, "unknown input `%U'",
1329                                   format_unformat_error, input);
1330     }
1331
1332   if (numeric_sort && compare_current)
1333     return clib_error_return
1334       (0, "Comparison and numeric sorting are incompatible");
1335
1336   if (filename == 0)
1337     return clib_error_return (0, "File not specified");
1338
1339   /* Load the serialized message table from the table dump */
1340
1341   error = unserialize_open_clib_file (sm, (char *) filename);
1342
1343   if (error)
1344     return error;
1345
1346   unserialize_integer (sm, &nmsgs, sizeof (u32));
1347
1348   for (i = 0; i < nmsgs; i++)
1349     {
1350       msg_index = unserialize_likely_small_unsigned_integer (sm);
1351       unserialize_cstring (sm, (char **) &name_and_crc);
1352       vec_add2 (table, item, 1);
1353       item->msg_index = msg_index;
1354       item->name_and_crc = name_and_crc;
1355       item->name = extract_name (name_and_crc);
1356       item->crc = extract_crc (name_and_crc);
1357       item->which = 0;          /* file */
1358     }
1359   unserialize_close (sm);
1360
1361   /* Compare with the current image? */
1362   if (compare_current)
1363     {
1364       /* Append the current message table */
1365       u8 *tblv = vl_api_serialize_message_table (am, 0);
1366
1367       serialize_open_vector (sm, tblv);
1368       unserialize_integer (sm, &nmsgs, sizeof (u32));
1369
1370       for (i = 0; i < nmsgs; i++)
1371         {
1372           msg_index = unserialize_likely_small_unsigned_integer (sm);
1373           unserialize_cstring (sm, (char **) &name_and_crc);
1374
1375           vec_add2 (table, item, 1);
1376           item->msg_index = msg_index;
1377           item->name_and_crc = name_and_crc;
1378           item->name = extract_name (name_and_crc);
1379           item->crc = extract_crc (name_and_crc);
1380           item->which = 1;      /* current_image */
1381         }
1382       vec_free (tblv);
1383     }
1384
1385   /* Sort the table. */
1386   if (numeric_sort)
1387     vec_sort_with_function (table, table_id_cmp);
1388   else
1389     vec_sort_with_function (table, table_name_and_crc_cmp);
1390
1391   if (compare_current)
1392     {
1393       u8 *dashes = 0;
1394       ndifferences = 0;
1395
1396       /*
1397        * In this case, the recovered table will have two entries per
1398        * API message. So, if entries i and i+1 match, the message definitions
1399        * are identical. Otherwise, the crc is different, or a message is
1400        * present in only one of the tables.
1401        */
1402       vlib_cli_output (vm, "%-60s | %s", "Message Name", "Result");
1403       vec_validate_init_empty (dashes, 60, '-');
1404       vec_terminate_c_string (dashes);
1405       vlib_cli_output (vm, "%60s-|-%s", dashes, "-----------------");
1406       vec_free (dashes);
1407       for (i = 0; i < vec_len (table);)
1408         {
1409           /* Last message lonely? */
1410           if (i == vec_len (table) - 1)
1411             {
1412               ndifferences++;
1413               goto last_unique;
1414             }
1415
1416           /* Identical pair? */
1417           if (!strncmp
1418               ((char *) table[i].name_and_crc,
1419                (char *) table[i + 1].name_and_crc,
1420                vec_len (table[i].name_and_crc)))
1421             {
1422               i += 2;
1423               continue;
1424             }
1425
1426           ndifferences++;
1427
1428           /* Only in one of two tables? */
1429           if (i + 1 == vec_len (table)
1430               || strcmp ((char *) table[i].name, (char *) table[i + 1].name))
1431             {
1432             last_unique:
1433               vlib_cli_output (vm, "%-60s | only in %s",
1434                                table[i].name, table[i].which ?
1435                                "image" : "file");
1436               i++;
1437               continue;
1438             }
1439           /* In both tables, but with different signatures */
1440           vlib_cli_output (vm, "%-60s | definition changed", table[i].name);
1441           i += 2;
1442         }
1443       if (ndifferences == 0)
1444         vlib_cli_output (vm, "No api message signature differences found.");
1445       else
1446         vlib_cli_output (vm, "\nFound %u api message signature differences",
1447                          ndifferences);
1448       goto cleanup;
1449     }
1450
1451   /* Dump the table, sorted as shown above */
1452   vlib_cli_output (vm, "%=60s %=8s %=10s", "Message name", "MsgID", "CRC");
1453
1454   for (i = 0; i < vec_len (table); i++)
1455     {
1456       item = table + i;
1457       vlib_cli_output (vm, "%-60s %8u %10s", item->name,
1458                        item->msg_index, item->crc);
1459     }
1460
1461 cleanup:
1462   for (i = 0; i < vec_len (table); i++)
1463     {
1464       vec_free (table[i].name_and_crc);
1465       vec_free (table[i].name);
1466       vec_free (table[i].crc);
1467     }
1468
1469   vec_free (table);
1470
1471   return 0;
1472 }
1473
1474 /*?
1475  * Displays a serialized API message decode table, sorted by message name
1476  *
1477  * @cliexpar
1478  * @cliexstart{show api dump file <filename>}
1479  *                                                Message name    MsgID        CRC
1480  * accept_session                                                    407   8e2a127e
1481  * accept_session_reply                                              408   67d8c22a
1482  * add_node_next                                                     549   e4202993
1483  * add_node_next_reply                                               550   e89d6eed
1484  * etc.
1485  * @cliexend
1486 ?*/
1487
1488 /*?
1489  * Compares a serialized API message decode table with the current image
1490  *
1491  * @cliexpar
1492  * @cliexstart{show api dump file <filename> compare}
1493  * ip_add_del_route                                             definition changed
1494  * ip_table_add_del                                             definition changed
1495  * l2_macs_event                                                only in image
1496  * vnet_ip4_fib_counters                                        only in file
1497  * vnet_ip4_nbr_counters                                        only in file
1498  * @cliexend
1499 ?*/
1500
1501 /*?
1502  * Display a serialized API message decode table, compare a saved
1503  * decode table with the current image, to establish API differences.
1504  *
1505 ?*/
1506 /* *INDENT-OFF* */
1507 VLIB_CLI_COMMAND (dump_api_table_file, static) =
1508 {
1509   .path = "show api dump",
1510   .short_help = "show api dump file <filename> [numeric | compare-current]",
1511   .function = dump_api_table_file_command_fn,
1512 };
1513
1514 /* *INDENT-ON* */
1515 /*
1516  * fd.io coding-style-patch-verification: ON
1517  *
1518  * Local Variables:
1519  * eval: (c-set-style "gnu")
1520  * End:
1521  */