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