Add a name to the creation of an IP and MPLS table
[vpp.git] / src / vlibmemory / memory_vlib.c
index d2e0596..401f388 100644 (file)
 #include <vlibapi/api.h>
 #include <vlibmemory/api.h>
 
+/**
+ * @file
+ * @brief Binary API messaging via shared memory
+ * Low-level, primary provisioning interface
+ */
+/*? %%clicmd:group_label Binary API CLI %% ?*/
+/*? %%syscfg:group_label Binary API configuration %% ?*/
+
 #define TRACE_VLIB_MEMORY_QUEUE 0
 
 #include <vlibmemory/vl_memory_msg_enum.h>     /* enumerate all vlib messages */
@@ -136,6 +144,44 @@ vl_api_serialize_message_table (api_main_t * am, u8 * vector)
   return serialize_close_vector (sm);
 }
 
+/*
+ * vl_api_memclnt_create_internal
+ */
+
+u32
+vl_api_memclnt_create_internal (char *name, unix_shared_memory_queue_t * q)
+{
+  vl_api_registration_t **regpp;
+  vl_api_registration_t *regp;
+  svm_region_t *svm;
+  void *oldheap;
+  api_main_t *am = &api_main;
+
+  ASSERT (vlib_get_thread_index () == 0);
+  pool_get (am->vl_clients, regpp);
+
+  svm = am->vlib_rp;
+
+  pthread_mutex_lock (&svm->mutex);
+  oldheap = svm_push_data_heap (svm);
+  *regpp = clib_mem_alloc (sizeof (vl_api_registration_t));
+
+  regp = *regpp;
+  memset (regp, 0, sizeof (*regp));
+  regp->registration_type = REGISTRATION_TYPE_SHMEM;
+  regp->vl_api_registration_pool_index = regpp - am->vl_clients;
+
+  regp->vl_input_queue = q;
+  regp->name = format (0, "%s%c", name, 0);
+
+  pthread_mutex_unlock (&svm->mutex);
+  svm_pop_heap (oldheap);
+  return vl_msg_api_handle_from_index_and_epoch
+    (regp->vl_api_registration_pool_index,
+     am->shmem_hdr->application_restarts);
+}
+
+
 /*
  * vl_api_memclnt_create_t_handler
  */
@@ -150,7 +196,6 @@ vl_api_memclnt_create_t_handler (vl_api_memclnt_create_t * mp)
   int rv = 0;
   void *oldheap;
   api_main_t *am = &api_main;
-  u8 *serialized_message_table = 0;
 
   /*
    * This is tortured. Maintain a vlib-address-space private
@@ -182,9 +227,6 @@ vl_api_memclnt_create_t_handler (vl_api_memclnt_create_t * mp)
 
   svm = am->vlib_rp;
 
-  if (am->serialized_message_table_in_shmem == 0)
-    serialized_message_table = vl_api_serialize_message_table (am, 0);
-
   pthread_mutex_lock (&svm->mutex);
   oldheap = svm_push_data_heap (svm);
   *regpp = clib_mem_alloc (sizeof (vl_api_registration_t));
@@ -199,14 +241,11 @@ vl_api_memclnt_create_t_handler (vl_api_memclnt_create_t * mp)
 
   regp->name = format (0, "%s", mp->name);
   vec_add1 (regp->name, 0);
-  if (serialized_message_table)
-    am->serialized_message_table_in_shmem =
-      vec_dup (serialized_message_table);
 
   pthread_mutex_unlock (&svm->mutex);
   svm_pop_heap (oldheap);
 
-  vec_free (serialized_message_table);
+  ASSERT (am->serialized_message_table_in_shmem);
 
   rp = vl_msg_api_alloc (sizeof (*rp));
   rp->_vl_msg_id = ntohs (VL_API_MEMCLNT_CREATE_REPLY);
@@ -216,17 +255,26 @@ vl_api_memclnt_create_t_handler (vl_api_memclnt_create_t * mp)
      am->shmem_hdr->application_restarts);
   rp->context = mp->context;
   rp->response = ntohl (rv);
-  rp->message_table = (u64) am->serialized_message_table_in_shmem;
+  rp->message_table =
+    pointer_to_uword (am->serialized_message_table_in_shmem);
 
   vl_msg_api_send_shmem (q, (u8 *) & rp);
 }
 
-/* Application callback to clean up leftover registrations from this client */
-int vl_api_memclnt_delete_callback (u32 client_index) __attribute__ ((weak));
-
-int
-vl_api_memclnt_delete_callback (u32 client_index)
+static int
+call_reaper_functions (u32 client_index)
 {
+  clib_error_t *error = 0;
+  _vl_msg_api_function_list_elt_t *i;
+
+  i = api_main.reaper_function_registrations;
+  while (i)
+    {
+      error = i->f (client_index);
+      if (error)
+       clib_error_report (error);
+      i = i->next_init_function;
+    }
   return 0;
 }
 
