From 2fee16787ed0d622631223567635a77e14c8c076 Mon Sep 17 00:00:00 2001 From: Ole Troan Date: Thu, 23 Aug 2018 13:00:53 +0200 Subject: 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 ] Prometheus integration through: vpp_prometheus_export Change-Id: I6f370cf599e9fcf066f22965a62d3a8acd529994 Signed-off-by: Ole Troan --- src/vpp-api/CMakeLists.txt | 2 + src/vpp-api/client/libvppapiclient.map | 12 +- src/vpp-api/client/stat_client.c | 372 +++++++++++++++++++++++++++++++++ src/vpp-api/client/stat_client.h | 64 ++++++ 4 files changed, 447 insertions(+), 3 deletions(-) create mode 100644 src/vpp-api/client/stat_client.c create mode 100644 src/vpp-api/client/stat_client.h (limited to 'src/vpp-api') 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 +#include +#include +#include +#include +#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 (®ex[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(®ex[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 (®ex[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 +#include + +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: + */ -- cgit 1.2.3-korg