export counters in a memfd segment 92/12892/3
authorDave Barach <dave@barachs.net>
Fri, 1 Jun 2018 22:52:25 +0000 (18:52 -0400)
committerDave Barach <dave@barachs.net>
Fri, 8 Jun 2018 15:42:01 +0000 (11:42 -0400)
also export per-node error counters

directory entries implement object types

Change-Id: I8ce8e0a754e1be9de895c44ed9be6533b4ecef0f
Signed-off-by: Dave Barach <dave@barachs.net>
18 files changed:
src/vat/api_format.c
src/vat/vat.h
src/vcl.am
src/vlib.am
src/vlib/counter.c
src/vlib/counter.h
src/vlib/error.c
src/vlib/main.c
src/vlibmemory/memory_api.c
src/vlibmemory/vlib_api.c
src/vnet/interface.c
src/vpp.am
src/vpp/app/stat_client.c [new file with mode: 0644]
src/vpp/app/stat_client.h [new file with mode: 0644]
src/vpp/stats/stat_segment.c [new file with mode: 0644]
src/vpp/stats/stats.api
src/vpp/stats/stats.c
src/vpp/stats/stats.h

index f17802d..161e309 100644 (file)
@@ -5884,7 +5884,8 @@ _(SESSION_RULE_ADD_DEL_REPLY, session_rule_add_del_reply)         \
 _(SESSION_RULES_DETAILS, session_rules_details)                                \
 _(IP_CONTAINER_PROXY_ADD_DEL_REPLY, ip_container_proxy_add_del_reply)  \
 _(OUTPUT_ACL_SET_INTERFACE_REPLY, output_acl_set_interface_reply)       \
-_(QOS_RECORD_ENABLE_DISABLE_REPLY, qos_record_enable_disable_reply)
+_(QOS_RECORD_ENABLE_DISABLE_REPLY, qos_record_enable_disable_reply)    \
+_(MAP_STATS_SEGMENT_REPLY, map_stats_segment_reply)
 
 #define foreach_standalone_reply_msg                                   \
 _(SW_INTERFACE_EVENT, sw_interface_event)                               \
@@ -22431,6 +22432,92 @@ api_app_namespace_add_del (vat_main_t * vam)
   return ret;
 }
 
+static void vl_api_map_stats_segment_reply_t_handler
+  (vl_api_map_stats_segment_reply_t * mp)
+{
+#if VPP_API_TEST_BUILTIN == 0
+  vat_main_t *vam = &vat_main;
+  ssvm_private_t *ssvmp = &vam->stat_segment;
+  ssvm_shared_header_t *shared_header;
+  socket_client_main_t *scm = vam->socket_client_main;
+  int rv = ntohl (mp->retval);
+  int my_fd, retval;
+  clib_error_t *error;
+
+  vam->retval = rv;
+
+  if (rv != 0)
+    {
+      vam->result_ready = 1;
+      return;
+    }
+
+  /*
+   * Check the socket for the magic fd
+   */
+  error = vl_sock_api_recv_fd_msg (scm->socket_fd, &my_fd, 5);
+  if (error)
+    {
+      clib_error_report (error);
+      vam->retval = -99;
+      vam->result_ready = 1;
+      return;
+    }
+
+  memset (ssvmp, 0, sizeof (*ssvmp));
+  ssvmp->fd = my_fd;
+
+  /* Note: this closes memfd.fd */
+  retval = ssvm_slave_init_memfd (ssvmp);
+  if (retval)
+    {
+      clib_warning ("WARNING: segment map returned %d", retval);
+      vam->retval = -99;
+      vam->result_ready = 1;
+      return;
+    }
+  else
+    errmsg ("stat segment mapped OK...");
+
+  ASSERT (ssvmp && ssvmp->sh);
+
+  /* Pick up the segment lock from the shared memory header */
+  shared_header = ssvmp->sh;
+  vam->stat_segment_lockp = (clib_spinlock_t *) (shared_header->opaque[0]);
+  vam->retval = 0;
+  vam->result_ready = 1;
+#endif
+}
+
+static void vl_api_map_stats_segment_reply_t_handler_json
+  (vl_api_map_stats_segment_reply_t * mp)
+{
+#if VPP_API_TEST_BUILTIN == 0
+  vat_main_t *vam = &vat_main;
+  clib_warning ("not implemented");
+  vam->retval = -99;
+  vam->result_ready = 1;
+#endif
+}
+
+static int
+api_map_stats_segment (vat_main_t * vam)
+{
+#if VPP_API_TEST_BUILTIN == 0
+  vl_api_map_stats_segment_t *mp;
+  int ret;
+
+  M (MAP_STATS_SEGMENT, mp);
+  S (mp);
+  W (ret);
+
+  return ret;
+#else
+  errmsg ("api unavailable");
+  return -99;
+#endif
+}
+
 static int
 api_sock_init_shm (vat_main_t * vam)
 {
@@ -22956,6 +23043,7 @@ api_qos_record_enable_disable (vat_main_t * vam)
   return ret;
 }
 
+
 static int
 q_or_quit (vat_main_t * vam)
 {
@@ -22983,6 +23071,80 @@ comment (vat_main_t * vam)
   return 0;
 }
 