@@ -246,7 +294,7 @@ vl_api_memclnt_delete_t_handler (vl_api_memclnt_delete_t * mp)
 
   handle = mp->index;
 
-  if (vl_api_memclnt_delete_callback (handle))
+  if (call_reaper_functions (handle))
     return;
 
   epoch = vl_msg_api_handle_get_epoch (handle);
@@ -353,7 +401,7 @@ _(GET_FIRST_MSG_ID, get_first_msg_id)
  * vl_api_init
  */
 static int
-memory_api_init (char *region_name)
+memory_api_init (const char *region_name)
 {
   int rv;
   vl_msg_api_msg_config_t cfg;
@@ -440,6 +488,9 @@ memclnt_process (vlib_main_t * vm,
   f64 sleep_time, start_time;
   f64 vector_rate;
   int i;
+  u8 *serialized_message_table = 0;
+  svm_region_t *svm;
+  void *oldheap;
 
   vlib_set_queue_signal_callback (vm, memclnt_queue_callback);
 
@@ -472,6 +523,60 @@ memclnt_process (vlib_main_t * vm,
                                   rp->last_msg_id);
     }
 
+  /*
+   * Snapshoot the api message table.
+   */
+  serialized_message_table = vl_api_serialize_message_table (am, 0);
+
+  svm = am->vlib_rp;
+  pthread_mutex_lock (&svm->mutex);
+  oldheap = svm_push_data_heap (svm);
+
+  am->serialized_message_table_in_shmem = vec_dup (serialized_message_table);
+
+  pthread_mutex_unlock (&svm->mutex);
+  svm_pop_heap (oldheap);
+
+  /*
+   * Save the api message table snapshot, if configured
+   */
+  if (am->save_msg_table_filename)
+    {
+      int fd, rv;
+      u8 *chroot_file;
+      if (strstr ((char *) am->save_msg_table_filename, "..")
+         || index ((char *) am->save_msg_table_filename, '/'))
+       {
+         clib_warning ("illegal save-message-table filename '%s'",
+                       am->save_msg_table_filename);
+         goto skip_save;
+       }
+
+      chroot_file = format (0, "/tmp/%s%c", am->save_msg_table_filename, 0);
+
+      fd = creat ((char *) chroot_file, 0644);
+
+      if (fd < 0)
+       {
+         clib_unix_warning ("creat");
+         goto skip_save;
+       }
+      rv = write (fd, serialized_message_table,
+                 vec_len (serialized_message_table));
+
+      if (rv != vec_len (serialized_message_table))
+       clib_unix_warning ("write");
+
+      rv = close (fd);
+      if (rv < 0)
+       clib_unix_warning ("close");
+
+      vec_free (chroot_file);
+    }
+
+skip_save:
+  vec_free (serialized_message_table);
+
   /* $$$ pay attention to frame size, control CPU usage */
   while (1)
     {
@@ -621,7 +726,7 @@ memclnt_process (vlib_main_t * vm,
 
                      handle = vl_msg_api_handle_from_index_and_epoch
                        (dead_indices[i], shm->application_restarts);
-                     (void) vl_api_memclnt_delete_callback (handle);
+                     (void) call_reaper_functions (handle);
                    }
                }
 
@@ -679,6 +784,15 @@ memclnt_process (vlib_main_t * vm,
 
   return 0;
 }
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (memclnt_node,static) = {
+    .function = memclnt_process,
+    .type = VLIB_NODE_TYPE_PROCESS,
+    .name = "api-rx-from-ring",
+    .state = VLIB_NODE_STATE_DISABLED,
+};
+/* *INDENT-ON* */
+
 
 static clib_error_t *
 vl_api_show_histogram_command (vlib_main_t * vm,
@@ -715,11 +829,15 @@ vl_api_show_histogram_command (vlib_main_t * vm,
   return 0;
 }
 
+/*?
+ * Display the binary api sleep-time histogram
+?*/
 /* *INDENT-OFF* */
-VLIB_CLI_COMMAND (cli_show_api_histogram_command, static) = {
-    .path = "show api histogram",
-    .short_help = "show api histogram",
-    .function = vl_api_show_histogram_command,
+VLIB_CLI_COMMAND (cli_show_api_histogram_command, static) =
+{
+  .path = "show api histogram",
+  .short_help = "show api histogram",
+  .function = vl_api_show_histogram_command,
 };
 /* *INDENT-ON* */
 
@@ -735,21 +853,15 @@ vl_api_clear_histogram_command (vlib_main_t * vm,
   return 0;
 }
 
+/*?
+ * Clear the binary api sleep-time histogram
+?*/
 /* *INDENT-OFF* */
-VLIB_CLI_COMMAND (cli_clear_api_histogram_command, static) = {
-    .path = "clear api histogram",
-    .short_help = "clear api histogram",
-    .function = vl_api_clear_histogram_command,
-};
-/* *INDENT-ON* */
-
-
-/* *INDENT-OFF* */
-VLIB_REGISTER_NODE (memclnt_node,static) = {
-    .function = memclnt_process,
-    .type = VLIB_NODE_TYPE_PROCESS,
-    .name = "api-rx-from-ring",
-    .state = VLIB_NODE_STATE_DISABLED,
+VLIB_CLI_COMMAND (cli_clear_api_histogram_command, static) =
+{
+  .path = "clear api histogram",
+  .short_help = "clear api histogram",
+  .function = vl_api_clear_histogram_command,
 };
 /* *INDENT-ON* */
 
@@ -1017,33 +1129,46 @@ vl_api_status_command (vlib_main_t * vm,
 }
 
 /* *INDENT-OFF* */
-VLIB_CLI_COMMAND (cli_show_api_command, static) = {
-    .path = "show api",
-    .short_help = "Show API information",
+VLIB_CLI_COMMAND (cli_show_api_command, static) =
+{
+  .path = "show api",
+  .short_help = "Show API information",
 };
 /* *INDENT-ON* */
 
+/*?
+ * Display binary api message allocation ring statistics
+?*/
 /* *INDENT-OFF* */
-VLIB_CLI_COMMAND (cli_show_api_ring_command, static) = {
-    .path = "show api ring-stats",
-    .short_help = "Message ring statistics",
-    .function = vl_api_ring_command,
+VLIB_CLI_COMMAND (cli_show_api_ring_command, static) =
+{
+  .path = "show api ring-stats",
+  .short_help = "Message ring statistics",
+  .function = vl_api_ring_command,
 };
 /* *INDENT-ON* */
 
+/*?
+ * Display current api client connections
+?*/
 /* *INDENT-OFF* */
-VLIB_CLI_COMMAND (cli_show_api_clients_command, static) = {
-    .path = "show api clients",
-    .short_help = "Client information",
-    .function = vl_api_client_command,
+VLIB_CLI_COMMAND (cli_show_api_clients_command, static) =
+{
+  .path = "show api clients",
+  .short_help = "Client information",
+  .function = vl_api_client_command,
 };
 /* *INDENT-ON* */
 
+/*?
+ * Display the current api message tracing status
+?*/
 /* *INDENT-OFF* */
-VLIB_CLI_COMMAND (cli_show_api_status_command, static) = {
-    .path = "show api status",
-    .short_help = "Show API trace status",
-    .function = vl_api_status_command,
+VLIB_CLI_COMMAND (cli_show_api_status_command, static) =
+{
+  .path = "show api trace-status",
+  .short_help = "Display API trace status",
+  .function = vl_api_status_command,
 };
 /* *INDENT-ON* */
 
@@ -1086,11 +1211,15 @@ vl_api_message_table_command (vlib_main_t * vm,
   return 0;
 }
 
+/*?
+ * Display the current api message decode tables
+?*/
 /* *INDENT-OFF* */
-VLIB_CLI_COMMAND (cli_show_api_message_table_command, static) = {
-    .path = "show api message-table",
-    .short_help = "Message Table",
-    .function = vl_api_message_table_command,
+VLIB_CLI_COMMAND (cli_show_api_message_table_command, static) =
+{
+  .path = "show api message-table",
+  .short_help = "Message Table",
+  .function = vl_api_message_table_command,
 };
 /* *INDENT-ON* */
 
@@ -1160,11 +1289,15 @@ configure:
   return 0;
 }
 
+/*?
+ * Control the binary API trace mechanism
+?*/
 /* *INDENT-OFF* */
-VLIB_CLI_COMMAND (trace, static) = {
-    .path = "set api-trace",
-    .short_help = "API trace",
-    .function = vl_api_trace_command,
+VLIB_CLI_COMMAND (trace, static) =
+{
+  .path = "set api-trace [on][on tx][on rx][off][free][debug on][debug off]",
+  .short_help = "API trace",
+  .function = vl_api_trace_command,
 };
 /* *INDENT-ON* */
 
@@ -1194,7 +1327,7 @@ vlibmemory_init (vlib_main_t * vm)
 VLIB_INIT_FUNCTION (vlibmemory_init);
 
 void
-vl_set_memory_region_name (char *name)
+vl_set_memory_region_name (const char *name)
 {
   api_main_t *am = &api_main;
 
@@ -1218,9 +1351,9 @@ format_api_msg_range (u8 * s, va_list * args)
   vl_api_msg_range_t *rp = va_arg (*args, vl_api_msg_range_t *);
 
   if (rp == 0)
-    s = format (s, "%-20s%9s%9s", "Name", "First-ID", "Last-ID");
+    s = format (s, "%-50s%9s%9s", "Name", "First-ID", "Last-ID");
   else
-    s = format (s, "%-20s%9d%9d", rp->name, rp->first_msg_id,
+    s = format (s, "%-50s%9d%9d", rp->name, rp->first_msg_id,
                rp->last_msg_id);
 
   return s;
@@ -1256,18 +1389,22 @@ vl_api_show_plugin_command (vlib_main_t * vm,
   return 0;
 }
 
+/*?
+ * Display the plugin binary API message range table
+?*/
 /* *INDENT-OFF* */
-VLIB_CLI_COMMAND (cli_show_api_plugin_command, static) = {
-    .path = "show api plugin",
-    .short_help = "show api plugin",
-    .function = vl_api_show_plugin_command,
+VLIB_CLI_COMMAND (cli_show_api_plugin_command, static) =
+{
+  .path = "show api plugin",
+  .short_help = "show api plugin",
+  .function = vl_api_show_plugin_command,
 };
 /* *INDENT-ON* */
 
 static void
 vl_api_rpc_call_t_handler (vl_api_rpc_call_t * mp)
 {
-  vl_api_rpc_reply_t *rmp;
+  vl_api_rpc_call_reply_t *rmp;
   int (*fp) (void *);
   i32 rv = 0;
   vlib_main_t *vm = vlib_get_main ();
@@ -1297,7 +1434,7 @@ vl_api_rpc_call_t_handler (vl_api_rpc_call_t * mp)
       if (q)
        {
          rmp = vl_msg_api_alloc_as_if_client (sizeof (*rmp));
-         rmp->_vl_msg_id = ntohs (VL_API_RPC_REPLY);
+         rmp->_vl_msg_id = ntohs (VL_API_RPC_CALL_REPLY);
          rmp->context = mp->context;
          rmp->retval = rv;
          vl_msg_api_send_shmem (q, (u8 *) & rmp);
@@ -1310,7 +1447,7 @@ vl_api_rpc_call_t_handler (vl_api_rpc_call_t * mp)
 }
 
 static void
-vl_api_rpc_reply_t_handler (vl_api_rpc_reply_t * mp)
+vl_api_rpc_call_reply_t_handler (vl_api_rpc_call_reply_t * mp)
 {
   clib_warning ("unimplemented");
 }
@@ -1324,7 +1461,7 @@ vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length)
   unix_shared_memory_queue_t *q;
 
   /* Main thread: call the function directly */
-  if (os_get_cpu_number () == 0)
+  if (vlib_get_thread_index () == 0)
     {
       vlib_main_t *vm = vlib_get_main ();
       void (*call_fp) (void *);
@@ -1407,7 +1544,7 @@ vl_api_trace_plugin_msg_ids_t_handler (vl_api_trace_plugin_msg_ids_t * mp)
 
 #define foreach_rpc_api_msg                     \
 _(RPC_CALL,rpc_call)                            \
-_(RPC_REPLY,rpc_reply)
+_(RPC_CALL_REPLY,rpc_call_reply)
 
 #define foreach_plugin_trace_msg               \
 _(TRACE_PLUGIN_MSG_IDS,trace_plugin_msg_ids)
@@ -1415,6 +1552,7 @@ _(TRACE_PLUGIN_MSG_IDS,trace_plugin_msg_ids)
 static clib_error_t *
 rpc_api_hookup (vlib_main_t * vm)
 {
+  api_main_t *am = &api_main;
 #define _(N,n)                                                  \
     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
                            vl_api_##n##_t_handler,              \
@@ -1434,6 +1572,10 @@ rpc_api_hookup (vlib_main_t * vm)
                            sizeof(vl_api_##n##_t), 1 /* do trace */);
   foreach_plugin_trace_msg;
 #undef _
+
+  /* No reason to halt the parade to create a trace record... */
+  am->is_mp_safe[VL_API_TRACE_PLUGIN_MSG_IDS] = 1;
+
   return 0;
 }
 
@@ -1873,12 +2015,17 @@ api_trace_command_fn (vlib_main_t * vm,
   return 0;
 }
 
+/*?
+ * Display, replay, or save a binary API trace
+?*/
+
 /* *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,
+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* */
 
@@ -1899,6 +2046,9 @@ api_config_fn (vlib_main_t * vm, unformat_input_t * input)
          vl_msg_api_trace_onoff (am, which, 1 /* on */ );
          vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
        }
+      else if (unformat (input, "save-api-table %s",
+                        &am->save_msg_table_filename))
+       ;
       else
        return clib_error_return (0, "unknown input `%U'",
                                  format_unformat_error, input);
@@ -1906,8 +2056,309 @@ api_config_fn (vlib_main_t * vm, unformat_input_t * input)
   return 0;
 }
 
+/*?
+ * This module has three configuration parameters:
+ * "on" or "enable" - enables binary api tracing
+ * "nitems <nnn>" - sets the size of the circular buffer to <nnn>
+ * "save-api-table <filename>" - dumps the API message table to /tmp/<filename>
+?*/
 VLIB_CONFIG_FUNCTION (api_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;
+  u32 nitems;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "length %d", &nitems) ||
+         (unformat (input, "len %d", &nitems)))
+       {
+         if (nitems >= 1024)
+           am->vlib_input_queue_length = nitems;
+         else
+           clib_warning ("vlib input queue length %d too small, ignored",
+                         nitems);
+       }
+      else
+       return clib_error_return (0, "unknown input `%U'",
+                                 format_unformat_error, input);
+    }
+  return 0;
+}
+
+VLIB_CONFIG_FUNCTION (api_queue_config_fn, "api-queue");
+
+static u8 *
+extract_name (u8 * s)
+{
+  u8 *rv;
+
+  rv = vec_dup (s);
+
+  while (vec_len (rv) && rv[vec_len (rv)] != '_')
+    _vec_len (rv)--;
+
+  rv[vec_len (rv)] = 0;
+
+  return rv;
+}
+
+static u8 *
+extract_crc (u8 * s)
+{
+  int i;
+  u8 *rv;
+
+  rv = vec_dup (s);
+
+  for (i = vec_len (rv) - 1; i >= 0; i--)
+    {
+      if (rv[i] == '_')
+       {
+         vec_delete (rv, i + 1, 0);
+         break;
+       }
+    }
+  return rv;
+}
+
+typedef struct
+{
+  u8 *name_and_crc;
+  u8 *name;
+  u8 *crc;
+  u32 msg_index;
+  int which;
+} msg_table_unserialize_t;
+
+static int
+table_id_cmp (void *a1, void *a2)
+{
+  msg_table_unserialize_t *n1 = a1;
+  msg_table_unserialize_t *n2 = a2;
+
+  return (n1->msg_index - n2->msg_index);
+}
+
+static int
+table_name_and_crc_cmp (void *a1, void *a2)
+{
+  msg_table_unserialize_t *n1 = a1;
+  msg_table_unserialize_t *n2 = a2;
+
+  return strcmp ((char *) n1->name_and_crc, (char *) n2->name_and_crc);
+}
+
+static clib_error_t *
+dump_api_table_file_command_fn (vlib_main_t * vm,
+                               unformat_input_t * input,
+                               vlib_cli_command_t * cmd)
+{
+  u8 *filename = 0;
+  api_main_t *am = &api_main;
+  serialize_main_t _sm, *sm = &_sm;
+  clib_error_t *error;
+  u32 nmsgs;
+  u32 msg_index;
+  u8 *name_and_crc;
+  int compare_current = 0;
+  int numeric_sort = 0;
+  msg_table_unserialize_t *table = 0, *item;
+  u32 i;
+  u32 ndifferences = 0;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "file %s", &filename))
+       ;
+      else if (unformat (input, "compare-current")
+              || unformat (input, "compare"))
+       compare_current = 1;
+      else if (unformat (input, "numeric"))
+       numeric_sort = 1;
+      else
+       return clib_error_return (0, "unknown input `%U'",
+                                 format_unformat_error, input);
+    }
+
+  if (numeric_sort && compare_current)
+    return clib_error_return
+      (0, "Comparison and numeric sorting are incompatible");
+
+  if (filename == 0)
+    return clib_error_return (0, "File not specified");
+
+  /* Load the serialized message table from the table dump */
+
+  error = unserialize_open_unix_file (sm, (char *) filename);
+
+  if (error)
+    return error;
+
+  unserialize_integer (sm, &nmsgs, sizeof (u32));
+
+  for (i = 0; i < nmsgs; i++)
+    {
+      msg_index = unserialize_likely_small_unsigned_integer (sm);
+      unserialize_cstring (sm, (char **) &name_and_crc);
+      vec_add2 (table, item, 1);
+      item->msg_index = msg_index;
+      item->name_and_crc = name_and_crc;
+      item->name = extract_name (name_and_crc);
+      item->crc = extract_crc (name_and_crc);
+      item->which = 0;         /* file */
+    }
+  serialize_close (sm);
+
+  /* Compare with the current image? */
+  if (compare_current)
+    {
+      /* Append the current message table */
+      u8 *tblv = vec_dup (am->serialized_message_table_in_shmem);
+
+      serialize_open_vector (sm, tblv);
+      unserialize_integer (sm, &nmsgs, sizeof (u32));
+
+      for (i = 0; i < nmsgs; i++)
+       {
+         msg_index = unserialize_likely_small_unsigned_integer (sm);
+         unserialize_cstring (sm, (char **) &name_and_crc);
+
+         vec_add2 (table, item, 1);
+         item->msg_index = msg_index;
+         item->name_and_crc = name_and_crc;
+         item->name = extract_name (name_and_crc);
+         item->crc = extract_crc (name_and_crc);
+         item->which = 1;      /* current_image */
+       }
+    }
+
+  /* Sort the table. */
+  if (numeric_sort)
+    vec_sort_with_function (table, table_id_cmp);
+  else
+    vec_sort_with_function (table, table_name_and_crc_cmp);
+
+  if (compare_current)
+    {
+      ndifferences = 0;
+
+      /*
+       * In this case, the recovered table will have two entries per
+       * API message. So, if entries i and i+1 match, the message definitions
+       * 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");
+
+      for (i = 0; i < vec_len (table);)
+       {
+         /* Last message lonely? */
+         if (i == vec_len (table) - 1)
+           {
+             ndifferences++;
+             goto last_unique;
+           }
+
+         /* Identical pair? */
+         if (!strncmp
+             ((char *) table[i].name_and_crc,
+              (char *) table[i + 1].name_and_crc,
+              vec_len (table[i].name_and_crc)))
+           {
+             i += 2;
+             continue;
+           }
+
+         ndifferences++;
+
+         /* Only in one of two tables? */
+         if (strncmp ((char *) table[i].name, (char *) table[i + 1].name,
+                      vec_len (table[i].name)))
+           {
+           last_unique:
+             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);
+         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",
+                        ndifferences);
+      goto cleanup;
+    }
+
+  /* Dump the table, sorted as shown above */
+  vlib_cli_output (vm, "%=60s %=8s %=10s", "Message name", "MsgID", "CRC");
+
+  for (i = 0; i < vec_len (table); i++)
+    {
+      item = table + i;
+      vlib_cli_output (vm, "%-60s %8u %10s", item->name,
+                      item->msg_index, item->crc);
+    }
+
+cleanup:
+  for (i = 0; i < vec_len (table); i++)
+    {
+      vec_free (table[i].name_and_crc);
+      vec_free (table[i].name);
+      vec_free (table[i].crc);
+    }
+
+  vec_free (table);
+
+  return 0;
+}
+
+/*?
+ * Displays a serialized API message decode table, sorted by message name
+ *
+ * @cliexpar
+ * @cliexstart{show api dump file <filename>}
+ *                                                Message name    MsgID        CRC
+ * accept_session                                                    407   8e2a127e
+ * accept_session_reply                                              408   67d8c22a
+ * add_node_next                                                     549   e4202993
+ * add_node_next_reply                                               550   e89d6eed
+ * etc.
+ * @cliexend
+?*/
+
+/*?
+ * Compares a serialized API message decode table with the current image
+ *
+ * @cliexpar
+ * @cliexstart{show api dump file <filename> compare}
+ * ip_add_del_route                                             definition changed
+ * ip_table_add_del                                             definition changed
+ * l2_macs_event                                                only in image
+ * vnet_ip4_fib_counters                                        only in file
+ * vnet_ip4_nbr_counters                                        only in file
+ * @cliexend
+?*/
+
+/*?
+ * Display a serialized API message decode table
+?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (dump_api_table_file, static) =
+{
+  .path = "show api dump",
+  .short_help = "show api dump file <filename> [numeric | compare-current]",
+  .function = dump_api_table_file_command_fn,
+};
+/* *INDENT-ON* */
+
 /*
  * fd.io coding-style-patch-verification: ON
  *