export counters in a memfd segment
[vpp.git] / src / vpp / app / stat_client.c
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:
+ */