+static int
+statseg (vat_main_t * vam)
+{
+  ssvm_private_t *ssvmp = &vam->stat_segment;
+  ssvm_shared_header_t *shared_header = ssvmp->sh;
+  vlib_counter_t **counters;
+  u64 thread0_index1_packets;
+  u64 thread0_index1_bytes;
+  f64 vector_rate, input_rate;
+  uword *p;
+
+  uword *counter_vector_by_name;
+  if (vam->stat_segment_lockp == 0)
+    {
+      errmsg ("Stat segment not mapped...");
+      return -99;
+    }
+
+  /* look up "/if/rx for sw_if_index 1 as a test */
+
+  clib_spinlock_lock (vam->stat_segment_lockp);
+
+  counter_vector_by_name = (uword *) shared_header->opaque[1];
+
+  p = hash_get_mem (counter_vector_by_name, "/if/rx");
+  if (p == 0)
+    {
+      clib_spinlock_unlock (vam->stat_segment_lockp);
+      errmsg ("/if/tx not found?");
+      return -99;
+    }
+
+  /* Fish per-thread vector of combined counters from shared memory */
+  counters = (vlib_counter_t **) p[0];
+
+  if (vec_len (counters[0]) < 2)
+    {
+      clib_spinlock_unlock (vam->stat_segment_lockp);
+      errmsg ("/if/tx vector length %d", vec_len (counters[0]));
+      return -99;
+    }
+
+  /* Read thread 0 sw_if_index 1 counter */
+  thread0_index1_packets = counters[0][1].packets;
+  thread0_index1_bytes = counters[0][1].bytes;
+
+  p = hash_get_mem (counter_vector_by_name, "vector_rate");
+  if (p == 0)
+    {
+      clib_spinlock_unlock (vam->stat_segment_lockp);
+      errmsg ("vector_rate not found?");
+      return -99;
+    }
+
+  vector_rate = *(f64 *) (p[0]);
+  p = hash_get_mem (counter_vector_by_name, "input_rate");
+  if (p == 0)
+    {
+      clib_spinlock_unlock (vam->stat_segment_lockp);
+      errmsg ("input_rate not found?");
+      return -99;
+    }
+  input_rate = *(f64 *) (p[0]);
+
+  clib_spinlock_unlock (vam->stat_segment_lockp);
+
+  print (vam->ofp, "vector_rate %.2f input_rate %.2f",
+        vector_rate, input_rate);
+  print (vam->ofp, "thread 0 sw_if_index 1 rx pkts %lld, bytes %lld",
+        thread0_index1_packets, thread0_index1_bytes);
+
+  return 0;
+}
+
 static int
 cmd_cmp (void *a1, void *a2)
 {
@@ -23799,7 +23961,8 @@ _(ip_container_proxy_add_del, "[add|del] <address> <sw_if_index>")      \
 _(output_acl_set_interface,                                             \
   "<intfc> | sw_if_index <nn> [ip4-table <nn>] [ip6-table <nn>]\n"      \
   "  [l2-table <nn>] [del]")                                            \
-_(qos_record_enable_disable, "<record-source> <intfc> | sw_if_index <id> [disable]")
+_(qos_record_enable_disable, "<record-source> <intfc> | sw_if_index <id> [disable]") \
+_(map_stats_segment, "<no-args>")
 
 /* List of command functions, CLI names map directly to functions */
 #define foreach_cli_function                                    \
@@ -23822,7 +23985,9 @@ _(quit, "usage: quit")                                          \
 _(search_node_table, "usage: search_node_table <name>...")     \
 _(set, "usage: set <variable-name> <value>")                    \
 _(script, "usage: script <file-name>")                          \
+_(statseg, "usage: statseg");                                   \
 _(unset, "usage: unset <variable-name>")
+
 #define _(N,n)                                  \
     static void vl_api_##n##_t_handler_uni      \
     (vl_api_##n##_t * mp)                       \
index beeccd5..19796b9 100644 (file)
@@ -209,6 +209,9 @@ typedef struct
   ip4_nbr_counter_t **ip4_nbr_counters;
   ip6_nbr_counter_t **ip6_nbr_counters;
 
+  ssvm_private_t stat_segment;
+  clib_spinlock_t *stat_segment_lockp;
+
   socket_client_main_t *socket_client_main;
   u8 *socket_name;
 
index 89e1841..ccb323a 100644 (file)
@@ -25,7 +25,6 @@ libvppcom_la_SOURCES +=                               \
   vcl/vcl_event.c               \
   vcl/vppcom.c                                 \
   $(libvppinfra_la_SOURCES)                    \
-  $(libvlib_la_SOURCES)                                \
   $(libsvm_la_SOURCES)                         \
   $(libvlibmemoryclient_la_SOURCES)
 
index b1e6bbe..8756f9b 100644 (file)
@@ -87,13 +87,4 @@ nobase_include_HEADERS +=                    \
   vlib/unix/plugin.h                           \
   vlib/unix/unix.h
 
-noinst_PROGRAMS += vlib_unix
-
-vlib_unix_SOURCES =                            \
-  examples/vlib/main_stub.c                    \
-  examples/vlib/mc_test.c
-
-vlib_unix_LDADD = libvlib.la \
-                 libvppinfra.la -lpthread -lm -ldl -lrt
-
 # vi:syntax=automake
index 62f4bd6..29cd004 100644 (file)
@@ -74,15 +74,32 @@ vlib_clear_combined_counters (vlib_combined_counter_main_t * cm)
     }
 }
 
+void *vlib_stats_push_heap (void) __attribute__ ((weak));
+void *
+vlib_stats_push_heap (void)
+{
+  return 0;
+};
+
+void vlib_stats_pop_heap (void *, void *) __attribute__ ((weak));
+void
+vlib_stats_pop_heap (void *notused, void *notused2)
+{
+};
+
+
 void
 vlib_validate_simple_counter (vlib_simple_counter_main_t * cm, u32 index)
 {
   vlib_thread_main_t *tm = vlib_get_thread_main ();
   int i;
+  void *oldheap = vlib_stats_push_heap ();
 
   vec_validate (cm->counters, tm->n_vlib_mains - 1);
   for (i = 0; i < tm->n_vlib_mains; i++)
     vec_validate_aligned (cm->counters[i], index, CLIB_CACHE_LINE_BYTES);
+
+  vlib_stats_pop_heap (cm, oldheap);
 }
 
 void
@@ -90,10 +107,13 @@ vlib_validate_combined_counter (vlib_combined_counter_main_t * cm, u32 index)
 {
   vlib_thread_main_t *tm = vlib_get_thread_main ();
   int i;
+  void *oldheap = vlib_stats_push_heap ();
 
   vec_validate (cm->counters, tm->n_vlib_mains - 1);
   for (i = 0; i < tm->n_vlib_mains; i++)
     vec_validate_aligned (cm->counters[i], index, CLIB_CACHE_LINE_BYTES);
+
+  vlib_stats_pop_heap (cm, oldheap);
 }
 
 u32
index 60e2055..fe5279a 100644 (file)
@@ -63,6 +63,7 @@ typedef struct
                                            serialized incrementally. */
 
   char *name;                  /**< The counter collection's name. */
+  char *stat_segment_name;    /**< Name in stat segment directory */
 } vlib_simple_counter_main_t;
 
 /** The number of counters (not the number of per-thread counters) */
@@ -183,6 +184,7 @@ typedef struct
   vlib_counter_t *value_at_last_serialize; /**< Counter values as of last serialize. */
   u32 last_incremental_serialize_index;        /**< Last counter index serialized incrementally. */
   char *name; /**< The counter collection's name. */
+  char *stat_segment_name;     /**< Name in stat segment directory */
 } vlib_combined_counter_main_t;
 
 /** The number of counters (not the number of per-thread counters) */
index dec90bb..3681182 100644 (file)
@@ -140,6 +140,19 @@ VLIB_REGISTER_NODE (misc_drop_buffers_node,static) = {
 };
 /* *INDENT-ON* */
 
