From: Ole Troan Date: Thu, 23 Aug 2018 11:00:53 +0000 (+0200) Subject: STATS: stat_client updates. X-Git-Tag: v18.10-rc1~325 X-Git-Url: https://gerrit.fd.io/r/gitweb?p=vpp.git;a=commitdiff_plain;h=2fee16787ed0d622631223567635a77e14c8c076 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 --- 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 +#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: + */ 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 - -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 ]\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 -#include -#include -#include - -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 +#include +#include + +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 ] [ls|dump|poll] ...\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 ] [ls|dump|poll] ...\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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 "Metrics exporter" +#define NOT_FOUND_ERROR "Document not found

404 - Document not found

" + +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 ] ...\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;