diff options
author | Dave Barach <dave@barachs.net> | 2018-06-01 18:52:25 -0400 |
---|---|---|
committer | Dave Barach <dave@barachs.net> | 2018-06-08 11:42:01 -0400 |
commit | 048a4e5a000017d0d632ebf02dcc23d9bf9ccf72 (patch) | |
tree | 5f00dd3888faae59e316059506d064ba0a17fb5a /src/vpp | |
parent | 59ae61ee7587502c0446655ecbe3daa296498f56 (diff) |
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 <dave@barachs.net>
Diffstat (limited to 'src/vpp')
-rw-r--r-- | src/vpp/app/stat_client.c | 343 | ||||
-rw-r--r-- | src/vpp/app/stat_client.h | 70 | ||||
-rw-r--r-- | src/vpp/stats/stat_segment.c | 425 | ||||
-rw-r--r-- | src/vpp/stats/stats.api | 9 | ||||
-rw-r--r-- | src/vpp/stats/stats.c | 67 | ||||
-rw-r--r-- | src/vpp/stats/stats.h | 29 |
6 files changed, 932 insertions, 11 deletions
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 <vpp/app/stat_client.h> + +#include <vpp/api/vpe_msg_enum.h> + +#define vl_typedefs /* define message structures */ +#include <vpp/api/vpe_all_api_h.h> +#undef vl_typedefs + +#define vl_endianfun /* define endian fcns */ +#include <vpp/api/vpe_all_api_h.h> +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) fformat (handle, __VA_ARGS__) +#define vl_printfun +#include <vpp/api/vpe_all_api_h.h> +#undef vl_printfun + +stat_client_main_t stat_client_main; + +static void vl_api_map_stats_segment_reply_t_handler + (vl_api_map_stats_segment_reply_t * mp) +{ + stat_client_main_t *sm = &stat_client_main; + ssvm_private_t *ssvmp = &sm->stat_segment; + ssvm_shared_header_t *shared_header; + socket_client_main_t *scm = sm->socket_client_main; + int rv = ntohl (mp->retval); + int my_fd, retval; + clib_error_t *error; + + if (rv != 0) + { + fformat (stderr, "ERROR mapping stats segment: %d", rv); + exit (1); + } + + /* + * Check the socket for the magic fd + */ + error = vl_sock_api_recv_fd_msg (scm->socket_fd, &my_fd, 5); + if (error) + { + clib_error_report (error); + exit (1); + } + + memset (ssvmp, 0, sizeof (*ssvmp)); + ssvmp->fd = my_fd; + + /* Note: this closes memfd.fd */ + retval = ssvm_slave_init_memfd (ssvmp); + if (retval) + { + clib_warning ("WARNING: segment map returned %d", retval); + exit (1); + } + + fformat (stdout, "Stat segment mapped OK...\n"); + + ASSERT (ssvmp && ssvmp->sh); + + /* Pick up the segment lock from the shared memory header */ + shared_header = ssvmp->sh; + sm->stat_segment_lockp = (clib_spinlock_t *) (shared_header->opaque[0]); + sm->segment_ready = 1; + + /* No need to keep the socket API connection open */ + close (sm->socket_client_main->socket_fd); +} + +#define foreach_api_reply_msg \ +_(MAP_STATS_SEGMENT_REPLY, map_stats_segment_reply) + +static void +vpp_api_hookup (void) +{ +#define _(N,n) \ + vl_msg_api_set_handlers(VL_API_##N, #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_api_reply_msg; +#undef _ +} + +static int +connect_to_vpp (stat_client_main_t * sm) +{ + int rv; + vl_api_map_stats_segment_t *mp; + api_main_t *am = &api_main; + + sm->socket_client_main = &socket_client_main; + + rv = vl_socket_client_connect ((char *) sm->socket_name, + "stat_client", + 0 /* default socket rx, tx buffer */ ); + if (rv) + { + fformat (stderr, "Error connecting to vpp...\n"); + exit (1); + } + + /* Hook up reply handler */ + vpp_api_hookup (); + + /* Map the stats segment */ + mp = vl_socket_client_msg_alloc (sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_MAP_STATS_SEGMENT); + mp->client_index = am->my_client_index; + mp->context = 0xdeaddabe; + + /* Send the message */ + vl_socket_client_write (); + + /* Wait for a reply, process it.. */ + vl_socket_client_read (5 /* timeout in seconds */ ); + + return 0; +} + +#define foreach_cached_pointer \ +_(vector_rate, SCALAR_POINTER, &stat_client_main.vector_rate_ptr) \ +_(input_rate, SCALAR_POINTER, &stat_client_main.input_rate_ptr) \ +_(rx, COUNTER_VECTOR, &stat_client_main.intfc_rx_counters) \ +_(tx, COUNTER_VECTOR, &stat_client_main.intfc_tx_counters) \ +_(/err/0/counter_vector, VECTOR_POINTER, \ + &stat_client_main.thread_0_error_counts) \ +_(/err/IP4 source address matches local interface, ERROR_INDEX, \ + &stat_client_main.source_address_match_error_index) + +typedef struct +{ + char *name; + stat_directory_type_t type; + void *valuep; +} cached_pointer_t; + +cached_pointer_t cached_pointers[] = { +#define _(n,t,p) {#n, STAT_DIR_TYPE_##t, (void *)p}, + foreach_cached_pointer +#undef _ +}; + +static void +maybe_update_cached_pointers (stat_client_main_t * sm, + ssvm_shared_header_t * shared_header) +{ + uword *p, *counter_vector_by_name; + int i; + stat_segment_directory_entry_t *ep; + cached_pointer_t *cp; + u64 *valuep; + + /* Cached pointers OK? */ + if (sm->current_epoch == + (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH]) + return; + + fformat (stdout, "Updating cached pointers...\n"); + + /* Nope, fix them... */ + counter_vector_by_name = (uword *) + shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR]; + + for (i = 0; i < ARRAY_LEN (cached_pointers); i++) + { + cp = &cached_pointers[i]; + + p = hash_get_mem (counter_vector_by_name, cp->name); + + if (p == 0) + { + clib_warning ("WARN: %s not in directory!", cp->name); + continue; + } + ep = (stat_segment_directory_entry_t *) (p[0]); + ASSERT (ep->type == cp->type); + valuep = (u64 *) cp->valuep; + *valuep = (u64) ep->value; + } + + /* And remember that we did... */ + sm->current_epoch = (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH]; +} + +static void +stat_poll_loop (stat_client_main_t * sm) +{ + struct timespec ts, tsrem; + ssvm_private_t *ssvmp = &sm->stat_segment; + ssvm_shared_header_t *shared_header; + vlib_counter_t *thread0_rx_counters = 0, *thread0_tx_counters = 0; + f64 vector_rate, input_rate; + u32 len; + int i; + u32 source_address_match_errors; + + /* Wait until the stats segment is mapped */ + while (!sm->segment_ready) + { + ts.tv_sec = 0; + ts.tv_nsec = 100000000; + while (nanosleep (&ts, &tsrem) < 0) + ts = tsrem; + } + + shared_header = ssvmp->sh; + ASSERT (ssvmp->sh); + + while (1) + { + /* Scrape stats every 5 seconds */ + ts.tv_sec = 5; + ts.tv_nsec = 0; + while (nanosleep (&ts, &tsrem) < 0) + ts = tsrem; + + vec_reset_length (thread0_rx_counters); + vec_reset_length (thread0_tx_counters); + + /* Grab the stats segment lock */ + clib_spinlock_lock (sm->stat_segment_lockp); + + /* see if we need to update cached pointers */ + maybe_update_cached_pointers (sm, shared_header); + + ASSERT (sm->vector_rate_ptr); + ASSERT (sm->intfc_rx_counters); + ASSERT (sm->intfc_tx_counters); + + /* Read data from the segment */ + vector_rate = *sm->vector_rate_ptr; + input_rate = *sm->input_rate_ptr; + + len = vec_len (sm->intfc_rx_counters[0]); + + ASSERT (len); + + vec_validate (thread0_rx_counters, len - 1); + vec_validate (thread0_tx_counters, len - 1); + + clib_memcpy (thread0_rx_counters, sm->intfc_rx_counters[0], + len * sizeof (vlib_counter_t)); + clib_memcpy (thread0_tx_counters, sm->intfc_tx_counters[0], + len * sizeof (vlib_counter_t)); + + source_address_match_errors = + sm->thread_0_error_counts[sm->source_address_match_error_index]; + + /* Drop the lock */ + clib_spinlock_unlock (sm->stat_segment_lockp); + + /* And print results... */ + + fformat (stdout, "vector_rate %.2f input_rate %.2f\n", + vector_rate, input_rate); + + for (i = 0; i < vec_len (thread0_rx_counters); i++) + { + fformat (stdout, "[%d]: %lld rx packets, %lld rx bytes\n", + i, thread0_rx_counters[i].packets, + thread0_rx_counters[i].bytes); + fformat (stdout, "[%d]: %lld tx packets, %lld tx bytes\n", + i, thread0_tx_counters[i].packets, + thread0_tx_counters[i].bytes); + } + + fformat (stdout, "%lld source address match errors\n", + source_address_match_errors); + } +} + + +int +main (int argc, char **argv) +{ + unformat_input_t _argv, *a = &_argv; + stat_client_main_t *sm = &stat_client_main; + u8 *socket_name; + int rv; + + clib_mem_init (0, 128 << 20); + + unformat_init_command_line (a, argv); + + socket_name = (u8 *) API_SOCKET_FILE; + + while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT) + { + if (unformat (a, "socket-name %s", &socket_name)) + ; + else + { + fformat (stderr, "%s: usage [socket-name <name>]\n", argv[0]); + exit (1); + } + } + + sm->socket_name = socket_name; + + rv = connect_to_vpp (sm); + + if (rv) + { + fformat (stderr, "Couldn't connect to vpp, does %s exist?\n", + socket_name); + exit (1); + } + + stat_poll_loop (sm); + exit (0); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ 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 <vlib/vlib.h> +#include <vppinfra/socket.h> +#include <svm/ssvm.h> +#include <vlibapi/api.h> +#include <vlibmemory/api.h> +#include <vlibmemory/socket_api.h> +#include <vlibmemory/socket_client.h> +#include <vpp/stats/stats.h> + +typedef struct +{ + u64 current_epoch; + + /* Cached pointers to scalar quantities, these wont change */ + f64 *vector_rate_ptr; + f64 *input_rate_ptr; + + 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 <vpp/stats/stats.h> + +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 <vpp/stats/stats.api.h> @@ -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 <vlibmemory/api.h> #include <vlibapi/api_helper_macros.h> #include <svm/queue.h> +#include <svm/ssvm.h> 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__ */ /* |