+void vlib_stats_register_error_index (u8 *, u64) __attribute__ ((weak));
+void
+vlib_stats_register_error_index (u8 * notused, u64 notused2)
+{
+};
+
+void vlib_stats_pop_heap2 (void *, u32, void *) __attribute__ ((weak));
+void
+vlib_stats_pop_heap2 (void *notused, u32 notused2, void *notused3)
+{
+};
+
+
 /* Reserves given number of error codes for given node. */
 void
 vlib_register_errors (vlib_main_t * vm,
@@ -148,6 +161,8 @@ vlib_register_errors (vlib_main_t * vm,
   vlib_error_main_t *em = &vm->error_main;
   vlib_node_t *n = vlib_get_node (vm, node_index);
   uword l;
+  void *oldheap;
+  void *vlib_stats_push_heap (void) __attribute__ ((weak));
 
   ASSERT (vlib_get_thread_index () == 0);
 
@@ -169,9 +184,13 @@ vlib_register_errors (vlib_main_t * vm,
   clib_memcpy (vec_elt_at_index (em->error_strings_heap, n->error_heap_index),
               error_strings, n_errors * sizeof (error_strings[0]));
 
+  vec_validate (vm->error_elog_event_types, l - 1);
+
+  /* Switch to the stats segment ... */
+  oldheap = vlib_stats_push_heap ();
+
   /* Allocate a counter/elog type for each error. */
   vec_validate (em->counters, l - 1);
-  vec_validate (vm->error_elog_event_types, l - 1);
 
   /* Zero counters for re-registrations of errors. */
   if (n->error_heap_index + n_errors <= vec_len (em->counters_last_clear))
@@ -182,6 +201,22 @@ vlib_register_errors (vlib_main_t * vm,
     memset (em->counters + n->error_heap_index,
            0, n_errors * sizeof (em->counters[0]));
 
+  /* Register counter indices in the stat segment directory */
+  {
+    int i;
+    u8 *error_name;
+
+    for (i = 0; i < n_errors; i++)
+      {
+       error_name = format (0, "/err/%s%c", error_strings[i], 0);
+       /* Note: error_name consumed by the following call */
+       vlib_stats_register_error_index (error_name, n->error_heap_index + i);
+      }
+  }
+
+  /* (re)register the em->counters base address, switch back to main heap */
+  vlib_stats_pop_heap2 (em->counters, vm->thread_index, oldheap);
+
   {
     elog_event_type_t t;
     uword i;
index 7da5192..e4c4438 100644 (file)
@@ -1717,6 +1717,12 @@ vlib_main (vlib_main_t * volatile vm, unformat_input_t * input)
       goto done;
     }
 
+  if ((error = vlib_call_init_function (vm, map_stat_segment_init)))
+    {
+      clib_error_report (error);
+      goto done;
+    }
+
   /* Register static nodes so that init functions may use them. */
   vlib_register_all_static_nodes (vm);
 
@@ -1736,6 +1742,24 @@ vlib_main (vlib_main_t * volatile vm, unformat_input_t * input)
        goto done;
     }
 
+  if ((error = vlib_call_init_function (vm, vpe_api_init)))
+    {
+      clib_error_report (error);
+      goto done;
+    }
+
+  if ((error = vlib_call_init_function (vm, vlibmemory_init)))
+    {
+      clib_error_report (error);
+      goto done;
+    }
+
+  if ((error = vlib_call_init_function (vm, map_api_segment_init)))
+    {
+      clib_error_report (error);
+      goto done;
+    }
+
   /* See unix/main.c; most likely already set up */
   if (vm->init_functions_called == 0)
     vm->init_functions_called = hash_create (0, /* value bytes */ 0);
index 205bea5..4d31b35 100644 (file)
@@ -459,6 +459,22 @@ vl_mem_api_init (const char *region_name)
   return 0;
 }
 
+static clib_error_t *
+map_api_segment_init (vlib_main_t * vm)
+{
+  api_main_t *am = &api_main;
+  int rv;
+
+  if ((rv = vl_mem_api_init (am->region_name)) < 0)
+    {
+      return clib_error_return (0, "vl_mem_api_init (%s) failed",
+                               am->region_name);
+    }
+  return 0;
+}
+
+VLIB_INIT_FUNCTION (map_api_segment_init);
+
 static void
 send_memclnt_keepalive (vl_api_registration_t * regp, f64 now)
 {
index 1067bf3..eb7655c 100644 (file)
@@ -287,12 +287,6 @@ vl_api_clnt_process (vlib_main_t * vm, vlib_node_runtime_t * node,
   uword *event_data = 0;
   f64 now;
 
-  if ((rv = vl_mem_api_init (am->region_name)) < 0)
-    {
-      clib_warning ("memory_api_init returned %d, quitting...", rv);
-      return 0;
-    }
-
   if ((error = vl_sock_api_init (vm)))
     {
       clib_error_report (error);
index 9300074..797fe44 100644 (file)
@@ -1250,6 +1250,10 @@ vnet_interface_init (vlib_main_t * vm)
                                                   CLIB_CACHE_LINE_BYTES);
   im->sw_if_counter_lock[0] = 1;       /* should be no need */
 
+  /*
+   * $$$$ add stat segment name(s) if desired
+   * set xxx.stat_segment_name = "whatever"...
+   */
   vec_validate (im->sw_if_counters, VNET_N_SIMPLE_INTERFACE_COUNTER - 1);
   im->sw_if_counters[VNET_INTERFACE_COUNTER_DROP].name = "drops";
   im->sw_if_counters[VNET_INTERFACE_COUNTER_PUNT].name = "punts";
index 0e9ddb0..e8c0155 100644 (file)
@@ -11,7 +11,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-bin_PROGRAMS += bin/vpp
+bin_PROGRAMS += bin/vpp 
 
 bin_vpp_SOURCES =                              \
   vpp/vnet/main.c                              \
@@ -19,7 +19,8 @@ bin_vpp_SOURCES =                             \
   vpp/app/version.c                            \
   vpp/oam/oam.c                                        \
   vpp/oam/oam_api.c                            \
-  vpp/stats/stats.c
+  vpp/stats/stats.c                            \
+  vpp/stats/stat_segment.c
 
 bin_vpp_SOURCES +=                             \
   vpp/api/api.c                                        \
@@ -130,6 +131,20 @@ bin_summary_stats_client_LDADD = \
    libvppinfra.la \
    -lpthread -lm -lrt
 
+noinst_PROGRAMS += bin/stat_client
+
+bin_stat_client_SOURCES = \
+  vpp/app/stat_client.c \
+  vpp/app/stat_client.h
+
+bin_stat_client_LDADD = \
+  libvlibmemoryclient.la \
+  libsvm.la \
+  libvppinfra.la \
+  -lpthread -lm -lrt
+
+
+
 bin_PROGRAMS += bin/vpp_get_metrics
 
 bin_vpp_get_metrics_SOURCES = \
diff --git a/src/vpp/app/stat_client.c b/src/vpp/app/stat_client.c
new file mode 100644 (file)
index 0000000..610a6a5
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ *------------------------------------------------------------------
+ * api_format.c
+ *
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+#include <vpp/app/stat_client.h>
+
+#include <vpp/api/vpe_msg_enum.h>
+
+#define vl_typedefs            /* define message structures */
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_typedefs
+
+#define vl_endianfun           /* define endian fcns */
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...) fformat (handle, __VA_ARGS__)
+#define vl_printfun
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_printfun
+
+stat_client_main_t stat_client_main;
+
+static void vl_api_map_stats_segment_reply_t_handler
+  (vl_api_map_stats_segment_reply_t * mp)
+{
+  stat_client_main_t *sm = &stat_client_main;
+  ssvm_private_t *ssvmp = &sm->stat_segment;
+  ssvm_shared_header_t *shared_header;
+  socket_client_main_t *scm = sm->socket_client_main;
+  int rv = ntohl (mp->retval);
+  int my_fd, retval;
+  clib_error_t *error;
+
+  if (rv != 0)
+    {
+      fformat (stderr, "ERROR mapping stats segment: %d", rv);
+      exit (1);
+    }
+
+  /*
+   * Check the socket for the magic fd
+   */
+  error = vl_sock_api_recv_fd_msg (scm->socket_fd, &my_fd, 5);
+  if (error)
+    {
+      clib_error_report (error);
+      exit (1);
+    }
+
+  memset (ssvmp, 0, sizeof (*ssvmp));
+  ssvmp->fd = my_fd;
+
+  /* Note: this closes memfd.fd */
+  retval = ssvm_slave_init_memfd (ssvmp);
+  if (retval)
+    {
+      clib_warning ("WARNING: segment map returned %d", retval);
+      exit (1);
+    }
+
+  fformat (stdout, "Stat segment mapped OK...\n");
+
+  ASSERT (ssvmp && ssvmp->sh);
+
+  /* Pick up the segment lock from the shared memory header */
+  shared_header = ssvmp->sh;
+  sm->stat_segment_lockp = (clib_spinlock_t *) (shared_header->opaque[0]);
+  sm->segment_ready = 1;
+
+  /* No need to keep the socket API connection open */
+  close (sm->socket_client_main->socket_fd);
+}
+
+#define foreach_api_reply_msg \
+_(MAP_STATS_SEGMENT_REPLY, map_stats_segment_reply)
+
+static void
+vpp_api_hookup (void)
+{
+#define _(N,n)                                                  \
+    vl_msg_api_set_handlers(VL_API_##N, #n,                     \
+                           vl_api_##n##_t_handler,             \
+                           vl_noop_handler,                     \
+                           vl_api_##n##_t_endian,               \
+                           vl_api_##n##_t_print,                \
+                           sizeof(vl_api_##n##_t), 1);
+  foreach_api_reply_msg;
+#undef _
+}
+
+static int
+connect_to_vpp (stat_client_main_t * sm)
+{
+  int rv;
+  vl_api_map_stats_segment_t *mp;
+  api_main_t *am = &api_main;
+
+  sm->socket_client_main = &socket_client_main;
+
+  rv = vl_socket_client_connect ((char *) sm->socket_name,
+                                "stat_client",
+                                0 /* default socket rx, tx buffer */ );
+  if (rv)
+    {
+      fformat (stderr, "Error connecting to vpp...\n");
+      exit (1);
+    }
+
+  /* Hook up reply handler */
+  vpp_api_hookup ();
+
+  /* Map the stats segment */
+  mp = vl_socket_client_msg_alloc (sizeof (*mp));
+  mp->_vl_msg_id = ntohs (VL_API_MAP_STATS_SEGMENT);
+  mp->client_index = am->my_client_index;
+  mp->context = 0xdeaddabe;
+
+  /* Send the message */
+  vl_socket_client_write ();
+
+  /* Wait for a reply, process it.. */
+  vl_socket_client_read (5 /* timeout in seconds */ );
+
+  return 0;
+}
+
+#define foreach_cached_pointer                                          \
+_(vector_rate, SCALAR_POINTER, &stat_client_main.vector_rate_ptr)       \
+_(input_rate, SCALAR_POINTER, &stat_client_main.input_rate_ptr)         \
+_(rx, COUNTER_VECTOR, &stat_client_main.intfc_rx_counters)              \
+_(tx, COUNTER_VECTOR, &stat_client_main.intfc_tx_counters)              \
+_(/err/0/counter_vector, VECTOR_POINTER,                                \
+  &stat_client_main.thread_0_error_counts)                              \
+_(/err/IP4 source address matches local interface, ERROR_INDEX,         \
+  &stat_client_main.source_address_match_error_index)
+
+typedef struct
+{
+  char *name;
+  stat_directory_type_t type;
+  void *valuep;
+} cached_pointer_t;
+
+cached_pointer_t cached_pointers[] = {
+#define _(n,t,p) {#n, STAT_DIR_TYPE_##t, (void *)p},
+  foreach_cached_pointer
+#undef _
+};
+
+static void
+maybe_update_cached_pointers (stat_client_main_t * sm,
+                             ssvm_shared_header_t * shared_header)
+{
+  uword *p, *counter_vector_by_name;
+  int i;
+  stat_segment_directory_entry_t *ep;
+  cached_pointer_t *cp;
+  u64 *valuep;
+
+  /* Cached pointers OK? */
+  if (sm->current_epoch ==
+      (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH])
+    return;
+
+  fformat (stdout, "Updating cached pointers...\n");
+
+  /* Nope, fix them... */
+  counter_vector_by_name = (uword *)
+    shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR];
+
+  for (i = 0; i < ARRAY_LEN (cached_pointers); i++)
+    {
+      cp = &cached_pointers[i];
+
+      p = hash_get_mem (counter_vector_by_name, cp->name);
+
+      if (p == 0)
+       {
+         clib_warning ("WARN: %s not in directory!", cp->name);
+         continue;
+       }
+      ep = (stat_segment_directory_entry_t *) (p[0]);
+      ASSERT (ep->type == cp->type);
+      valuep = (u64 *) cp->valuep;
+      *valuep = (u64) ep->value;
+    }
+
+  /* And remember that we did... */
+  sm->current_epoch = (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH];
+}
+
+static void
+stat_poll_loop (stat_client_main_t * sm)
+{
+  struct timespec ts, tsrem;
+  ssvm_private_t *ssvmp = &sm->stat_segment;
+  ssvm_shared_header_t *shared_header;
+  vlib_counter_t *thread0_rx_counters = 0, *thread0_tx_counters = 0;
+  f64 vector_rate, input_rate;
+  u32 len;
+  int i;
+  u32 source_address_match_errors;
+
+  /* Wait until the stats segment is mapped */
+  while (!sm->segment_ready)
+    {
+      ts.tv_sec = 0;
+      ts.tv_nsec = 100000000;
+      while (nanosleep (&ts, &tsrem) < 0)
+       ts = tsrem;
+    }
+
+  shared_header = ssvmp->sh;
+  ASSERT (ssvmp->sh);
+
+  while (1)
+    {
+      /* Scrape stats every 5 seconds */
+      ts.tv_sec = 5;
+      ts.tv_nsec = 0;
+      while (nanosleep (&ts, &tsrem) < 0)
+       ts = tsrem;
+
+      vec_reset_length (thread0_rx_counters);
+      vec_reset_length (thread0_tx_counters);
+
+      /* Grab the stats segment lock */
+      clib_spinlock_lock (sm->stat_segment_lockp);
+
+      /* see if we need to update cached pointers */
+      maybe_update_cached_pointers (sm, shared_header);
+
+      ASSERT (sm->vector_rate_ptr);
+      ASSERT (sm->intfc_rx_counters);
+      ASSERT (sm->intfc_tx_counters);
+
+      /* Read data from the segment */
+      vector_rate = *sm->vector_rate_ptr;
+      input_rate = *sm->input_rate_ptr;
+
+      len = vec_len (sm->intfc_rx_counters[0]);
+
+      ASSERT (len);
+
+      vec_validate (thread0_rx_counters, len - 1);
+      vec_validate (thread0_tx_counters, len - 1);
+
+      clib_memcpy (thread0_rx_counters, sm->intfc_rx_counters[0],
+                  len * sizeof (vlib_counter_t));
+      clib_memcpy (thread0_tx_counters, sm->intfc_tx_counters[0],
+                  len * sizeof (vlib_counter_t));
+
+      source_address_match_errors =
+       sm->thread_0_error_counts[sm->source_address_match_error_index];
+
+      /* Drop the lock */
+      clib_spinlock_unlock (sm->stat_segment_lockp);
+
+      /* And print results... */
+
+      fformat (stdout, "vector_rate %.2f input_rate %.2f\n",
+              vector_rate, input_rate);
+
+      for (i = 0; i < vec_len (thread0_rx_counters); i++)
+       {
+         fformat (stdout, "[%d]: %lld rx packets, %lld rx bytes\n",
+                  i, thread0_rx_counters[i].packets,
+                  thread0_rx_counters[i].bytes);
+         fformat (stdout, "[%d]: %lld tx packets, %lld tx bytes\n",
+                  i, thread0_tx_counters[i].packets,
+                  thread0_tx_counters[i].bytes);
+       }
+
+      fformat (stdout, "%lld source address match errors\n",
+              source_address_match_errors);
+    }
+}
+
+
+int
+main (int argc, char **argv)
+{
+  unformat_input_t _argv, *a = &_argv;
+  stat_client_main_t *sm = &stat_client_main;
+  u8 *socket_name;
+  int rv;
+
+  clib_mem_init (0, 128 << 20);
+
+  unformat_init_command_line (a, argv);
+
+  socket_name = (u8 *) API_SOCKET_FILE;
+
+  while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (a, "socket-name %s", &socket_name))
+       ;
+      else
+       {
+         fformat (stderr, "%s: usage [socket-name <name>]\n", argv[0]);
+         exit (1);
+       }
+    }
+
+  sm->socket_name = socket_name;
+
+  rv = connect_to_vpp (sm);
+
+  if (rv)
+    {
+      fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
+              socket_name);
+      exit (1);
+    }
+
+  stat_poll_loop (sm);
+  exit (0);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vpp/app/stat_client.h b/src/vpp/app/stat_client.h
new file mode 100644 (file)
index 0000000..9cec1ee
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __included_stat_client_h__
+#define __included_stat_client_h__
+
+#include <vlib/vlib.h>
+#include <vppinfra/socket.h>
+#include <svm/ssvm.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vlibmemory/socket_api.h>
+#include <vlibmemory/socket_client.h>
+#include <vpp/stats/stats.h>
+
+typedef struct
+{
+  u64 current_epoch;
+
+  /* Cached pointers to scalar quantities, these wont change */
+  f64 *vector_rate_ptr;
+  f64 *input_rate_ptr;
+
+  volatile int segment_ready;
+
+  /*
+   * Cached pointers to vector quantities,
+   * MUST invalidate when the epoch changes
+   */
+  vlib_counter_t **intfc_rx_counters;
+  vlib_counter_t **intfc_tx_counters;
+
+  u64 *thread_0_error_counts;
+  u64 source_address_match_error_index;
+
+  /* mapped stats segment object */
+  ssvm_private_t stat_segment;
+
+  /* Socket client object */
+  socket_client_main_t *socket_client_main;
+
+  /* Spinlock for the stats segment */
+  clib_spinlock_t *stat_segment_lockp;
+
+  u8 *socket_name;
+} stat_client_main_t;
+
+extern stat_client_main_t stat_client_main;
+
+#endif /* __included_stat_client_h__ */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vpp/stats/stat_segment.c b/src/vpp/stats/stat_segment.c
new file mode 100644 (file)
index 0000000..795e347
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <vpp/stats/stats.h>
+
+void *
+vlib_stats_push_heap (void)
+{
+  stats_main_t *sm = &stats_main;
+  ssvm_private_t *ssvmp = &sm->stat_segment;
+  ssvm_shared_header_t *shared_header;
+
+  ASSERT (ssvmp && ssvmp->sh);
+
+  shared_header = ssvmp->sh;
+
+  return ssvm_push_heap (shared_header);
+}
+
+void
+vlib_stats_pop_heap (void *cm_arg, void *oldheap)
+{
+  vlib_simple_counter_main_t *cm = (vlib_simple_counter_main_t *) cm_arg;
+  stats_main_t *sm = &stats_main;
+  ssvm_private_t *ssvmp = &sm->stat_segment;
+  ssvm_shared_header_t *shared_header;
+  char *stat_segment_name;
+  stat_segment_directory_entry_t *ep;
+  uword *p;
+
+  ASSERT (ssvmp && ssvmp->sh);
+
+  shared_header = ssvmp->sh;
+
+  /* Not all counters have names / hash-table entries */
+  if (cm->name || cm->stat_segment_name)
+    {
+      hash_pair_t *hp;
+      u8 *name_copy;
+
+      stat_segment_name = cm->stat_segment_name ?
+       cm->stat_segment_name : cm->name;
+
+      clib_spinlock_lock (sm->stat_segment_lockp);
+
+      /* Update hash table. The name must be copied into the segment */
+      hp = hash_get_pair (sm->counter_vector_by_name, stat_segment_name);
+      if (hp)
+       {
+         name_copy = (u8 *) hp->key;
+         ep = (stat_segment_directory_entry_t *) (hp->value[0]);
+         hash_unset_mem (sm->counter_vector_by_name, stat_segment_name);
+         vec_free (name_copy);
+         clib_mem_free (ep);
+       }
+      name_copy = format (0, "%s%c", stat_segment_name, 0);
+      ep = clib_mem_alloc (sizeof (*ep));
+      ep->type = STAT_DIR_TYPE_COUNTER_VECTOR;
+      ep->value = cm->counters;
+      hash_set_mem (sm->counter_vector_by_name, name_copy, ep);
+
+      /* Reset the client hash table pointer, since it WILL change! */
+      shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR]
+       = sm->counter_vector_by_name;
+
+      /* Warn clients to refresh any pointers they might be holding */
+      shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
+       ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
+      clib_spinlock_unlock (sm->stat_segment_lockp);
+    }
+  ssvm_pop_heap (oldheap);
+}
+
+void
+vlib_stats_register_error_index (u8 * name, u64 index)
+{
+  stats_main_t *sm = &stats_main;
+  ssvm_private_t *ssvmp = &sm->stat_segment;
+  ssvm_shared_header_t *shared_header;
+  stat_segment_directory_entry_t *ep;
+  hash_pair_t *hp;
+  u8 *name_copy;
+  uword *p;
+
+  ASSERT (ssvmp && ssvmp->sh);
+
+  shared_header = ssvmp->sh;
+
+  clib_spinlock_lock (sm->stat_segment_lockp);
+
+  /* Update hash table. The name must be copied into the segment */
+  hp = hash_get_pair (sm->counter_vector_by_name, name);
+  if (hp)
+    {
+      name_copy = (u8 *) hp->key;
+      ep = (stat_segment_directory_entry_t *) (hp->value[0]);
+      hash_unset_mem (sm->counter_vector_by_name, name);
+      vec_free (name_copy);
+      clib_mem_free (ep);
+    }
+
+  ep = clib_mem_alloc (sizeof (*ep));
+  ep->type = STAT_DIR_TYPE_ERROR_INDEX;
+  ep->value = (void *) index;
+
+  hash_set_mem (sm->counter_vector_by_name, name, ep);
+
+  /* Reset the client hash table pointer, since it WILL change! */
+  shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
+
+  /* Warn clients to refresh any pointers they might be holding */
+  shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
+    ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
+  clib_spinlock_unlock (sm->stat_segment_lockp);
+}
+
+void
+vlib_stats_pop_heap2 (u64 * counter_vector, u32 thread_index, void *oldheap)
+{
+  stats_main_t *sm = &stats_main;
+  ssvm_private_t *ssvmp = &sm->stat_segment;
+  ssvm_shared_header_t *shared_header;
+  stat_segment_directory_entry_t *ep;
+  hash_pair_t *hp;
+  u8 *error_vector_name;
+  u8 *name_copy;
+  uword *p;
+
+  ASSERT (ssvmp && ssvmp->sh);
+
+  shared_header = ssvmp->sh;
+
+  clib_spinlock_lock (sm->stat_segment_lockp);
+
+  error_vector_name = format (0, "/err/%d/counter_vector%c", thread_index, 0);
+
+  /* Update hash table. The name must be copied into the segment */
+  hp = hash_get_pair (sm->counter_vector_by_name, error_vector_name);
+  if (hp)
+    {
+      name_copy = (u8 *) hp->key;
+      ep = (stat_segment_directory_entry_t *) (hp->value[0]);
+      hash_unset_mem (sm->counter_vector_by_name, error_vector_name);
+      vec_free (name_copy);
+      clib_mem_free (ep);
+    }
+
+  ep = clib_mem_alloc (sizeof (*ep));
+  ep->type = STAT_DIR_TYPE_VECTOR_POINTER;
+  ep->value = counter_vector;
+
+  hash_set_mem (sm->counter_vector_by_name, error_vector_name, ep);
+
+  /* Reset the client hash table pointer, since it WILL change! */
+  shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
+
+  /* Warn clients to refresh any pointers they might be holding */
+  shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
+    ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
+  clib_spinlock_unlock (sm->stat_segment_lockp);
+  ssvm_pop_heap (oldheap);
+}
+
+static clib_error_t *
+map_stat_segment_init (vlib_main_t * vm)
+{
+  stats_main_t *sm = &stats_main;
+  ssvm_private_t *ssvmp = &sm->stat_segment;
+  ssvm_shared_header_t *shared_header;
+  stat_segment_directory_entry_t *ep;
+  f64 *scalar_data;
+  u8 *name;
+  void *oldheap;
+  u32 *lock;
+  int rv;
+
+  ssvmp->ssvm_size = 32 << 20; /*$$$$$ CONFIG PARAM */
+  ssvmp->i_am_master = 1;
+  ssvmp->my_pid = getpid ();
+  ssvmp->name = format (0, "/stats%c", 0);
+  ssvmp->requested_va = 0;
+
+  rv = ssvm_master_init (ssvmp, SSVM_SEGMENT_MEMFD);
+
+  if (rv)
+    return clib_error_return (0, "stat segment ssvm init failure");
+  shared_header = ssvmp->sh;
+
+  oldheap = ssvm_push_heap (shared_header);
+
+  /* Set up the name to counter-vector hash table */
+  sm->counter_vector_by_name = hash_create_string (0, sizeof (uword));
+
+  sm->stat_segment_lockp = clib_mem_alloc (sizeof (clib_spinlock_t));
+
+  /* Save the hash table address in the shared segment, for clients */
+  clib_spinlock_init (sm->stat_segment_lockp);
+  shared_header->opaque[STAT_SEGMENT_OPAQUE_LOCK] = sm->stat_segment_lockp;
+  shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *) 1;
+
+  /* Set up a few scalar stats */
+
+  scalar_data = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
+                                       CLIB_CACHE_LINE_BYTES);
+  sm->vector_rate_ptr = (scalar_data + 0);
+  sm->input_rate_ptr = (scalar_data + 1);
+
+  name = format (0, "vector_rate%c", 0);
+  ep = clib_mem_alloc (sizeof (*ep));
+  ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
+  ep->value = sm->vector_rate_ptr;
+
+  hash_set_mem (sm->counter_vector_by_name, name, ep);
+
+  name = format (0, "input_rate%c", 0);
+  ep = clib_mem_alloc (sizeof (*ep));
+  ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
+  ep->value = sm->input_rate_ptr;
+
+  hash_set_mem (sm->counter_vector_by_name, name, ep);
+
+  /* Publish the hash table */
+  shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
+
+  ssvm_pop_heap (oldheap);
+
+  return 0;
+}
+
+VLIB_INIT_FUNCTION (map_stat_segment_init);
+
+typedef struct
+{
+  u8 *name;
+  stat_segment_directory_entry_t *dir_entry;
+} show_stat_segment_t;
+
+static int
+name_sort_cmp (void *a1, void *a2)
+{
+  show_stat_segment_t *n1 = a1;
+  show_stat_segment_t *n2 = a2;
+
+  return strcmp ((char *) n1->name, (char *) n2->name);
+}
+
+static u8 *
+format_stat_dir_entry (u8 * s, va_list * args)
+{
+  stat_segment_directory_entry_t *ep =
+    va_arg (*args, stat_segment_directory_entry_t *);
+  char *type_name;
+  char *format_string;
+
+  format_string = "%-10s %20llx";
+
+  switch (ep->type)
+    {
+    case STAT_DIR_TYPE_SCALAR_POINTER:
+      type_name = "ScalarPtr";
+      break;
+
+    case STAT_DIR_TYPE_VECTOR_POINTER:
+      type_name = "VectorPtr";
+      break;
+
+    case STAT_DIR_TYPE_COUNTER_VECTOR:
+      type_name = "CMainPtr";
+      break;
+
+    case STAT_DIR_TYPE_ERROR_INDEX:
+      type_name = "ErrIndex";
+      format_string = "%-10s %20lld";
+      break;
+
+    default:
+      type_name = "illegal!";
+      break;
+    }
+
+  return format (s, format_string, type_name, ep->value);
+}
+
+
+
+static clib_error_t *
+show_stat_segment_command_fn (vlib_main_t * vm,
+                             unformat_input_t * input,
+                             vlib_cli_command_t * cmd)
+{
+  stats_main_t *sm = &stats_main;
+  ssvm_private_t *ssvmp = &sm->stat_segment;
+  ssvm_shared_header_t *shared_header;
+  counter_t *counter;
+  hash_pair_t *p;
+  show_stat_segment_t *show_data = 0;
+  show_stat_segment_t *this;
+  int i;
+
+  int verbose = 0;
+  u8 *s;
+
+  if (unformat (input, "verbose"))
+    verbose = 1;
+
+  clib_spinlock_lock (sm->stat_segment_lockp);
+
+  /* *INDENT-OFF* */
+  hash_foreach_pair (p, sm->counter_vector_by_name,
+  ({
+    vec_add2 (show_data, this, 1);
+
+    this->name = (u8 *) (p->key);
+    this->dir_entry = (stat_segment_directory_entry_t *)(p->value[0]);
+  }));
+  /* *INDENT-ON* */
+
+  clib_spinlock_unlock (sm->stat_segment_lockp);
+
+  vec_sort_with_function (show_data, name_sort_cmp);
+
+  vlib_cli_output (vm, "%-60s %10s %20s", "Name", "Type", "Value");
+
+  for (i = 0; i < vec_len (show_data); i++)
+    {
+      this = vec_elt_at_index (show_data, i);
+
+      vlib_cli_output (vm, "%-60s %31U",
+                      this->name, format_stat_dir_entry, this->dir_entry);
+    }
+
+  if (verbose)
+    {
+      ASSERT (ssvmp && ssvmp->sh);
+
+      shared_header = ssvmp->sh;
+
+      vlib_cli_output (vm, "%U", format_mheap,
+                      shared_header->heap, 0 /* verbose */ );
+    }
+
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_stat_segment_command, static) =
+{
+  .path = "show statistics segment",
+  .short_help = "show statistics segment [verbose]",
+  .function = show_stat_segment_command_fn,
+};
+/* *INDENT-ON* */
+
+static uword
+stat_segment_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
+                     vlib_frame_t * f)
+{
+  f64 vector_rate;
+  u64 input_packets, last_input_packets;
+  f64 last_runtime, dt, now;
+  vlib_main_t *this_vlib_main;
+  stats_main_t *sm = &stats_main;
+
+  last_runtime = 0.0;
+  last_input_packets = 0;
+
+  last_runtime = 0.0;
+  last_input_packets = 0;
+
+  while (1)
+    {
+      vlib_process_suspend (vm, 5.0);
+
+      /*
+       * Compute the average vector rate across all workers
+       */
+      vector_rate = 0.0;
+
+      /* *INDENT-OFF* */
+      foreach_vlib_main
+      ({
+        vector_rate += vlib_last_vector_length_per_node (vm);
+      });
+      /* *INDENT-ON* */
+
+      *sm->vector_rate_ptr = vector_rate / ((f64) vec_len (vlib_mains));
+      now = vlib_time_now (vm);
+      dt = now - last_runtime;
+      input_packets = vnet_get_aggregate_rx_packets ();
+      *sm->input_rate_ptr = (f64) (input_packets - last_input_packets) / dt;
+      last_runtime = now;
+      last_input_packets = input_packets;
+    }
+
+  return 0;                    /* not so much */
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (stat_segment_node,static) =
+{
+  .function = stat_segment_process,
+  .type = VLIB_NODE_TYPE_PROCESS,
+  .name = "stat-segment-process",
+};
+/* *INDENT-ON* */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index 86eaa62..bd1c3f6 100644 (file)
@@ -58,6 +58,8 @@ service {
   rpc want_udp_encap_stats
     returns want_udp_encap_stats_reply
     events vnet_udp_encap_counters;
+  rpc map_stats_segment
+    returns map_stats_segment_reply;
 };
 
 /** \brief Want Stats, enable/disable ALL stats updates
@@ -472,6 +474,13 @@ manual_print manual_endian define vnet_udp_encap_counters
   vl_api_udp_encap_counter_t c[count];
 };
 
+autoreply define map_stats_segment
+{
+  u32 client_index;
+  u32 context;
+}; 
+
+
 /*
  * Local Variables:
  * eval: (c-set-style "gnu")
index fc4b905..bf0bc50 100644 (file)
@@ -48,24 +48,25 @@ stats_main_t stats_main;
 #define foreach_stats_msg                                              \
 _(WANT_STATS, want_stats)                                              \
 _(VNET_INTERFACE_SIMPLE_COUNTERS, vnet_interface_simple_counters)      \
-_(WANT_INTERFACE_SIMPLE_STATS, want_interface_simple_stats)    \
+_(WANT_INTERFACE_SIMPLE_STATS, want_interface_simple_stats)             \
 _(VNET_INTERFACE_COMBINED_COUNTERS, vnet_interface_combined_counters)  \
-_(WANT_INTERFACE_COMBINED_STATS, want_interface_combined_stats)        \
+_(WANT_INTERFACE_COMBINED_STATS, want_interface_combined_stats)         \
 _(WANT_PER_INTERFACE_COMBINED_STATS, want_per_interface_combined_stats)        \
-_(WANT_PER_INTERFACE_SIMPLE_STATS, want_per_interface_simple_stats) \
+_(WANT_PER_INTERFACE_SIMPLE_STATS, want_per_interface_simple_stats)     \
 _(VNET_IP4_FIB_COUNTERS, vnet_ip4_fib_counters)                                \
-_(WANT_IP4_FIB_STATS, want_ip4_fib_stats)            \
+_(WANT_IP4_FIB_STATS, want_ip4_fib_stats)                               \
 _(VNET_IP6_FIB_COUNTERS, vnet_ip6_fib_counters)                                \
-_(WANT_IP6_FIB_STATS, want_ip6_fib_stats)        \
+_(WANT_IP6_FIB_STATS, want_ip6_fib_stats)                               \
 _(WANT_IP4_MFIB_STATS, want_ip4_mfib_stats)                             \
 _(WANT_IP6_MFIB_STATS, want_ip6_mfib_stats)                             \
 _(VNET_IP4_NBR_COUNTERS, vnet_ip4_nbr_counters)                                \
-_(WANT_IP4_NBR_STATS, want_ip4_nbr_stats)            \
-_(VNET_IP6_NBR_COUNTERS, vnet_ip6_nbr_counters) \
-_(WANT_IP6_NBR_STATS, want_ip6_nbr_stats) \
-_(VNET_GET_SUMMARY_STATS, vnet_get_summary_stats) \
-_(STATS_GET_POLLER_DELAY, stats_get_poller_delay) \
-_(WANT_UDP_ENCAP_STATS, want_udp_encap_stats)
+_(WANT_IP4_NBR_STATS, want_ip4_nbr_stats)                               \
+_(VNET_IP6_NBR_COUNTERS, vnet_ip6_nbr_counters)                         \
+_(WANT_IP6_NBR_STATS, want_ip6_nbr_stats)                               \
+_(VNET_GET_SUMMARY_STATS, vnet_get_summary_stats)                       \
+_(STATS_GET_POLLER_DELAY, stats_get_poller_delay)                       \
+_(WANT_UDP_ENCAP_STATS, want_udp_encap_stats)                           \
+_(MAP_STATS_SEGMENT, map_stats_segment)
 
 #define vl_msg_name_crc_list
 #include <vpp/stats/stats.api.h>
@@ -2934,6 +2935,50 @@ stats_memclnt_delete_callback (u32 client_index)
 #define vl_api_vnet_ip4_nbr_counters_t_print vl_noop_handler
 #define vl_api_vnet_ip6_nbr_counters_t_endian vl_noop_handler
 #define vl_api_vnet_ip6_nbr_counters_t_print vl_noop_handler
+#define vl_api_map_stats_segment_t_print vl_noop_handler
+
+static void
+vl_api_map_stats_segment_t_handler (vl_api_map_stats_segment_t * mp)
+{
+  vl_api_map_stats_segment_reply_t *rmp;
+  stats_main_t *sm = &stats_main;
+  ssvm_private_t *ssvmp = &sm->stat_segment;
+  vl_api_registration_t *regp;
+  api_main_t *am = &api_main;
+  clib_file_t *cf;
+  vl_api_shm_elem_config_t *config = 0;
+  vl_shmem_hdr_t *shmem_hdr;
+  int rv = 0;
+
+  regp = vl_api_client_index_to_registration (mp->client_index);
+  if (regp == 0)
+    {
+      clib_warning ("API client disconnected");
+      return;
+    }
+  if (regp->registration_type != REGISTRATION_TYPE_SOCKET_SERVER)
+    rv = VNET_API_ERROR_INVALID_REGISTRATION;
+
+  rmp = vl_msg_api_alloc (sizeof (*rmp));
+  rmp->_vl_msg_id = htons (VL_API_MAP_STATS_SEGMENT_REPLY);
+  rmp->context = mp->context;
+  rmp->retval = htonl (rv);
+
+  vl_api_send_msg (regp, (u8 *) rmp);
+
+  if (rv != 0)
+    return;
+
+  /*
+   * We need the reply message to make it out the back door
+   * before we send the magic fd message so force a flush
+   */
+  cf = vl_api_registration_file (regp);
+  cf->write_function (cf);
+
+  /* Send the magic "here's your sign (aka fd)" socket message */
+  vl_sock_api_send_fd_msg (cf->file_descriptor, ssvmp->fd);
+}
 
 static clib_error_t *
 stats_init (vlib_main_t * vm)
