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