aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorOle Troan <ot@cisco.com>2018-08-23 13:00:53 +0200
committerDamjan Marion <dmarion@me.com>2018-08-29 09:39:38 +0000
commit2fee16787ed0d622631223567635a77e14c8c076 (patch)
treeaab9492cc925b7cfa769d996fd6fbbf1d0506971 /src
parentfcf9497d3bcd34b8b5090ee053575296cf56c5e6 (diff)
STATS: stat_client updates.
New stat segment client library: vpp-api/client/stat_client.h New stat segment query app: vpp_get_stats [ls | dump | poll ] <pattern ...> Prometheus integration through: vpp_prometheus_export <pattern> Change-Id: I6f370cf599e9fcf066f22965a62d3a8acd529994 Signed-off-by: Ole Troan <ot@cisco.com>
Diffstat (limited to 'src')
-rw-r--r--src/vlib/counter.c11
-rw-r--r--src/vlib/error.c2
-rw-r--r--src/vpp-api.am5
-rw-r--r--src/vpp-api/CMakeLists.txt2
-rw-r--r--src/vpp-api/client/libvppapiclient.map12
-rw-r--r--src/vpp-api/client/stat_client.c372
-rw-r--r--src/vpp-api/client/stat_client.h64
-rw-r--r--src/vpp.am27
-rw-r--r--src/vpp/CMakeLists.txt11
-rw-r--r--src/vpp/app/stat_client.c378
-rw-r--r--src/vpp/app/stat_client.h66
-rw-r--r--src/vpp/app/vpp_get_stats.c242
-rw-r--r--src/vpp/app/vpp_prometheus_export.c320
-rw-r--r--src/vpp/stats/stat_segment.c18
-rw-r--r--src/vpp/stats/stats.h4
15 files changed, 1064 insertions, 470 deletions
diff --git a/src/vlib/counter.c b/src/vlib/counter.c
index 29cd004fc3e..6afa73e0a7d 100644
--- a/src/vlib/counter.c
+++ b/src/vlib/counter.c
@@ -81,13 +81,12 @@ vlib_stats_push_heap (void)
return 0;
};
-void vlib_stats_pop_heap (void *, void *) __attribute__ ((weak));
+void vlib_stats_pop_heap (void *, void *, int) __attribute__ ((weak));
void
-vlib_stats_pop_heap (void *notused, void *notused2)
+vlib_stats_pop_heap (void *notused, void *notused2, int type)
{
};
-
void
vlib_validate_simple_counter (vlib_simple_counter_main_t * cm, u32 index)
{
@@ -99,7 +98,8 @@ vlib_validate_simple_counter (vlib_simple_counter_main_t * cm, u32 index)
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);
+ vlib_stats_pop_heap (cm, oldheap,
+ 3 /* STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE */ );
}
void
@@ -113,7 +113,8 @@ vlib_validate_combined_counter (vlib_combined_counter_main_t * cm, u32 index)
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);
+ vlib_stats_pop_heap (cm, oldheap,
+ 4 /*STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED */ );
}
u32
diff --git a/src/vlib/error.c b/src/vlib/error.c
index 3ea62e586ba..aa53324fa8f 100644
--- a/src/vlib/error.c
+++ b/src/vlib/error.c
@@ -208,7 +208,7 @@ vlib_register_errors (vlib_main_t * vm,
for (i = 0; i < n_errors; i++)
{
- error_name = format (0, "/err/%s/%s%c", n->name, error_strings[i], 0);
+ error_name = format (0, "/err/%v/%s%c", n->name, error_strings[i], 0);
/* Note: error_name consumed by the following call */
vlib_stats_register_error_index (error_name, n->error_heap_index + i);
}
diff --git a/src/vpp-api.am b/src/vpp-api.am
index 553eafa83b5..8742556714a 100644
--- a/src/vpp-api.am
+++ b/src/vpp-api.am
@@ -15,8 +15,9 @@
# VPP API C wrapper extension
#
lib_LTLIBRARIES += libvppapiclient.la
-libvppapiclient_la_SOURCES = \
- vpp-api/client/client.c \
+libvppapiclient_la_SOURCES = \
+ vpp-api/client/client.c \
+ vpp-api/client/stat_client.c \
vpp-api/client/libvppapiclient.map
libvppapiclient_la_LIBADD = \
diff --git a/src/vpp-api/CMakeLists.txt b/src/vpp-api/CMakeLists.txt
index 6132df0f737..872e9cc2187 100644
--- a/src/vpp-api/CMakeLists.txt
+++ b/src/vpp-api/CMakeLists.txt
@@ -18,6 +18,7 @@
add_vpp_library (vppapiclient
SOURCES
client/client.c
+ client/stat_client.c
client/libvppapiclient.map
LINK_LIBRARIES vppinfra vlibmemoryclient svm pthread m rt
@@ -26,6 +27,7 @@ add_dependencies(vppapiclient vpp_version_h api_headers)
add_vpp_headers(vpp-api
client/vppapiclient.h
+ client/stat_client.h
)
add_subdirectory(vapi)
diff --git a/src/vpp-api/client/libvppapiclient.map b/src/vpp-api/client/libvppapiclient.map
index a9d8f7dd704..81391ea7687 100644
--- a/src/vpp-api/client/libvppapiclient.map
+++ b/src/vpp-api/client/libvppapiclient.map
@@ -1,5 +1,5 @@
-VPPAPICLIENT_17.07 {
+VPPAPICLIENT_18.10 {
global:
vac_read;
vac_write;
@@ -12,8 +12,14 @@ VPPAPICLIENT_17.07 {
vac_rx_resume;
vac_free;
vac_msg_table_size;
-
api_main;
-
+ stat_segment_connect;
+ stat_segment_disconnect;
+ stat_segment_register;
+ stat_segment_collect;
+ stat_segment_ls;
+ stat_segment_dump;
+ stat_segment_data_free;
+ stat_segment_heartbeat;
local: *;
};
diff --git a/src/vpp-api/client/stat_client.c b/src/vpp-api/client/stat_client.c
new file mode 100644
index 00000000000..d7d20452583
--- /dev/null
+++ b/src/vpp-api/client/stat_client.c
@@ -0,0 +1,372 @@
+/*
+ *------------------------------------------------------------------
+ * stat_client.c - Library for access to VPP statistics segment
+ *
+ * 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 <vlib/vlib.h>
+#include <vppinfra/socket.h>
+#include <svm/ssvm.h>
+#include <vpp/stats/stats.h>
+#include <regex.h>
+#include "stat_client.h"
+
+typedef struct
+{
+ u64 current_epoch;
+ volatile int segment_ready;
+ ssvm_private_t stat_segment; /* mapped stats segment object */
+ ssvm_shared_header_t *shared_header;
+ clib_spinlock_t *stat_segment_lockp; /* Spinlock for the stats segment */
+ uword *counter_vector_by_name;
+ u64 *error_base;
+} stat_client_main_t;
+
+stat_client_main_t stat_client_main;
+
+int
+stat_segment_connect (char *socket_name)
+{
+ stat_client_main_t *sm = &stat_client_main;
+ ssvm_private_t *ssvmp = &sm->stat_segment;
+ clib_socket_t s = { 0 };
+ clib_error_t *err;
+ int fd = -1, retval;
+
+ memset (sm, 0, sizeof (*sm));
+ s.config = socket_name;
+ s.flags = CLIB_SOCKET_F_IS_CLIENT | CLIB_SOCKET_F_SEQPACKET;
+ err = clib_socket_init (&s);
+ if (err)
+ {
+ clib_error_report (err);
+ return -1;
+ }
+ err = clib_socket_recvmsg (&s, 0, 0, &fd, 1);
+ if (err)
+ {
+ clib_error_report (err);
+ return -1;
+ }
+ clib_socket_close (&s);
+
+ memset (ssvmp, 0, sizeof (*ssvmp));
+ ssvmp->fd = fd;
+
+ /* Note: this closes memfd.fd */
+ retval = ssvm_slave_init_memfd (ssvmp);
+ if (retval)
+ {
+ fprintf (stderr, "WARNING: segment map returned %d\n", retval);
+ return -1;
+ }
+
+ ASSERT (ssvmp && ssvmp->sh);
+
+ /* Pick up the segment lock from the shared memory header */
+ sm->shared_header = ssvmp->sh;
+ sm->stat_segment_lockp = (clib_spinlock_t *) (sm->shared_header->opaque[0]);
+ sm->segment_ready = 1;
+
+ sm->counter_vector_by_name =
+ (uword *) sm->shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR];
+
+ return 0;
+}
+
+void
+stat_segment_disconnect (void)
+{
+ stat_client_main_t *sm = &stat_client_main;
+ ssvm_delete_memfd (&sm->stat_segment);
+ return;
+}
+
+/*
+ * The application needs to register which counters it is interested
+ * in.
+ */
+stat_segment_cached_pointer_t *
+stat_segment_register (u8 * stats[])
+{
+ int i;
+ uword *p;
+ stat_client_main_t *sm = &stat_client_main;
+ stat_segment_cached_pointer_t *cp, *cached_pointer_vec = 0;
+
+ for (i = 0; i < vec_len (stats); i++)
+ {
+ p = hash_get_mem (sm->counter_vector_by_name, stats[i]);
+ if (p == 0)
+ {
+ fprintf (stderr, "WARN: %s not in directory!", stats[i]);
+ continue;
+ }
+ vec_add2 (cached_pointer_vec, cp, 1);
+ cp->name = strdup ((char *) stats[i]); // Point to p->key instead?
+ }
+ return cached_pointer_vec;
+}
+
+static u64 *
+get_error_base (u32 thread_index)
+{
+ u64 *error_base = 0;
+ uword *p;
+ stat_client_main_t *sm = &stat_client_main;
+ stat_segment_directory_entry_t *ep;
+
+ /* Special case /err/0/counter_vector */
+ p = hash_get_mem (sm->counter_vector_by_name,
+ format (0, "/err/%d/counter_vector", thread_index));
+ if (p)
+ {
+ ep = (stat_segment_directory_entry_t *) (p[0]);
+ error_base = ep->value;
+ }
+ return error_base;
+}
+
+f64
+stat_segment_heartbeat (void)
+{
+ f64 *heartbeat = 0;
+ uword *p;
+ stat_client_main_t *sm = &stat_client_main;
+ stat_segment_directory_entry_t *ep;
+
+ /* Special case /err/0/counter_vector */
+ p = hash_get_mem (sm->counter_vector_by_name,
+ format (0, "/sys/heartbeat%c", 0));
+ if (p)
+ {
+ ep = (stat_segment_directory_entry_t *) (p[0]);
+ heartbeat = ep->value;
+ }
+ return *heartbeat;
+}
+
+static void
+maybe_update_cached_pointers (stat_segment_cached_pointer_t * cached_pointers)
+{
+ stat_client_main_t *sm = &stat_client_main;
+ stat_segment_cached_pointer_t *cp;
+ uword *p;
+ int i;
+
+ /* Cached pointers OK? */
+ if (sm->current_epoch ==
+ (u64) sm->shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH])
+ return;
+
+ /* Special case /err/0/counter_vector */
+ sm->error_base = get_error_base (0);
+
+ /* Nope, fix them... */
+ for (i = 0; i < vec_len (cached_pointers); i++)
+ {
+ cp = &cached_pointers[i];
+
+ p = hash_get_mem (sm->counter_vector_by_name, cp->name);
+ if (p == 0)
+ {
+ fprintf (stderr, "WARN: %s not in directory!", cp->name);
+ continue;
+ }
+ cp->ep = (stat_segment_directory_entry_t *) (p[0]);
+ }
+
+ /* And remember that we did... */
+ sm->current_epoch =
+ (u64) sm->shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH];
+}
+
+stat_segment_data_t
+copy_data (stat_segment_directory_entry_t * ep, u64 * error_base, char *name)
+{
+ stat_segment_data_t result = { 0 };
+ u32 error_index;
+ int i;
+ vlib_counter_t **combined_c; /* Combined counter */
+ counter_t **simple_c; /* Simple counter */
+ result.type = ep->type;
+ result.name = name;
+ switch (ep->type)
+ {
+ case STAT_DIR_TYPE_SCALAR_POINTER:
+ result.scalar_value = *(f64 *) ep->value;
+ break;
+
+ case STAT_DIR_TYPE_VECTOR_POINTER:
+ result.vector_pointer = ep->value;
+ break;
+
+ case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
+ simple_c = ep->value;
+ result.simple_counter_vec = vec_dup (simple_c);
+ for (i = 0; i < vec_len (simple_c); i++)
+ result.simple_counter_vec[i] = vec_dup (simple_c[i]);
+ break;
+
+ case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
+ combined_c = ep->value;
+ result.combined_counter_vec = vec_dup (combined_c);
+ for (i = 0; i < vec_len (combined_c); i++)
+ result.combined_counter_vec[i] = vec_dup (combined_c[i]);
+ break;
+
+ case STAT_DIR_TYPE_ERROR_INDEX:
+ error_index = (uintptr_t) ep->value;
+ result.error_value = error_base[error_index];
+ break;
+
+ default:
+ fprintf (stderr, "Unknown type: %d", ep->type);
+ }
+ return result;
+}
+
+stat_segment_data_t *
+stat_segment_collect (stat_segment_cached_pointer_t * cached_pointers)
+{
+ stat_client_main_t *sm = &stat_client_main;
+ stat_segment_data_t *res = 0;
+ int i;
+
+ /* Grab the stats segment lock */
+ clib_spinlock_lock (sm->stat_segment_lockp);
+
+ /* see if we need to update cached pointers */
+ maybe_update_cached_pointers (cached_pointers);
+
+ for (i = 0; i < vec_len (cached_pointers); i++)
+ {
+ vec_add1 (res,
+ copy_data (cached_pointers[i].ep, sm->error_base,
+ cached_pointers[i].name));
+ }
+
+ /* Drop the lock */
+ clib_spinlock_unlock (sm->stat_segment_lockp);
+
+ return res;
+}
+
+void
+stat_segment_data_free (stat_segment_data_t * res)
+{
+ int i, j;
+ for (i = 0; i < vec_len (res); i++)
+ {
+ switch (res[i].type)
+ {
+ case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
+ for (j = 0; j < vec_len (res[i].simple_counter_vec); j++)
+ vec_free (res[i].simple_counter_vec[j]);
+ vec_free (res[i].simple_counter_vec);
+ break;
+ case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
+ for (j = 0; j < vec_len (res[i].combined_counter_vec); j++)
+ vec_free (res[i].combined_counter_vec[j]);
+ vec_free (res[i].combined_counter_vec);
+ break;
+ default:
+ ;
+ }
+ }
+ vec_free (res);
+}
+
+u8 **
+stat_segment_ls (u8 ** patterns)
+{
+ stat_client_main_t *sm = &stat_client_main;
+ hash_pair_t *p;
+ u8 **dir = 0;
+ regex_t regex[vec_len (patterns)];
+
+ int i;
+ for (i = 0; i < vec_len (patterns); i++)
+ {
+ int rv = regcomp (&regex[i], (char *) patterns[i], 0);
+ if (rv)
+ {
+ fprintf (stderr, "Could not compile regex %s\n", patterns[i]);
+ return dir;
+ }
+ }
+
+ clib_spinlock_lock (sm->stat_segment_lockp);
+
+ /* *INDENT-OFF* */
+ hash_foreach_pair (p, sm->counter_vector_by_name,
+ ({
+ for (i = 0; i < vec_len(patterns); i++) {
+ int rv = regexec(&regex[i], (char *)p->key, 0, NULL, 0);
+ if (rv == 0) {
+ vec_add1 (dir, (u8 *)p->key);
+ break;
+ }
+ }
+ if (vec_len(patterns) == 0)
+ vec_add1 (dir, (u8 *)p->key);
+ }));
+ /* *INDENT-ON* */
+
+ clib_spinlock_unlock (sm->stat_segment_lockp);
+
+ for (i = 0; i < vec_len (patterns); i++)
+ regfree (&regex[i]);
+
+ return dir;
+}
+
+stat_segment_data_t *
+stat_segment_dump (u8 * stats[])
+{
+ int i;
+ uword *p;
+ stat_client_main_t *sm = &stat_client_main;
+ stat_segment_directory_entry_t *ep;
+ stat_segment_data_t *res = 0;
+
+ clib_spinlock_lock (sm->stat_segment_lockp);
+
+ sm->error_base = get_error_base (0);
+ for (i = 0; i < vec_len (stats); i++)
+ {
+ p = hash_get_mem (sm->counter_vector_by_name, stats[i]);
+ if (p == 0)
+ {
+ fprintf (stderr, "WARN: %s not in directory!", stats[i]);
+ continue;
+ }
+ /* Collect counter */
+ ep = (stat_segment_directory_entry_t *) (p[0]);
+ vec_add1 (res, copy_data (ep, sm->error_base, (char *) stats[i]));
+ }
+ clib_spinlock_unlock (sm->stat_segment_lockp);
+
+ return res;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vpp-api/client/stat_client.h b/src/vpp-api/client/stat_client.h
new file mode 100644
index 00000000000..5f076686fdd
--- /dev/null
+++ b/src/vpp-api/client/stat_client.h
@@ -0,0 +1,64 @@
+/*
+ * stat_client.h - Library for access to VPP statistics segment
+ *
+ * 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 <vpp/stats/stats.h>
+
+typedef struct
+{
+ char *name;
+ stat_directory_type_t type;
+ union
+ {
+ f64 scalar_value;
+ u64 error_value;
+ u64 *vector_pointer;
+ counter_t **simple_counter_vec;
+ vlib_counter_t **combined_counter_vec;
+ };
+} stat_segment_data_t;
+
+typedef struct
+{
+ char *name;
+ stat_segment_directory_entry_t *ep;
+} stat_segment_cached_pointer_t;
+
+int stat_segment_connect (char *socket_name);
+void stat_segment_disconnect (void);
+
+u8 **stat_segment_ls (u8 ** pattern);
+stat_segment_data_t *stat_segment_dump (u8 ** counter_vec);
+
+stat_segment_cached_pointer_t *stat_segment_register (u8 ** counter_vec);
+stat_segment_data_t *stat_segment_collect (stat_segment_cached_pointer_t *); /* Collects registered counters */
+
+void stat_segment_data_free (stat_segment_data_t * res);
+
+f64 stat_segment_heartbeat (void);
+
+#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.am b/src/vpp.am
index e8c01557656..60be623627b 100644
--- a/src/vpp.am
+++ b/src/vpp.am
@@ -131,18 +131,29 @@ bin_summary_stats_client_LDADD = \
libvppinfra.la \
-lpthread -lm -lrt
-noinst_PROGRAMS += bin/stat_client
+bin_PROGRAMS += bin/vpp_get_stats
-bin_stat_client_SOURCES = \
- vpp/app/stat_client.c \
- vpp/app/stat_client.h
+bin_vpp_get_stats_SOURCES = \
+ vpp/app/vpp_get_stats.c
-bin_stat_client_LDADD = \
- libvlibmemoryclient.la \
- libsvm.la \
- libvppinfra.la \
+bin_vpp_get_stats_LDADD = \
+ libvppapiclient.la \
+ libvlibmemoryclient.la \
+ libsvm.la \
+ libvppinfra.la \
-lpthread -lm -lrt
+bin_PROGRAMS += bin/vpp_prometheus_export
+
+bin_vpp_prometheus_export_SOURCES = \
+ vpp/app/vpp_prometheus_export.c
+
+bin_vpp_prometheus_export_LDADD = \
+ libvppapiclient.la \
+ libvlibmemoryclient.la \
+ libsvm.la \
+ libvppinfra.la \
+ -lpthread -lm -lrt
bin_PROGRAMS += bin/vpp_get_metrics
diff --git a/src/vpp/CMakeLists.txt b/src/vpp/CMakeLists.txt
index 7e59090fc2e..78ca867abff 100644
--- a/src/vpp/CMakeLists.txt
+++ b/src/vpp/CMakeLists.txt
@@ -110,10 +110,15 @@ add_vpp_executable(summary_stats_client
NO_INSTALL
)
-add_vpp_executable(stat_client
- SOURCES app/stat_client.c
- LINK_LIBRARIES vppinfra svm vlibmemoryclient
+add_vpp_executable(vpp_get_stats
+ SOURCES app/vpp_get_stats.c
+ LINK_LIBRARIES vppapiclient vppinfra svm vlibmemoryclient
DEPENDS api_headers
NO_INSTALL
)
+add_vpp_executable(vpp_prometheus_export
+ SOURCES app/vpp_prometheus_export.c
+ LINK_LIBRARIES vppapiclient vppinfra svm vlibmemoryclient
+ DEPENDS api_headers
+)
diff --git a/src/vpp/app/stat_client.c b/src/vpp/app/stat_client.c
deleted file mode 100644
index 2bedf6f2d7d..00000000000
--- a/src/vpp/app/stat_client.c
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- *------------------------------------------------------------------
- * stat_client.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>
-
-stat_client_main_t stat_client_main;
-
-static int
-stat_segment_connect (stat_client_main_t * sm)
-{
- ssvm_private_t *ssvmp = &sm->stat_segment;
- ssvm_shared_header_t *shared_header;
- clib_socket_t s = { 0 };
- clib_error_t *err;
- int fd = -1, retval;
-
- s.config = (char *) sm->socket_name;
- s.flags = CLIB_SOCKET_F_IS_CLIENT | CLIB_SOCKET_F_SEQPACKET;
- err = clib_socket_init (&s);
- if (err)
- {
- clib_error_report (err);
- exit (1);
- }
- err = clib_socket_recvmsg (&s, 0, 0, &fd, 1);
- if (err)
- {
- clib_error_report (err);
- return -1;
- }
- clib_socket_close (&s);
-
- memset (ssvmp, 0, sizeof (*ssvmp));
- ssvmp->fd = fd;
-
- /* Note: this closes memfd.fd */
- retval = ssvm_slave_init_memfd (ssvmp);
- if (retval)
- {
- clib_warning ("WARNING: segment map returned %d", retval);
- return -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;
-
- return 0;
-}
-
-#define foreach_cached_pointer \
-_(/sys/vector_rate, SCALAR_POINTER, &stat_client_main.vector_rate_ptr) \
-_(/sys/input_rate, SCALAR_POINTER, &stat_client_main.input_rate_ptr) \
-_(/sys/last_update, SCALAR_POINTER, &stat_client_main.last_runtime_ptr) \
-_(/sys/last_stats_clear, SCALAR_POINTER, \
- &stat_client_main.last_runtime_stats_clear_ptr) \
-_(/if/rx, COUNTER_VECTOR, &stat_client_main.intfc_rx_counters) \
-_(/if/tx, COUNTER_VECTOR, &stat_client_main.intfc_tx_counters) \
-_(/err/0/counter_vector, VECTOR_POINTER, \
- &stat_client_main.thread_0_error_counts) \
-_(serialized_nodes, SERIALIZED_NODES, \
- &stat_client_main.serialized_nodes)
-
-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;
- vlib_node_t ***nodes_by_thread;
- vlib_node_t **nodes;
- vlib_node_t *n;
- f64 vector_rate, input_rate;
- u32 len;
- int i, j;
- 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);
-
- if (sm->serialized_nodes)
- {
- nodes_by_thread = vlib_node_unserialize (sm->serialized_nodes);
-
- /* Across all threads... */
- for (i = 0; i < vec_len (nodes_by_thread); i++)
- {
- u64 n_input, n_output, n_drop, n_punt;
- u64 n_internal_vectors, n_internal_calls;
- u64 n_clocks, l, v, c;
- f64 dt;
-
- nodes = nodes_by_thread[i];
-
- fformat (stdout, "Thread %d -------------------------\n", i);
-
- n_input = n_output = n_drop = n_punt = n_clocks = 0;
- n_internal_vectors = n_internal_calls = 0;
-
- /* Across all nodes */
- for (j = 0; j < vec_len (nodes); j++)
- {
- n = nodes[j];
-
- /* Exactly stolen from node_cli.c... */
- l = n->stats_total.clocks - n->stats_last_clear.clocks;
- n_clocks += l;
-
- v = n->stats_total.vectors - n->stats_last_clear.vectors;
- c = n->stats_total.calls - n->stats_last_clear.calls;
-
- switch (n->type)
- {
- default:
- vec_free (n->name);
- vec_free (n->next_nodes);
- vec_free (n);
- continue;
-
- case VLIB_NODE_TYPE_INTERNAL:
- n_output +=
- (n->flags & VLIB_NODE_FLAG_IS_OUTPUT) ? v : 0;
- n_drop += (n->flags & VLIB_NODE_FLAG_IS_DROP) ? v : 0;
- n_punt += (n->flags & VLIB_NODE_FLAG_IS_PUNT) ? v : 0;
- if (!(n->flags & VLIB_NODE_FLAG_IS_OUTPUT))
- {
- n_internal_vectors += v;
- n_internal_calls += c;
- }
- if (n->flags & VLIB_NODE_FLAG_IS_HANDOFF)
- n_input += v;
- break;
-
- case VLIB_NODE_TYPE_INPUT:
- n_input += v;
- break;
- }
-
- if (n->stats_total.calls)
- {
- fformat (stdout,
- "%s (%s): clocks %lld calls %lld vectors %lld ",
- n->name,
- n->state_string,
- n->stats_total.clocks,
- n->stats_total.calls, n->stats_total.vectors);
- if (n->stats_total.vectors)
- fformat (stdout, "clocks/pkt %.2f\n",
- (f64) n->stats_total.clocks /
- (f64) n->stats_total.vectors);
- else
- fformat (stdout, "\n");
- }
- vec_free (n->name);
- vec_free (n->next_nodes);
- vec_free (n);
- }
-
- fformat (stdout, "average vectors/node %.2f\n",
- (n_internal_calls > 0
- ? (f64) n_internal_vectors / (f64) n_internal_calls
- : 0));
-
-
- dt = *sm->last_runtime_ptr - *sm->last_runtime_stats_clear_ptr;
-
- fformat (stdout,
- " vectors rates in %.4e, out %.4e, drop %.4e, "
- "punt %.4e\n",
- (f64) n_input / dt,
- (f64) n_output / dt, (f64) n_drop / dt,
- (f64) n_punt / dt);
-
- vec_free (nodes);
- }
- vec_free (nodes_by_thread);
- }
- else
- {
- fformat (stdout, "serialized nodes NULL?\n");
- }
-
- }
-}
-
-int
-main (int argc, char **argv)
-{
- unformat_input_t _argv, *a = &_argv;
- stat_client_main_t *sm = &stat_client_main;
- u8 *stat_segment_name;
- int rv;
-
- clib_mem_init (0, 128 << 20);
-
- unformat_init_command_line (a, argv);
-
- stat_segment_name = (u8 *) STAT_SEGMENT_SOCKET_FILE;
-
- while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
- {
- if (unformat (a, "socket-name %s", &stat_segment_name))
- ;
- else
- {
- fformat (stderr, "%s: usage [socket-name <name>]\n", argv[0]);
- exit (1);
- }
- }
-
- sm->socket_name = stat_segment_name;
-
- rv = stat_segment_connect (sm);
- if (rv)
- {
- fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
- stat_segment_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
deleted file mode 100644
index 97bd3f9bb0b..00000000000
--- a/src/vpp/app/stat_client.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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 <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;
- f64 *last_runtime_ptr;
- f64 *last_runtime_stats_clear_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;
- u8 *serialized_nodes;
-
- u64 *thread_0_error_counts;
- u64 source_address_match_error_index;
-
- /* mapped stats segment object */
- ssvm_private_t stat_segment;
-
- /* 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/app/vpp_get_stats.c b/src/vpp/app/vpp_get_stats.c
new file mode 100644
index 00000000000..86e511e56f2
--- /dev/null
+++ b/src/vpp/app/vpp_get_stats.c
@@ -0,0 +1,242 @@
+/*
+ *------------------------------------------------------------------
+ * vpp_get_stats.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-api/client/stat_client.h>
+#include <vlib/vlib.h>
+#include <vpp/stats/stats.h>
+
+static int
+stat_poll_loop (stat_segment_cached_pointer_t * cp)
+{
+ struct timespec ts, tsrem;
+ stat_segment_data_t *res;
+ int i, j, k, lost_connection = 0;
+ f64 heartbeat, prev_heartbeat = 0;
+
+ printf ("\033[2J"); /* clear the screen */
+ while (1)
+ {
+ heartbeat = stat_segment_heartbeat ();
+ if (heartbeat > prev_heartbeat)
+ {
+ prev_heartbeat = heartbeat;
+ lost_connection = 0;
+ }
+ else
+ {
+ lost_connection++;
+ }
+ if (lost_connection > 10)
+ {
+ fformat (stderr, "Lost connection to VPP...\n");
+ return -1;
+ }
+
+ printf ("\033[H"); /* Cursor top left corner */
+ res = stat_segment_collect (cp);
+ for (i = 0; i < vec_len (res); i++)
+ {
+ switch (res[i].type)
+ {
+ case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
+ for (k = 0; k < vec_len (res[i].simple_counter_vec); k++)
+ for (j = 0; j < vec_len (res[i].simple_counter_vec[k]); j++)
+ fformat (stdout, "[%d]: %lld packets %s\n",
+ j, res[i].simple_counter_vec[k][j], res[i].name);
+ break;
+
+ case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
+ for (k = 0; k < vec_len (res[i].simple_counter_vec); k++)
+ for (j = 0; j < vec_len (res[i].combined_counter_vec[k]); j++)
+ fformat (stdout, "[%d]: %lld packets, %lld bytes %s\n",
+ j, res[i].combined_counter_vec[k][j].packets,
+ res[i].combined_counter_vec[k][j].bytes,
+ res[i].name);
+ break;
+
+ case STAT_DIR_TYPE_ERROR_INDEX:
+ fformat (stdout, "%lld %s\n", res[i].error_value, res[i].name);
+ break;
+
+ case STAT_DIR_TYPE_SCALAR_POINTER:
+ fformat (stdout, "%.2f %s\n", res[i].scalar_value, res[i].name);
+ break;
+
+ default:
+ printf ("Unknown value\n");
+ ;
+ }
+ }
+ stat_segment_data_free (res);
+ /* Scrape stats every 5 seconds */
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ while (nanosleep (&ts, &tsrem) < 0)
+ ts = tsrem;
+
+ }
+}
+
+enum stat_client_cmd_e
+{
+ STAT_CLIENT_CMD_UNKNOWN,
+ STAT_CLIENT_CMD_LS,
+ STAT_CLIENT_CMD_POLL,
+ STAT_CLIENT_CMD_DUMP,
+};
+
+int
+main (int argc, char **argv)
+{
+ unformat_input_t _argv, *a = &_argv;
+ u8 *stat_segment_name, *pattern = 0, **patterns = 0;
+ int rv;
+ enum stat_client_cmd_e cmd = STAT_CLIENT_CMD_UNKNOWN;
+
+ clib_mem_init (0, 128 << 20);
+
+ unformat_init_command_line (a, argv);
+
+ stat_segment_name = (u8 *) STAT_SEGMENT_SOCKET_FILE;
+
+ while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (a, "socket-name %s", &stat_segment_name))
+ ;
+ else if (unformat (a, "ls"))
+ {
+ cmd = STAT_CLIENT_CMD_LS;
+ }
+ else if (unformat (a, "dump"))
+ {
+ cmd = STAT_CLIENT_CMD_DUMP;
+ }
+ else if (unformat (a, "poll"))
+ {
+ cmd = STAT_CLIENT_CMD_POLL;
+ }
+ else if (unformat (a, "%s", &pattern))
+ {
+ vec_add1 (patterns, pattern);
+ }
+ else
+ {
+ fformat (stderr,
+ "%s: usage [socket-name <name>] [ls|dump|poll] <patterns> ...\n",
+ argv[0]);
+ exit (1);
+ }
+ }
+reconnect:
+ rv = stat_segment_connect ((char *) stat_segment_name);
+ if (rv)
+ {
+ fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
+ stat_segment_name);
+ exit (1);
+ }
+
+ u8 **dir;
+ int i, j, k;
+ stat_segment_data_t *res;
+ stat_segment_cached_pointer_t *cp;
+
+ dir = stat_segment_ls (patterns);
+
+ switch (cmd)
+ {
+ case STAT_CLIENT_CMD_LS:
+ /* List all counters */
+ for (i = 0; i < vec_len (dir); i++)
+ {
+ printf ("%s\n", (char *) dir[i]);
+ }
+ break;
+
+ case STAT_CLIENT_CMD_DUMP:
+ res = stat_segment_dump (dir);
+ for (i = 0; i < vec_len (res); i++)
+ {
+ switch (res[i].type)
+ {
+ case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
+ for (k = 0; k < vec_len (res[i].simple_counter_vec) - 1; k++)
+ for (j = 0; j < vec_len (res[i].simple_counter_vec[k]); j++)
+ fformat (stdout, "[%d @ %d]: %lld packets %s\n",
+ j, k, res[i].simple_counter_vec[k][j],
+ res[i].name);
+ break;
+
+ case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
+ for (k = 0; k < vec_len (res[i].simple_counter_vec); k++)
+ for (j = 0; j < vec_len (res[i].combined_counter_vec[k]); j++)
+ fformat (stdout, "[%d @ %d]: %lld packets, %lld bytes %s\n",
+ j, k, res[i].combined_counter_vec[k][j].packets,
+ res[i].combined_counter_vec[k][j].bytes,
+ res[i].name);
+ break;
+
+ case STAT_DIR_TYPE_ERROR_INDEX:
+ fformat (stdout, "%lld %s\n", res[i].error_value, dir[i]);
+ break;
+
+ case STAT_DIR_TYPE_SCALAR_POINTER:
+ fformat (stdout, "%.2f %s\n", dir[i], res[i].scalar_value,
+ res[i].name);
+ break;
+
+ default:
+ ;
+ }
+ }
+ stat_segment_data_free (res);
+ break;
+
+ case STAT_CLIENT_CMD_POLL:
+ cp = stat_segment_register (dir);
+ if (!cp)
+ {
+ fformat (stderr,
+ "Couldn't register required counters with stat segment\n");
+ exit (1);
+ }
+ stat_poll_loop (cp);
+ /* We can only exist the pool loop if we lost connection to VPP */
+ stat_segment_disconnect ();
+ goto reconnect;
+ break;
+
+ default:
+ fformat (stderr,
+ "%s: usage [socket-name <name>] [ls|dump|poll] <patterns> ...\n",
+ argv[0]);
+ }
+
+ stat_segment_disconnect ();
+
+ exit (0);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vpp/app/vpp_prometheus_export.c b/src/vpp/app/vpp_prometheus_export.c
new file mode 100644
index 00000000000..57c65178c73
--- /dev/null
+++ b/src/vpp/app/vpp_prometheus_export.c
@@ -0,0 +1,320 @@
+/*
+ *------------------------------------------------------------------
+ * vpp_get_stats.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 <arpa/inet.h>
+#include <sys/epoll.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <vpp-api/client/stat_client.h>
+#include <vlib/vlib.h>
+#include <vpp/stats/stats.h>
+#include <ctype.h>
+
+/* https://github.com/prometheus/prometheus/wiki/Default-port-allocations */
+#define SERVER_PORT 9477
+
+static char *
+prom_string (char *s)
+{
+ char *p = s;
+ while (*p)
+ {
+ if (!isalnum (*p))
+ *p = '_';
+ p++;
+ }
+ return s;
+}
+
+static void
+dump_metrics (FILE * stream, stat_segment_cached_pointer_t * cp)
+{
+ stat_segment_data_t *res;
+ int i, j, k;
+
+ res = stat_segment_collect (cp);
+ for (i = 0; i < vec_len (res); i++)
+ {
+ switch (res[i].type)
+ {
+ case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
+ fformat (stream, "# TYPE %s counter\n", prom_string (res[i].name));
+ for (k = 0; k < vec_len (res[i].simple_counter_vec); k++)
+ for (j = 0; j < vec_len (res[i].simple_counter_vec[k]); j++)
+ fformat (stream, "%s{thread=\"%d\",interface=\"%d\"} %lld\n",
+ prom_string (res[i].name), k, j,
+ res[i].simple_counter_vec[k][j]);
+ break;
+
+ case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
+ fformat (stream, "# TYPE %s_packets counter\n",
+ prom_string (res[i].name));
+ fformat (stream, "# TYPE %s_bytes counter\n",
+ prom_string (res[i].name));
+ for (k = 0; k < vec_len (res[i].simple_counter_vec); k++)
+ for (j = 0; j < vec_len (res[i].combined_counter_vec[k]); j++)
+ {
+ fformat (stream,
+ "%s_packets{thread=\"%d\",interface=\"%d\"} %lld\n",
+ prom_string (res[i].name), k, j,
+ res[i].combined_counter_vec[k][j].packets);
+ fformat (stream,
+ "%s_bytes{thread=\"%d\",interface=\"%d\"} %lld\n",
+ prom_string (res[i].name), k, j,
+ res[i].combined_counter_vec[k][j].bytes);
+ }
+ break;
+ case STAT_DIR_TYPE_ERROR_INDEX:
+ fformat (stream, "# TYPE %s counter\n", prom_string (res[i].name));
+ fformat (stream, "%s{thread=\"0\"} %lld\n",
+ prom_string (res[i].name), res[i].error_value);
+ break;
+
+ case STAT_DIR_TYPE_SCALAR_POINTER:
+ fformat (stream, "# TYPE %s counter\n", prom_string (res[i].name));
+ fformat (stream, "%s %.2f\n", prom_string (res[i].name),
+ res[i].scalar_value);
+ break;
+
+ default:
+ fformat (stderr, "Unknown value %d\n", res[i].type);
+ ;
+ }
+ }
+ stat_segment_data_free (res);
+
+}
+
+
+#define ROOTPAGE "<html><head><title>Metrics exporter</title></head><body><ul><li><a href=\"/metrics\">metrics</a></li></ul></body></html>"
+#define NOT_FOUND_ERROR "<html><head><title>Document not found</title></head><body><h1>404 - Document not found</h1></body></html>"
+
+static void
+http_handler (FILE * stream, stat_segment_cached_pointer_t * cp)
+{
+ char status[80] = { 0 };
+ if (fgets (status, sizeof (status) - 1, stream) == 0)
+ {
+ fprintf (stderr, "fgets error: %s %s\n", status, strerror (errno));
+ return;
+ }
+ char *saveptr;
+ char *method = strtok_r (status, " \t\r\n", &saveptr);
+ if (method == 0 || strncmp (method, "GET", 4) != 0)
+ {
+ fputs ("HTTP/1.0 405 Method Not Allowed\r\n", stream);
+ return;
+ }
+ char *request_uri = strtok_r (NULL, " \t", &saveptr);
+ char *protocol = strtok_r (NULL, " \t\r\n", &saveptr);
+ if (protocol == 0 || strncmp (protocol, "HTTP/1.", 7) != 0)
+ {
+ fputs ("HTTP/1.0 400 Bad Request\r\n", stream);
+ return;
+ }
+ /* Read the other headers */
+ for (;;)
+ {
+ char header[1024];
+ if (fgets (header, sizeof (header) - 1, stream) == 0)
+ {
+ fprintf (stderr, "fgets error: %s\n", strerror (errno));
+ return;
+ }
+ if (header[0] == '\n' || header[1] == '\n')
+ {
+ break;
+ }
+ }
+ if (strcmp (request_uri, "/") == 0)
+ {
+ fprintf (stream, "HTTP/1.0 200 OK\r\nContent-Length: %lu\r\n\r\n",
+ (unsigned long) strlen (ROOTPAGE));
+ fputs (ROOTPAGE, stream);
+ return;
+ }
+ if (strcmp (request_uri, "/metrics") != 0)
+ {
+ fprintf (stream,
+ "HTTP/1.0 404 Not Found\r\nContent-Length: %lu\r\n\r\n",
+ (unsigned long) strlen (NOT_FOUND_ERROR));
+ fputs (NOT_FOUND_ERROR, stream);
+ return;
+ }
+ fputs ("HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n", stream);
+ dump_metrics (stream, cp);
+}
+
+static int
+start_listen (u16 port)
+{
+ struct sockaddr_in6 serveraddr;
+ int addrlen = sizeof (serveraddr);
+ int enable = 1;
+
+ int listenfd = socket (AF_INET6, SOCK_STREAM, 0);
+ if (listenfd == -1)
+ {
+ perror ("Failed opening socket");
+ }
+
+ int rv =
+ setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof (int));
+ if (rv < 0)
+ {
+ perror ("Failed setsockopt");
+ }
+
+ memset (&serveraddr, 0, sizeof (serveraddr));
+ serveraddr.sin6_family = AF_INET6;
+ serveraddr.sin6_port = htons (port);
+ serveraddr.sin6_addr = in6addr_any;
+
+ if (bind (listenfd, (struct sockaddr *) &serveraddr, addrlen) < 0)
+ {
+ fprintf (stderr, "bind() error %s\n", strerror (errno));
+ return -1;
+ }
+ if (listen (listenfd, 1000000) != 0)
+ {
+ fprintf (stderr, "listen() error for %s\n", strerror (errno));
+ return -1;
+ }
+ return listenfd;
+}
+
+/* Socket epoll, linux-specific */
+union my_sockaddr
+{
+ struct sockaddr_storage storage;
+ struct sockaddr addr;
+ struct sockaddr_in sin_addr;
+ struct sockaddr_in6 sin6_addr;
+};
+
+
+
+int
+main (int argc, char **argv)
+{
+ unformat_input_t _argv, *a = &_argv;
+ u8 *stat_segment_name, *pattern = 0, **patterns = 0;
+ int rv;
+
+ clib_mem_init (0, 128 << 20);
+
+ unformat_init_command_line (a, argv);
+
+ stat_segment_name = (u8 *) STAT_SEGMENT_SOCKET_FILE;
+
+ while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (a, "socket-name %s", &stat_segment_name))
+ ;
+ else if (unformat (a, "%s", &pattern))
+ {
+ vec_add1 (patterns, pattern);
+ }
+ else
+ {
+ fformat (stderr,
+ "%s: usage [socket-name <name>] <patterns> ...\n",
+ argv[0]);
+ exit (1);
+ }
+ }
+
+ rv = stat_segment_connect ((char *) stat_segment_name);
+ if (rv)
+ {
+ fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
+ stat_segment_name);
+ exit (1);
+ }
+
+ u8 **dir;
+ stat_segment_cached_pointer_t *cp;
+
+ dir = stat_segment_ls (patterns);
+ cp = stat_segment_register (dir);
+ if (!cp)
+ {
+ fformat (stderr,
+ "Couldn't register required counters with stat segment\n");
+ exit (1);
+ }
+
+ int fd = start_listen (SERVER_PORT);
+ if (fd < 0)
+ {
+ exit (1);
+ }
+ for (;;)
+ {
+ int conn_sock = accept (fd, NULL, NULL);
+ if (conn_sock < 0)
+ {
+ fprintf (stderr, "Accept failed: %s", strerror (errno));
+ continue;
+ }
+ else
+ {
+ struct sockaddr_in6 clientaddr;
+ char address[INET6_ADDRSTRLEN];
+ socklen_t addrlen;
+ getpeername (conn_sock, (struct sockaddr *) &clientaddr, &addrlen);
+ if (inet_ntop
+ (AF_INET6, &clientaddr.sin6_addr, address, sizeof (address)))
+ {
+ printf ("Client address is [%s]:%d\n", address,
+ ntohs (clientaddr.sin6_port));
+ }
+ }
+
+ FILE *stream = fdopen (conn_sock, "r+");
+ if (stream == NULL)
+ {
+ fprintf (stderr, "fdopen error: %s\n", strerror (errno));
+ close (conn_sock);
+ continue;
+ }
+ /* Single reader at the moment */
+ http_handler (stream, cp);
+ fclose (stream);
+ }
+
+ stat_segment_disconnect ();
+
+ exit (0);
+}
+
+/*
+ * 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
index 29b3bf3812d..8459138be34 100644
--- a/src/vpp/stats/stat_segment.c
+++ b/src/vpp/stats/stat_segment.c
@@ -43,7 +43,7 @@ vlib_stats_push_heap (void)
}
void
-vlib_stats_pop_heap (void *cm_arg, void *oldheap)
+vlib_stats_pop_heap (void *cm_arg, void *oldheap, stat_directory_type_t type)
{
vlib_simple_counter_main_t *cm = (vlib_simple_counter_main_t *) cm_arg;
stats_main_t *sm = &stats_main;
@@ -80,7 +80,7 @@ vlib_stats_pop_heap (void *cm_arg, void *oldheap)
}
name_copy = format (0, "%s%c", stat_segment_name, 0);
ep = clib_mem_alloc (sizeof (*ep));
- ep->type = STAT_DIR_TYPE_COUNTER_VECTOR;
+ ep->type = type;
ep->value = cm->counters;
hash_set_mem (sm->counter_vector_by_name, name_copy, ep);
@@ -236,6 +236,7 @@ vlib_map_stat_segment_init (void)
sm->input_rate_ptr = (scalar_data + 1);
sm->last_runtime_ptr = (scalar_data + 2);
sm->last_runtime_stats_clear_ptr = (scalar_data + 3);
+ sm->heartbeat_ptr = (scalar_data + 4);
name = format (0, "/sys/vector_rate%c", 0);
ep = clib_mem_alloc (sizeof (*ep));
@@ -265,6 +266,13 @@ vlib_map_stat_segment_init (void)
hash_set_mem (sm->counter_vector_by_name, name, ep);
+ name = format (0, "/sys/heartbeat%c", 0);
+ ep = clib_mem_alloc (sizeof (*ep));
+ ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
+ ep->value = sm->heartbeat_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;
@@ -309,7 +317,8 @@ format_stat_dir_entry (u8 * s, va_list * args)
type_name = "VectorPtr";
break;
- case STAT_DIR_TYPE_COUNTER_VECTOR:
+ case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
+ case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
type_name = "CMainPtr";
break;
@@ -512,6 +521,9 @@ do_stat_segment_updates (stats_main_t * sm)
if (sm->serialize_nodes)
update_serialized_nodes (sm);
+
+ /* Heartbeat, so clients detect we're still here */
+ (*sm->heartbeat_ptr)++;
}
static clib_error_t *
diff --git a/src/vpp/stats/stats.h b/src/vpp/stats/stats.h
index aea2da7a2c2..911706cbb09 100644
--- a/src/vpp/stats/stats.h
+++ b/src/vpp/stats/stats.h
@@ -175,6 +175,7 @@ typedef struct
f64 *last_runtime_ptr;
f64 *last_runtime_stats_clear_ptr;
f64 *vector_rate_ptr;
+ f64 *heartbeat_ptr;
u64 last_input_packets;
/* Pointers to vector stats maintained by the stat thread */
@@ -209,7 +210,8 @@ 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_COUNTER_VECTOR_SIMPLE,
+ STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED,
STAT_DIR_TYPE_ERROR_INDEX,
STAT_DIR_TYPE_SERIALIZED_NODES,
} stat_directory_type_t;