index 629ac98..fd1ab27 100644 (file)
@@ -27,6 +27,7 @@
 #include <vlibmemory/api.h>
 #include <vlibapi/api_helper_macros.h>
 #include <svm/queue.h>
+#include <svm/ssvm.h>
 
 typedef struct
 {
@@ -157,6 +158,15 @@ typedef struct
   vpe_client_stats_registration_t **regs_tmp;
   vpe_client_registration_t **clients_tmp;
 
+  /* statistics segment */
+  ssvm_private_t stat_segment;
+  uword *counter_vector_by_name;
+  clib_spinlock_t *stat_segment_lockp;
+
+  /* Pointers to scalar stats maintained by the stat segment process */
+  f64 *input_rate_ptr;
+  f64 *vector_rate_ptr;
+
   /* convenience */
   vlib_main_t *vlib_main;
   vnet_main_t *vnet_main;
@@ -166,6 +176,25 @@ typedef struct
 
 extern stats_main_t stats_main;
 
+#define STAT_SEGMENT_OPAQUE_LOCK       0
+#define STAT_SEGMENT_OPAQUE_DIR                1
+#define STAT_SEGMENT_OPAQUE_EPOCH      2
+
+typedef enum
+{
+  STAT_DIR_TYPE_ILLEGAL = 0,
+  STAT_DIR_TYPE_SCALAR_POINTER,
+  STAT_DIR_TYPE_VECTOR_POINTER,
+  STAT_DIR_TYPE_COUNTER_VECTOR,
+  STAT_DIR_TYPE_ERROR_INDEX,
+} stat_directory_type_t;
+
+typedef struct
+{
+  stat_directory_type_t type;
+  void *value;
+} stat_segment_directory_entry_t;
+
 #endif /* __included_stats_h__ */
 
 /*