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/app | |
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/app')
-rw-r--r-- | src/vpp/app/stat_client.c | 343 | ||||
-rw-r--r-- | src/vpp/app/stat_client.h | 70 |
2 files changed, 413 insertions, 0 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: + */ |