api: API trace improvements
[vpp.git] / src / vlibmemory / vlib_api_cli.c
old mode 100755 (executable)
new mode 100644 (file)
index 2d8d407..74ad3c5
@@ -101,7 +101,7 @@ vl_api_client_command (vlib_main_t * vm,
   vl_api_registration_t **regpp, *regp;
   svm_queue_t *q;
   char *health;
-  api_main_t *am = &api_main;
+  api_main_t *am = vlibapi_get_main ();
   u32 *confused_indices = 0;
 
   if (!pool_elts (am->vl_clients))
@@ -111,8 +111,8 @@ vl_api_client_command (vlib_main_t * vm,
                   "Name", "PID", "Queue Length", "Queue VA", "Health");
 
   /* *INDENT-OFF* */
-  pool_foreach (regpp, am->vl_clients,
-  ({
+  pool_foreach (regpp, am->vl_clients)
+   {
     regp = *regpp;
 
     if (regp)
@@ -134,7 +134,7 @@ vl_api_client_command (vlib_main_t * vm,
                       regpp - am->vl_clients);
         vec_add1 (confused_indices, regpp - am->vl_clients);
       }
-  }));
+  }
   /* *INDENT-ON* */
 
   /* This should "never happen," but if it does, fix it... */
@@ -161,7 +161,7 @@ static clib_error_t *
 vl_api_status_command (vlib_main_t * vm,
                       unformat_input_t * input, vlib_cli_command_t * cli_cmd)
 {
-  api_main_t *am = &api_main;
+  api_main_t *am = vlibapi_get_main ();
 
   /* check if rx_trace and tx_trace are not null pointers */
   if (am->rx_trace == 0)
@@ -228,7 +228,7 @@ vl_api_message_table_command (vlib_main_t * vm,
                              unformat_input_t * input,
                              vlib_cli_command_t * cli_cmd)
 {
-  api_main_t *am = &api_main;
+  api_main_t *am = vlibapi_get_main ();
   int i;
   int verbose = 0;
 
@@ -304,7 +304,7 @@ vl_api_show_plugin_command (vlib_main_t * vm,
                            unformat_input_t * input,
                            vlib_cli_command_t * cli_cmd)
 {
-  api_main_t *am = &api_main;
+  api_main_t *am = vlibapi_get_main ();
   vl_api_msg_range_t *rp = 0;
   int i;
 
@@ -344,7 +344,7 @@ VLIB_CLI_COMMAND (cli_show_api_plugin_command, static) =
 typedef enum
 {
   DUMP,
-  CUSTOM_DUMP,
+  DUMP_JSON,
   REPLAY,
   INITIALIZERS,
 } vl_api_replay_t;
@@ -385,13 +385,6 @@ format_vl_msg_api_trace_status (u8 * s, va_list * args)
   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,
@@ -399,14 +392,13 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
 {
   vl_api_trace_file_header_t *hp;
   int i, fd;
+  u16 *msgid_vec = 0;
   struct stat statb;
   size_t file_size;
   u8 *msg;
-  u8 endian_swap_needed = 0;
-  api_main_t *am = &api_main;
+  api_main_t *am = vlibapi_get_main ();
   u8 *tmpbuf = 0;
-  u32 nitems;
-  void **saved_print_handlers = 0;
+  u32 nitems, nitems_msgtbl;
 
   fd = open ((char *) filename, O_RDONLY);
 
@@ -431,7 +423,7 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
     }
 
   file_size = statb.st_size;
-  file_size = (file_size + 4095) & ~(4096);
+  file_size = (file_size + 4095) & ~(4095);
 
   hp = mmap (0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
 
@@ -443,14 +435,9 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
     }
   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;
+  CLIB_MEM_UNPOISON (hp, file_size);
 
-  if (endian_swap_needed)
-    nitems = ntohl (hp->nitems);
-  else
-    nitems = hp->nitems;
+  nitems = ntohl (hp->nitems);
 
   if (last_index == (u32) ~ 0)
     {
@@ -468,14 +455,44 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
     vlib_cli_output (vm,
                     "Note: wrapped/incomplete trace, results may vary\n");
 
-  if (which == CUSTOM_DUMP)
+  size_t file_size_left = file_size;
+
+#define assert_size(size_left, s)                                             \
+  do                                                                          \
+    {                                                                         \
+      if ((s) >= size_left)                                                   \
+       {                                                                     \
+         vlib_cli_output (vm, "corrupted file");                             \
+         munmap (hp, file_size);                                             \
+         vec_free (msgid_vec);                                               \
+         return;                                                             \
+       }                                                                     \
+      size_left -= s;                                                         \
+    }                                                                         \
+  while (0);
+
+  assert_size (file_size_left, sizeof (hp[0]));
+  msg = (u8 *) (hp + 1);
+
+  serialize_main_t _sm, *sm = &_sm;
+  u32 msgtbl_size = ntohl (hp->msgtbl_size);
+  u8 *name_and_crc;
+
+  assert_size (file_size_left, msgtbl_size);
+
+  unserialize_open_data (sm, msg, msgtbl_size);
+  unserialize_integer (sm, &nitems_msgtbl, sizeof (u32));
+
+  for (i = 0; i < nitems_msgtbl; i++)
     {
-      saved_print_handlers = (void **) vec_dup (am->msg_print_handlers);
-      vl_msg_api_custom_dump_configure (am);
+      u16 msg_index = unserialize_likely_small_unsigned_integer (sm);
+      unserialize_cstring (sm, (char **) &name_and_crc);
+      u16 msg_index2 = vl_msg_api_get_msg_index (name_and_crc);
+      vec_validate (msgid_vec, msg_index);
+      msgid_vec[msg_index] = msg_index2;
     }
 
-
-  msg = (u8 *) (hp + 1);
+  msg += msgtbl_size;
 
   for (i = 0; i < first_index; i++)
     {
@@ -483,19 +500,20 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
       int size;
       u16 msg_id;
 
+      assert_size (file_size_left, sizeof (u32));
       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);
-
+      assert_size (file_size_left, size);
+      msg_id = ntohs (*((u16 *) msg));
+      if (msg_id < vec_len (msgid_vec))
+       msg_id = msgid_vec[msg_id];
       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 (msgid_vec);
          return;
        }
       msg += size;
@@ -507,7 +525,6 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
   for (; i <= last_index; i++)
     {
       trace_cfg_t *cfgp;
-      u16 *msg_idp;
       u16 msg_id;
       int size;
 
@@ -517,10 +534,11 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
       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);
+      msg_id = ntohs (*((u16 *) msg));
+      if (msg_id < vec_len (msgid_vec))
+       {
+         msg_id = msgid_vec[msg_id];
+       }
 
       cfgp = am->api_trace_cfg + msg_id;
       if (!cfgp)
@@ -538,12 +556,11 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
       clib_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.
+       * Endian swap if needed. All msg data is supposed to be in
+       * network byte order.
        */
-      if ((which == DUMP && clib_arch_is_little_endian) || endian_swap_needed)
+      if (((which == DUMP || which == DUMP_JSON) &&
+          clib_arch_is_little_endian))
        {
          void (*endian_fp) (void *);
          if (msg_id >= vec_len (am->msg_endian_handlers)
@@ -562,13 +579,29 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
       /* msg_id always in network byte order */
       if (clib_arch_is_little_endian)
        {
-         msg_idp = (u16 *) (tmpbuf + sizeof (uword));
+         u16 *msg_idp = (u16 *) (tmpbuf + sizeof (uword));
          *msg_idp = msg_id;
        }
 
       switch (which)
        {
-       case CUSTOM_DUMP:
+       case DUMP_JSON:
+         if (msg_id < vec_len (am->msg_print_json_handlers) &&
+             am->msg_print_json_handlers[msg_id])
+           {
+             u8 *(*print_fp) (void *, void *);
+
+             print_fp = (void *) am->msg_print_json_handlers[msg_id];
+             (*print_fp) (tmpbuf + sizeof (uword), vm);
+           }
+         else
+           {
+             vlib_cli_output (vm, "Skipping msg id %d: no JSON print fcn\n",
+                              msg_id);
+             break;
+           }
+         break;
+
        case DUMP:
          if (msg_id < vec_len (am->msg_print_handlers) &&
              am->msg_print_handlers[msg_id])
@@ -645,66 +678,433 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
       msg += size;
     }
 
-  if (saved_print_handlers)
+  munmap (hp, file_size);
+  vec_free (tmpbuf);
+  vec_free (msgid_vec);
+  am->replay_in_progress = 0;
+}
+
+static int
+file_exists (u8 *fname)
+{
+  FILE *fp = 0;
+  fp = fopen ((char *) fname, "r");
+  if (fp)
     {
-      clib_memcpy (am->msg_print_handlers, saved_print_handlers,
-                  vec_len (am->msg_print_handlers) * sizeof (void *));
-      vec_free (saved_print_handlers);
+      fclose (fp);
+      return 1;
+    }
+  return 0;
+}
+
+typedef struct
+{
+  vlib_main_t *vm;
+  u8 is_json;
+} vl_msg_print_args;
+
+static int
+vl_msg_print_trace (u8 *msg, void *ctx)
+{
+  vl_msg_print_args *a = ctx;
+  api_main_t *am = vlibapi_get_main ();
+  u16 msg_id = ntohs (*((u16 *) msg));
+  void (*print_fp) (void *, void *);
+  void (**handlers) (void *, void *);
+  u8 is_json = a->is_json;
+  u8 *tmpbuf = 0;
+
+  if (clib_arch_is_little_endian)
+    {
+      u32 msg_length = vec_len (msg);
+      vec_validate (tmpbuf, msg_length - 1);
+      clib_memcpy_fast (tmpbuf, msg, msg_length);
+      msg = tmpbuf;
+
+      void (*endian_fp) (void *);
+      endian_fp = am->msg_endian_handlers[msg_id];
+      (*endian_fp) (tmpbuf);
+    }
+
+  if (is_json)
+    handlers = am->msg_print_json_handlers;
+  else
+    handlers = am->msg_print_handlers;
+
+  if (msg_id < vec_len (handlers) && handlers[msg_id])
+    {
+      print_fp = (void *) handlers[msg_id];
+      (*print_fp) (msg, a->vm);
+    }
+  else
+    {
+      vlib_cli_output (a->vm, "Skipping msg id %d: no print fcn\n", msg_id);
     }
 
-  munmap (hp, file_size);
   vec_free (tmpbuf);
-  am->replay_in_progress = 0;
+  return 0;
+}
+
+static int
+vl_msg_api_dump_trace (vlib_main_t *vm, vl_api_trace_which_t which, u8 is_json)
+{
+  api_main_t *am = vlibapi_get_main ();
+  vl_api_trace_t *tp;
+
+  switch (which)
+    {
+    case VL_API_TRACE_TX:
+      tp = am->tx_trace;
+      break;
+    case VL_API_TRACE_RX:
+      tp = am->rx_trace;
+      break;
+    default:
+      return -1;
+    }
+
+  if (tp == 0 || tp->nitems == 0 || vec_len (tp->traces) == 0)
+    return -1;
+
+  vl_msg_print_args args;
+  clib_memset (&args, 0, sizeof (args));
+  args.is_json = is_json;
+  args.vm = vm;
+  vl_msg_traverse_trace (tp, vl_msg_print_trace, &args);
+
+  return 0;
+}
+
+static char *
+vl_msg_read_file (FILE *f)
+{
+  const size_t bufsize = 1024;
+  char *buf[bufsize], *v = 0;
+  size_t n;
+
+  while ((n = fread (buf, 1, bufsize, f)))
+    vec_add (v, buf, n);
+
+  return v;
+}
+
+static u16
+vl_msg_find_id_by_name_and_crc (vlib_main_t *vm, api_main_t *am, char *name)
+{
+  uword *p;
+  p = hash_get_mem (am->msg_index_by_name_and_crc, name);
+  if (!p)
+    return (u16) ~0;
+
+  return p[0];
+}
+
+static u16
+vl_msg_find_id_by_name (vlib_main_t *vm, api_main_t *am, char *name)
+{
+  uword *p;
+
+  if (!am->msg_id_by_name)
+    {
+      vlib_cli_output (vm, "message id table not yet initialized!\n");
+      return (u16) ~0;
+    }
+
+  p = hash_get_mem (am->msg_id_by_name, name);
+  if (!p)
+    return (u16) ~0;
+
+  return p[0];
+}
+
+static int
+vl_msg_exec_json_command (vlib_main_t *vm, cJSON *o)
+{
+  api_main_t *am = vlibapi_get_main ();
+  u16 msg_id;
+  void *(*fromjson) (cJSON *, int *);
+  int len = 0, rv = -1;
+  trace_cfg_t *cfgp;
+  u8 *msg = 0;
+
+  cJSON *msg_id_obj = cJSON_GetObjectItem (o, "_msgname");
+  if (!msg_id_obj)
+    {
+      vlib_cli_output (vm, "Missing '_msgname' element!\n");
+      return rv;
+    }
+  char *name = cJSON_GetStringValue (msg_id_obj);
+
+  cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc");
+  if (!msg_id_obj)
+    {
+      vlib_cli_output (vm, "Missing '_crc' element!\n");
+      return rv;
+    }
+  char *crc = cJSON_GetStringValue (crc_obj);
+  u8 proc_warning = 0;
+
+  u8 *name_crc = format (0, "%s_%s%c", name, crc, 0);
+  msg_id = vl_msg_find_id_by_name_and_crc (vm, am, (char *) name_crc);
+  if (msg_id == (u16) ~0)
+    {
+      msg_id = vl_msg_find_id_by_name (vm, am, name);
+      if (msg_id == (u16) ~0)
+       {
+         vlib_cli_output (vm, "unknown msg id %d!\n", msg_id);
+         vec_free (name_crc);
+         return rv;
+       }
+      proc_warning = 1;
+    }
+  vec_free (name_crc);
+
+  cfgp = am->api_trace_cfg + msg_id;
+  if (!cfgp)
+    {
+      vlib_cli_output (vm, "msg id %d no trace config\n", msg_id);
+      return rv;
+    }
+
+  if (cfgp->replay_enable)
+    {
+
+      if (proc_warning)
+       vlib_cli_output (vm, "warning: msg %d has different signature\n");
+
+      fromjson = am->msg_fromjson_handlers[msg_id];
+      if (!fromjson)
+       {
+         vlib_cli_output (vm, "missing fromjson convert function! id %d\n",
+                          msg_id);
+         return rv;
+       }
+
+      msg = (u8 *) fromjson (o, &len);
+      if (!msg)
+       {
+         vlib_cli_output (vm, "failed to convert JSON (msg id %d)!\n",
+                          msg_id);
+         return rv;
+       }
+
+      if (clib_arch_is_little_endian)
+       {
+         void (*endian_fp) (void *);
+         endian_fp = am->msg_endian_handlers[msg_id];
+         (*endian_fp) (msg);
+       }
+
+      void (*handler) (void *, vlib_main_t *);
+      handler = (void *) am->msg_handlers[msg_id];
+      if (!handler)
+       {
+         vlib_cli_output (vm, "no handler for msg id %d!\n", msg_id);
+         goto end;
+       }
+
+      if (!am->is_mp_safe[msg_id])
+       vl_msg_api_barrier_sync ();
+      (*handler) (msg, vm);
+      if (!am->is_mp_safe[msg_id])
+       vl_msg_api_barrier_release ();
+    }
+
+  rv = 0;
+end:
+  if (msg)
+    cJSON_free (msg);
+  return rv;
+}
+
+static void
+vl_msg_replay_json (vlib_main_t *vm, u8 *filename)
+{
+  api_main_t *am = vlibapi_get_main ();
+  cJSON *o = 0;
+  int rv = 0;
+  FILE *f = fopen ((char *) filename, "r");
+
+  if (!f)
+    {
+      vlib_cli_output (vm, "failed to open %s!\n", filename);
+      return;
+    }
+
+  char *buf = vl_msg_read_file (f);
+  fclose (f);
+
+  o = cJSON_Parse (buf);
+  vec_free (buf);
+  if (!o)
+    {
+      vlib_cli_output (vm, "%s: Failed parsing JSON input: %s\n", filename,
+                      cJSON_GetErrorPtr ());
+      return;
+    }
+
+  if (cJSON_IsArray (o))
+    {
+      am->replay_in_progress = 1;
+      size_t size = cJSON_GetArraySize (o);
+      for (int i = 0; i < size; i++)
+       {
+         rv = vl_msg_exec_json_command (vm, cJSON_GetArrayItem (o, i));
+         if (rv < 0)
+           {
+             am->replay_in_progress = 0;
+             break;
+           }
+       }
+    }
+  else
+    {
+      rv = vl_msg_exec_json_command (vm, o);
+    }
+
+  if (rv < 0)
+    vlib_cli_output (vm, "error during replaying API trace");
+
+  cJSON_Delete (o);
 }
 
+static void
+vl_msg_dump_file_json (vlib_main_t *vm, u8 *filename)
+{
+  FILE *f = fopen ((char *) filename, "r");
+  char *buf;
+
+  if (!f)
+    {
+      vlib_cli_output (vm, "failed to open %s!\n", filename);
+      return;
+    }
+
+  buf = vl_msg_read_file (f);
+  fclose (f);
+
+  if (!buf)
+    {
+      vlib_cli_output (vm, "no content in %s!\n", filename);
+      return;
+    }
+
+  vlib_cli_output (vm, buf);
+  vec_free (buf);
+}
+
+/** api_trace_command_fn - control the binary API trace / replay feature
+
+    Note: this command MUST be marked thread-safe. Replay with
+    multiple worker threads depends in many cases on worker thread
+    graph replica maintenance. If we (implicitly) assert a worker
+    thread barrier at the debug CLI level, all graph replica changes
+    are deferred until the replay operation completes. If an interface
+    is deleted, the wheels fall off.
+ */
+
 static clib_error_t *
 api_trace_command_fn (vlib_main_t * vm,
                      unformat_input_t * input, vlib_cli_command_t * cmd)
 {
+  unformat_input_t _line_input, *line_input = &_line_input;
   u32 nitems = 256 << 10;
-  api_main_t *am = &api_main;
+  api_main_t *am = vlibapi_get_main ();
   vl_api_trace_which_t which = VL_API_TRACE_RX;
-  u8 *filename;
+  u8 *filename = 0;
+  u8 *chroot_filename = 0;
   u32 first = 0;
   u32 last = (u32) ~ 0;
   FILE *fp;
   int rv;
 
-  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+  /* Get a line of input. */
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     {
-      if (unformat (input, "on") || unformat (input, "enable"))
+      if (unformat (line_input, "on") || unformat (line_input, "enable"))
        {
-         if (unformat (input, "nitems %d", &nitems))
+         if (unformat (line_input, "nitems %d", &nitems))
            ;
+         vlib_worker_thread_barrier_sync (vm);
          vl_msg_api_trace_configure (am, which, nitems);
          vl_msg_api_trace_onoff (am, which, 1 /* on */ );
+         vlib_worker_thread_barrier_release (vm);
        }
-      else if (unformat (input, "off"))
+      else if (unformat (line_input, "off"))
        {
+         vlib_worker_thread_barrier_sync (vm);
          vl_msg_api_trace_onoff (am, which, 0);
+         vlib_worker_thread_barrier_release (vm);
+       }
+      else if (unformat (line_input, "save-json %s", &filename))
+       {
+         if (strstr ((char *) filename, "..") ||
+             index ((char *) filename, '/'))
+           {
+             vlib_cli_output (vm, "illegal characters in filename '%s'",
+                              filename);
+             goto out;
+           }
+
+         chroot_filename = format (0, "/tmp/%s%c", filename, 0);
+
+         vec_free (filename);
+
+         if (file_exists (chroot_filename))
+           {
+             vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
+             goto out;
+           }
+
+         fp = fopen ((char *) chroot_filename, "w");
+         if (fp == NULL)
+           {
+             vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
+             goto out;
+           }
+         vlib_worker_thread_barrier_sync (vm);
+         rv = vl_msg_api_trace_save (am, which, fp, 1);
+         if (rv == -1)
+           vlib_cli_output (vm, "API Trace data not present\n");
+         else if (rv < 0)
+           vlib_cli_output (vm, "failed to save api trace\n");
+         else
+           vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
+         vlib_worker_thread_barrier_release (vm);
+         fclose (fp);
        }
-      else if (unformat (input, "save %s", &filename))
+      else if (unformat (line_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;
+             goto out;
            }
 
          chroot_filename = format (0, "/tmp/%s%c", filename, 0);
 
          vec_free (filename);
 
+         if (file_exists (chroot_filename))
+           {
+             vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
+             goto out;
+           }
+
          fp = fopen ((char *) chroot_filename, "w");
          if (fp == NULL)
            {
              vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
-             return 0;
+             goto out;
            }
-         rv = vl_msg_api_trace_save (am, which, fp);
+         vlib_worker_thread_barrier_sync (vm);
+         rv = vl_msg_api_trace_save (am, which, fp, 0);
+         vlib_worker_thread_barrier_release (vm);
          fclose (fp);
          if (rv == -1)
            vlib_cli_output (vm, "API Trace data not present\n");
@@ -724,54 +1124,76 @@ api_trace_command_fn (vlib_main_t * vm,
            vlib_cli_output (vm, "Unknown error while saving: %d", rv);
          else
            vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
-         vec_free (chroot_filename);
+         goto out;
+       }
+      else if (unformat (line_input, "tojson %s", &filename))
+       {
+         vl_msg_api_process_file (vm, filename, first, last, DUMP_JSON);
+       }
+      else if (unformat (line_input, "dump-file-json %s", &filename))
+       {
+         vl_msg_dump_file_json (vm, filename);
        }
-      else if (unformat (input, "dump %s", &filename))
+      else if (unformat (line_input, "dump-file %s", &filename))
        {
          vl_msg_api_process_file (vm, filename, first, last, DUMP);
        }
-      else if (unformat (input, "custom-dump %s", &filename))
+      else if (unformat (line_input, "dump-json"))
+       {
+         vl_msg_api_dump_trace (vm, which, 1);
+       }
+      else if (unformat (line_input, "dump"))
        {
-         vl_msg_api_process_file (vm, filename, first, last, CUSTOM_DUMP);
+         vl_msg_api_dump_trace (vm, which, 0);
        }
-      else if (unformat (input, "replay %s", &filename))
+      else if (unformat (line_input, "replay-json %s", &filename))
+       {
+         vl_msg_replay_json (vm, filename);
+       }
+      else if (unformat (line_input, "replay %s", &filename))
        {
          vl_msg_api_process_file (vm, filename, first, last, REPLAY);
        }
-      else if (unformat (input, "initializers %s", &filename))
+      else if (unformat (line_input, "initializers %s", &filename))
        {
          vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
        }
-      else if (unformat (input, "tx"))
+      else if (unformat (line_input, "tx"))
        {
          which = VL_API_TRACE_TX;
        }
-      else if (unformat (input, "first %d", &first))
+      else if (unformat (line_input, "first %d", &first))
        {
          ;
        }
-      else if (unformat (input, "last %d", &last))
+      else if (unformat (line_input, "last %d", &last))
        {
          ;
        }
-      else if (unformat (input, "status"))
+      else if (unformat (line_input, "status"))
        {
          vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
                           am, which);
        }
-      else if (unformat (input, "free"))
+      else if (unformat (line_input, "free"))
        {
+         vlib_worker_thread_barrier_sync (vm);
          vl_msg_api_trace_onoff (am, which, 0);
          vl_msg_api_trace_free (am, which);
+         vlib_worker_thread_barrier_release (vm);
        }
-      else if (unformat (input, "post-mortem-on"))
+      else if (unformat (line_input, "post-mortem-on"))
        vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
-      else if (unformat (input, "post-mortem-off"))
+      else if (unformat (line_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);
     }
+out:
+  vec_free (filename);
+  vec_free (chroot_filename);
+  unformat_free (line_input);
   return 0;
 }
 
@@ -780,90 +1202,14 @@ api_trace_command_fn (vlib_main_t * vm,
 ?*/
 
 /* *INDENT-OFF* */
-VLIB_CLI_COMMAND (api_trace_command, static) =
-{
+VLIB_CLI_COMMAND (api_trace_command, static) = {
   .path = "api trace",
-  .short_help = "api trace [on|off][first <n>][last <n>][status][free]"
-      "[post-mortem-on][dump|custom-dump|save|replay <file>]",
+  .short_help = "api trace [tx][on|off][first <n>][last <n>][status][free]"
+               "[post-mortem-on][dump|dump-file|dump-json|save|tojson|save-"
+               "json|replay <file>|replay-json <file>][nitems <n>]"
+               "[initializers <file>]",
   .function = api_trace_command_fn,
-};
-/* *INDENT-ON* */
-
-static clib_error_t *
-vl_api_trace_command (vlib_main_t * vm,
-                     unformat_input_t * input, vlib_cli_command_t * cli_cmd)
-{
-  u32 nitems = 1024;
-  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, "rx nitems %u", &nitems) || unformat (input, "rx"))
-       goto configure;
-      else if (unformat (input, "tx nitems %u", &nitems)
-              || unformat (input, "tx"))
-       {
-         which = VL_API_TRACE_RX;
-         goto configure;
-       }
-      else if (unformat (input, "on rx"))
-       {
-         vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
-       }
-      else if (unformat (input, "on tx"))
-       {
-         vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 1);
-       }
-      else if (unformat (input, "on"))
-       {
-         vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
-       }
-      else if (unformat (input, "off"))
-       {
-         vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
-         vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
-       }
-      else if (unformat (input, "free"))
-       {
-         vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
-         vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
-         vl_msg_api_trace_free (am, VL_API_TRACE_RX);
-         vl_msg_api_trace_free (am, VL_API_TRACE_TX);
-       }
-      else if (unformat (input, "debug on"))
-       {
-         am->msg_print_flag = 1;
-       }
-      else if (unformat (input, "debug off"))
-       {
-         am->msg_print_flag = 0;
-       }
-      else
-       return clib_error_return (0, "unknown input `%U'",
-                                 format_unformat_error, input);
-    }
-  return 0;
-
-configure:
-  if (vl_msg_api_trace_configure (am, which, nitems))
-    {
-      vlib_cli_output (vm, "warning: trace configure error (%d, %d)",
-                      which, nitems);
-    }
-
-  return 0;
-}
-
-/*?
- * Control the binary API trace mechanism
-?*/
-/* *INDENT-OFF* */
-VLIB_CLI_COMMAND (trace, static) =
-{
-  .path = "set api-trace",
-  .short_help = "API trace [on][on tx][on rx][off][free][debug on][debug off]",
-  .function = vl_api_trace_command,
+  .is_mp_safe = 1,
 };
 /* *INDENT-ON* */
 
@@ -872,7 +1218,7 @@ api_trace_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;
+  api_main_t *am = vlibapi_get_main ();
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     {
@@ -905,7 +1251,7 @@ VLIB_CONFIG_FUNCTION (api_trace_config_fn, "api-trace");
 static clib_error_t *
 api_queue_config_fn (vlib_main_t * vm, unformat_input_t * input)
 {
-  api_main_t *am = &api_main;
+  api_main_t *am = vlibapi_get_main ();
   u32 nitems;
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
@@ -995,7 +1341,7 @@ dump_api_table_file_command_fn (vlib_main_t * vm,
                                vlib_cli_command_t * cmd)
 {
   u8 *filename = 0;
-  api_main_t *am = &api_main;
+  api_main_t *am = vlibapi_get_main ();
   serialize_main_t _sm, *sm = &_sm;
   clib_error_t *error;
   u32 nmsgs;
@@ -1048,7 +1394,7 @@ dump_api_table_file_command_fn (vlib_main_t * vm,
       item->crc = extract_crc (name_and_crc);
       item->which = 0;         /* file */
     }
-  serialize_close (sm);
+  unserialize_close (sm);
 
   /* Compare with the current image? */
   if (compare_current)
@@ -1082,6 +1428,7 @@ dump_api_table_file_command_fn (vlib_main_t * vm,
 
   if (compare_current)
     {
+      u8 *dashes = 0;
       ndifferences = 0;
 
       /*
@@ -1090,8 +1437,11 @@ dump_api_table_file_command_fn (vlib_main_t * vm,
        * are identical. Otherwise, the crc is different, or a message is
        * present in only one of the tables.
        */
-      vlib_cli_output (vm, "%=60s %s", "Message Name", "Result");
-
+      vlib_cli_output (vm, "%-60s | %s", "Message Name", "Result");
+      vec_validate_init_empty (dashes, 60, '-');
+      vec_terminate_c_string (dashes);
+      vlib_cli_output (vm, "%60s-|-%s", dashes, "-----------------");
+      vec_free (dashes);
       for (i = 0; i < vec_len (table);)
        {
          /* Last message lonely? */
@@ -1118,20 +1468,20 @@ dump_api_table_file_command_fn (vlib_main_t * vm,
              || strcmp ((char *) table[i].name, (char *) table[i + 1].name))
            {
            last_unique:
-             vlib_cli_output (vm, "%-60s only in %s",
+             vlib_cli_output (vm, "%-60s only in %s",
                               table[i].name, table[i].which ?
                               "image" : "file");
              i++;
              continue;
            }
          /* In both tables, but with different signatures */
-         vlib_cli_output (vm, "%-60s definition changed", table[i].name);
+         vlib_cli_output (vm, "%-60s definition changed", table[i].name);
          i += 2;
        }
       if (ndifferences == 0)
        vlib_cli_output (vm, "No api message signature differences found.");
       else
-       vlib_cli_output (vm, "Found %u api message signature differences",
+       vlib_cli_output (vm, "\nFound %u api message signature differences",
                         ndifferences);
       goto cleanup;
     }