vlib_mains == 0 special cases be gone
[vpp.git] / src / vlibmemory / memory_vlib.c
index 3a7415c..d2e0596 100644 (file)
@@ -22,6 +22,8 @@
 #include <string.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 #include <signal.h>
 #include <pthread.h>
 #include <vppinfra/vec.h>
@@ -1437,6 +1439,475 @@ rpc_api_hookup (vlib_main_t * vm)
 
 VLIB_API_INIT_FUNCTION (rpc_api_hookup);
 
+typedef enum
+{
+  DUMP,
+  CUSTOM_DUMP,
+  REPLAY,
+  INITIALIZERS,
+} vl_api_replay_t;
+
+u8 *
+format_vl_msg_api_trace_status (u8 * s, va_list * args)
+{
+  api_main_t *am = va_arg (*args, api_main_t *);
+  vl_api_trace_which_t which = va_arg (*args, vl_api_trace_which_t);
+  vl_api_trace_t *tp;
+  char *trace_name;
+
+  switch (which)
+    {
+    case VL_API_TRACE_TX:
+      tp = am->tx_trace;
+      trace_name = "TX trace";
+      break;
+
+    case VL_API_TRACE_RX:
+      tp = am->rx_trace;
+      trace_name = "RX trace";
+      break;
+
+    default:
+      abort ();
+    }
+
+  if (tp == 0)
+    {
+      s = format (s, "%s: not yet configured.\n", trace_name);
+      return s;
+    }
+
+  s = format (s, "%s: used %d of %d items, %s enabled, %s wrapped\n",
+             trace_name, vec_len (tp->traces), tp->nitems,
+             tp->enabled ? "is" : "is not", tp->wrapped ? "has" : "has not");
+  return s;
+}
+
+void vl_msg_api_custom_dump_configure (api_main_t * am)
+  __attribute__ ((weak));
+void
+vl_msg_api_custom_dump_configure (api_main_t * am)
+{
+}
+
+static void
+vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
+                        u32 first_index, u32 last_index,
+                        vl_api_replay_t which)
+{
+  vl_api_trace_file_header_t *hp;
+  int i, fd;
+  struct stat statb;
+  size_t file_size;
+  u8 *msg;
+  u8 endian_swap_needed = 0;
+  api_main_t *am = &api_main;
+  u8 *tmpbuf = 0;
+  u32 nitems;
+  void **saved_print_handlers = 0;
+
+  fd = open ((char *) filename, O_RDONLY);
+
+  if (fd < 0)
+    {
+      vlib_cli_output (vm, "Couldn't open %s\n", filename);
+      return;
+    }
+
+  if (fstat (fd, &statb) < 0)
+    {
+      vlib_cli_output (vm, "Couldn't stat %s\n", filename);
+      close (fd);
+      return;
+    }
+
+  if (!(statb.st_mode & S_IFREG) || (statb.st_size < sizeof (*hp)))
+    {
+      vlib_cli_output (vm, "File not plausible: %s\n", filename);
+      close (fd);
+      return;
+    }
+
+  file_size = statb.st_size;
+  file_size = (file_size + 4095) & ~(4096);
+
+  hp = mmap (0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
+
+  if (hp == (vl_api_trace_file_header_t *) MAP_FAILED)
+    {
+      vlib_cli_output (vm, "mmap failed: %s\n", filename);
+      close (fd);
+      return;
+    }
+  close (fd);
+
+  if ((clib_arch_is_little_endian && hp->endian == VL_API_BIG_ENDIAN)
+      || (clib_arch_is_big_endian && hp->endian == VL_API_LITTLE_ENDIAN))
+    endian_swap_needed = 1;
+
+  if (endian_swap_needed)
+    nitems = ntohl (hp->nitems);
+  else
+    nitems = hp->nitems;
+
+  if (last_index == (u32) ~ 0)
+    {
+      last_index = nitems - 1;
+    }
+
+  if (first_index >= nitems || last_index >= nitems)
+    {
+      vlib_cli_output (vm, "Range (%d, %d) outside file range (0, %d)\n",
+                      first_index, last_index, nitems - 1);
+      munmap (hp, file_size);
+      return;
+    }
+  if (hp->wrapped)
+    vlib_cli_output (vm,
+                    "Note: wrapped/incomplete trace, results may vary\n");
+
+  if (which == CUSTOM_DUMP)
+    {
+      saved_print_handlers = (void **) vec_dup (am->msg_print_handlers);
+      vl_msg_api_custom_dump_configure (am);
+    }
+
+
+  msg = (u8 *) (hp + 1);
+
+  for (i = 0; i < first_index; i++)
+    {
+      trace_cfg_t *cfgp;
+      int size;
+      u16 msg_id;
+
+      size = clib_host_to_net_u32 (*(u32 *) msg);
+      msg += sizeof (u32);
+
+      if (clib_arch_is_little_endian)
+       msg_id = ntohs (*((u16 *) msg));
+      else
+       msg_id = *((u16 *) msg);
+
+      cfgp = am->api_trace_cfg + msg_id;
+      if (!cfgp)
+       {
+         vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
+         munmap (hp, file_size);
+         return;
+       }
+      msg += size;
+    }
+
+  if (which == REPLAY)
+    am->replay_in_progress = 1;
+
+  for (; i <= last_index; i++)
+    {
+      trace_cfg_t *cfgp;
+      u16 *msg_idp;
+      u16 msg_id;
+      int size;
+
+      if (which == DUMP)
+       vlib_cli_output (vm, "---------- trace %d -----------\n", i);
+
+      size = clib_host_to_net_u32 (*(u32 *) msg);
+      msg += sizeof (u32);
+
+      if (clib_arch_is_little_endian)
+       msg_id = ntohs (*((u16 *) msg));
+      else
+       msg_id = *((u16 *) msg);
+
+      cfgp = am->api_trace_cfg + msg_id;
+      if (!cfgp)
+       {
+         vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
+         munmap (hp, file_size);
+         vec_free (tmpbuf);
+         am->replay_in_progress = 0;
+         return;
+       }
+
+      /* Copy the buffer (from the read-only mmap'ed file) */
+      vec_validate (tmpbuf, size - 1 + sizeof (uword));
+      clib_memcpy (tmpbuf + sizeof (uword), msg, size);
+      memset (tmpbuf, 0xf, sizeof (uword));
+
+      /*
+       * Endian swap if needed. All msg data is supposed to be
+       * in network byte order. All msg handlers are supposed to
+       * know that. The generic message dumpers don't know that.
+       * One could fix apigen, I suppose.
+       */
+      if ((which == DUMP && clib_arch_is_little_endian) || endian_swap_needed)
+       {
+         void (*endian_fp) (void *);
+         if (msg_id >= vec_len (am->msg_endian_handlers)
+             || (am->msg_endian_handlers[msg_id] == 0))
+           {
+             vlib_cli_output (vm, "Ugh: msg id %d no endian swap\n", msg_id);
+             munmap (hp, file_size);
+             vec_free (tmpbuf);
+             am->replay_in_progress = 0;
+             return;
+           }
+         endian_fp = am->msg_endian_handlers[msg_id];
+         (*endian_fp) (tmpbuf + sizeof (uword));
+       }
+
+      /* msg_id always in network byte order */
+      if (clib_arch_is_little_endian)
+       {
+         msg_idp = (u16 *) (tmpbuf + sizeof (uword));
+         *msg_idp = msg_id;
+       }
+
+      switch (which)
+       {
+       case CUSTOM_DUMP:
+       case DUMP:
+         if (msg_id < vec_len (am->msg_print_handlers) &&
+             am->msg_print_handlers[msg_id])
+           {
+             u8 *(*print_fp) (void *, void *);
+
+             print_fp = (void *) am->msg_print_handlers[msg_id];
+             (*print_fp) (tmpbuf + sizeof (uword), vm);
+           }
+         else
+           {
+             vlib_cli_output (vm, "Skipping msg id %d: no print fcn\n",
+                              msg_id);
+             break;
+           }
+         break;
+
+       case INITIALIZERS:
+         if (msg_id < vec_len (am->msg_print_handlers) &&
+             am->msg_print_handlers[msg_id])
+           {
+             u8 *s;
+             int j;
+             u8 *(*print_fp) (void *, void *);
+
+             print_fp = (void *) am->msg_print_handlers[msg_id];
+
+             vlib_cli_output (vm, "/*");
+
+             (*print_fp) (tmpbuf + sizeof (uword), vm);
+             vlib_cli_output (vm, "*/\n");
+
+             s = format (0, "static u8 * vl_api_%s_%d[%d] = {",
+                         am->msg_names[msg_id], i,
+                         am->api_trace_cfg[msg_id].size);
+
+             for (j = 0; j < am->api_trace_cfg[msg_id].size; j++)
+               {
+                 if ((j & 7) == 0)
+                   s = format (s, "\n    ");
+                 s = format (s, "0x%02x,", tmpbuf[sizeof (uword) + j]);
+               }
+             s = format (s, "\n};\n%c", 0);
+             vlib_cli_output (vm, (char *) s);
+             vec_free (s);
+           }
+         break;
+
+       case REPLAY:
+         if (msg_id < vec_len (am->msg_print_handlers) &&
+             am->msg_print_handlers[msg_id] && cfgp->replay_enable)
+           {
+             void (*handler) (void *);
+
+             handler = (void *) am->msg_handlers[msg_id];
+
+             if (!am->is_mp_safe[msg_id])
+               vl_msg_api_barrier_sync ();
+             (*handler) (tmpbuf + sizeof (uword));
+             if (!am->is_mp_safe[msg_id])
+               vl_msg_api_barrier_release ();
+           }
+         else
+           {
+             if (cfgp->replay_enable)
+               vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
+                                msg_id);
+             break;
+           }
+         break;
+       }
+
+      _vec_len (tmpbuf) = 0;
+      msg += size;
+    }
+
+  if (saved_print_handlers)
+    {
+      clib_memcpy (am->msg_print_handlers, saved_print_handlers,
+                  vec_len (am->msg_print_handlers) * sizeof (void *));
+      vec_free (saved_print_handlers);
+    }
+
+  munmap (hp, file_size);
+  vec_free (tmpbuf);
+  am->replay_in_progress = 0;
+}
+
+static clib_error_t *
+api_trace_command_fn (vlib_main_t * vm,
+                     unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  u32 nitems = 256 << 10;
+  api_main_t *am = &api_main;
+  vl_api_trace_which_t which = VL_API_TRACE_RX;
+  u8 *filename;
+  u32 first = 0;
+  u32 last = (u32) ~ 0;
+  FILE *fp;
+  int rv;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "on") || unformat (input, "enable"))
+       {
+         if (unformat (input, "nitems %d", &nitems))
+           ;
+         vl_msg_api_trace_configure (am, which, nitems);
+         vl_msg_api_trace_onoff (am, which, 1 /* on */ );
+       }
+      else if (unformat (input, "off"))
+       {
+         vl_msg_api_trace_onoff (am, which, 0);
+       }
+      else if (unformat (input, "save %s", &filename))
+       {
+         u8 *chroot_filename;
+         if (strstr ((char *) filename, "..")
+             || index ((char *) filename, '/'))
+           {
+             vlib_cli_output (vm, "illegal characters in filename '%s'",
+                              filename);
+             return 0;
+           }
+
+         chroot_filename = format (0, "/tmp/%s%c", filename, 0);
+
+         vec_free (filename);
+
+         fp = fopen ((char *) chroot_filename, "w");
+         if (fp == NULL)
+           {
+             vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
+             return 0;
+           }
+         rv = vl_msg_api_trace_save (am, which, fp);
+         fclose (fp);
+         if (rv == -1)
+           vlib_cli_output (vm, "API Trace data not present\n");
+         else if (rv == -2)
+           vlib_cli_output (vm, "File for writing is closed\n");
+         else if (rv == -10)
+           vlib_cli_output (vm, "Error while writing header to file\n");
+         else if (rv == -11)
+           vlib_cli_output (vm, "Error while writing trace to file\n");
+         else if (rv == -12)
+           vlib_cli_output (vm,
+                            "Error while writing end of buffer trace to file\n");
+         else if (rv == -13)
+           vlib_cli_output (vm,
+                            "Error while writing start of buffer trace to file\n");
+         else if (rv < 0)
+           vlib_cli_output (vm, "Unkown error while saving: %d", rv);
+         else
+           vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
+         vec_free (chroot_filename);
+       }
+      else if (unformat (input, "dump %s", &filename))
+       {
+         vl_msg_api_process_file (vm, filename, first, last, DUMP);
+       }
+      else if (unformat (input, "custom-dump %s", &filename))
+       {
+         vl_msg_api_process_file (vm, filename, first, last, CUSTOM_DUMP);
+       }
+      else if (unformat (input, "replay %s", &filename))
+       {
+         vl_msg_api_process_file (vm, filename, first, last, REPLAY);
+       }
+      else if (unformat (input, "initializers %s", &filename))
+       {
+         vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
+       }
+      else if (unformat (input, "tx"))
+       {
+         which = VL_API_TRACE_TX;
+       }
+      else if (unformat (input, "first %d", &first))
+       {
+         ;
+       }
+      else if (unformat (input, "last %d", &last))
+       {
+         ;
+       }
+      else if (unformat (input, "status"))
+       {
+         vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
+                          am, which);
+       }
+      else if (unformat (input, "free"))
+       {
+         vl_msg_api_trace_onoff (am, which, 0);
+         vl_msg_api_trace_free (am, which);
+       }
+      else if (unformat (input, "post-mortem-on"))
+       vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
+      else if (unformat (input, "post-mortem-off"))
+       vl_msg_api_post_mortem_dump_enable_disable (0 /* enable */ );
+      else
+       return clib_error_return (0, "unknown input `%U'",
+                                 format_unformat_error, input);
+    }
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (api_trace_command, static) = {
+    .path = "api trace",
+    .short_help =
+    "api trace [on|off][dump|save|replay <file>][status][free][post-mortem-on]",
+    .function = api_trace_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+api_config_fn (vlib_main_t * vm, unformat_input_t * input)
+{
+  u32 nitems = 256 << 10;
+  vl_api_trace_which_t which = VL_API_TRACE_RX;
+  api_main_t *am = &api_main;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "on") || unformat (input, "enable"))
+       {
+         if (unformat (input, "nitems %d", &nitems))
+           ;
+         vl_msg_api_trace_configure (am, which, nitems);
+         vl_msg_api_trace_onoff (am, which, 1 /* on */ );
+         vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
+       }
+      else
+       return clib_error_return (0, "unknown input `%U'",
+                                 format_unformat_error, input);
+    }
+  return 0;
+}
+
+VLIB_CONFIG_FUNCTION (api_config_fn, "api-trace");
+
 /*
  * fd.io coding-style-patch-verification: ON
  *