diff options
Diffstat (limited to 'src/vpp-api/client/stat_client.c')
-rw-r--r-- | src/vpp-api/client/stat_client.c | 438 |
1 files changed, 228 insertions, 210 deletions
diff --git a/src/vpp-api/client/stat_client.c b/src/vpp-api/client/stat_client.c index ad7078ea745..b5aab74de8a 100644 --- a/src/vpp-api/client/stat_client.c +++ b/src/vpp-api/client/stat_client.c @@ -17,72 +17,122 @@ *------------------------------------------------------------------ */ -#include <vlib/vlib.h> -#include <vppinfra/socket.h> -#include <svm/ssvm.h> -#include <vpp/stats/stats.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <stdbool.h> +#include <sys/stat.h> #include <regex.h> +#include <assert.h> +#include <vppinfra/vec.h> +#include <vppinfra/lock.h> #include "stat_client.h" +#include <stdatomic.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; + uint64_t current_epoch; + stat_segment_shared_header_t *shared_header; + stat_segment_directory_entry_t *directory_vector; + ssize_t memory_size; } stat_client_main_t; stat_client_main_t stat_client_main; +static int +recv_fd (int sock) +{ + struct msghdr msg = { 0 }; + struct cmsghdr *cmsg; + int fd = -1; + char iobuf[1]; + struct iovec io = {.iov_base = iobuf,.iov_len = sizeof (iobuf) }; + union + { + char buf[CMSG_SPACE (sizeof (fd))]; + struct cmsghdr align; + } u; + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = u.buf; + msg.msg_controllen = sizeof (u.buf); + + ssize_t size; + if ((size = recvmsg (sock, &msg, 0)) < 0) + { + perror ("recvmsg failed"); + return -1; + } + cmsg = CMSG_FIRSTHDR (&msg); + if (cmsg && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) + { + memmove (&fd, CMSG_DATA (cmsg), sizeof (fd)); + } + return fd; +} + +static stat_segment_directory_entry_t * +get_stat_vector (void) +{ + stat_client_main_t *sm = &stat_client_main; + ASSERT (sm->shared_header); + return stat_segment_pointer (sm->shared_header, + sm->shared_header->directory_offset); +} + 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; + int mfd = -1; + int sock; 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) + if ((sock = socket (AF_UNIX, SOCK_SEQPACKET, 0)) < 0) { - clib_error_report (err); + perror ("Couldn't open socket"); return -1; } - err = clib_socket_recvmsg (&s, 0, 0, &fd, 1); - if (err) + + struct sockaddr_un un = { 0 }; + un.sun_family = AF_UNIX; + strncpy ((char *) un.sun_path, socket_name, sizeof (un.sun_path) - 1); + if (connect (sock, (struct sockaddr *) &un, sizeof (struct sockaddr_un)) < + 0) { - clib_error_report (err); + perror ("connect"); 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) + if ((mfd = recv_fd (sock)) < 0) { - fprintf (stderr, "WARNING: segment map returned %d\n", retval); + fprintf (stderr, "Receiving file descriptor failed\n"); return -1; } + close (sock); - ASSERT (ssvmp && ssvmp->sh); + /* mmap shared memory segment. */ + void *memaddr; + struct stat st = { 0 }; - /* 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; + if (fstat (mfd, &st) == -1) + { + perror ("mmap"); + return -1; + } + if ((memaddr = + mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, mfd, 0)) == MAP_FAILED) + { + perror ("mmap"); + return -1; + } - sm->counter_vector_by_name = - (uword *) sm->shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR]; + sm->memory_size = st.st_size; + sm->shared_header = memaddr; + sm->directory_vector = + stat_segment_pointer (memaddr, sm->shared_header->directory_offset); return 0; } @@ -91,146 +141,72 @@ 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; + munmap (sm->shared_header, sm->memory_size); - 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!\n", 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; + return; } -f64 +double 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!\n", 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_directory_entry_t *vec = get_stat_vector (); + double *hb = stat_segment_pointer (sm->shared_header, vec[4].offset); + return *hb; } stat_segment_data_t -copy_data (stat_segment_directory_entry_t * ep, u64 * error_base, char *name) +copy_data (stat_segment_directory_entry_t * ep) { + stat_client_main_t *sm = &stat_client_main; stat_segment_data_t result = { 0 }; - u32 error_index; int i; vlib_counter_t **combined_c; /* Combined counter */ counter_t **simple_c; /* Simple counter */ + counter_t *error_base; + uint64_t *offset_vector; + + assert (sm->shared_header); + result.type = ep->type; - result.name = name; + result.name = strdup (ep->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; + case STAT_DIR_TYPE_SCALAR_INDEX: + result.scalar_value = ep->value; break; case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE: - simple_c = ep->value; + simple_c = stat_segment_pointer (sm->shared_header, ep->offset); result.simple_counter_vec = vec_dup (simple_c); + offset_vector = + stat_segment_pointer (sm->shared_header, ep->offset_vector); for (i = 0; i < vec_len (simple_c); i++) - result.simple_counter_vec[i] = vec_dup (simple_c[i]); + { + counter_t *cb = + stat_segment_pointer (sm->shared_header, offset_vector[i]); + result.simple_counter_vec[i] = vec_dup (cb); + } break; case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED: - combined_c = ep->value; + combined_c = stat_segment_pointer (sm->shared_header, ep->offset); result.combined_counter_vec = vec_dup (combined_c); + offset_vector = + stat_segment_pointer (sm->shared_header, ep->offset_vector); for (i = 0; i < vec_len (combined_c); i++) - result.combined_counter_vec[i] = vec_dup (combined_c[i]); + { + vlib_counter_t *cb = + stat_segment_pointer (sm->shared_header, offset_vector[i]); + result.combined_counter_vec[i] = vec_dup (cb); + } break; case STAT_DIR_TYPE_ERROR_INDEX: - error_index = (uintptr_t) ep->value; - result.error_value = error_base[error_index]; + error_base = + stat_segment_pointer (sm->shared_header, + sm->shared_header->error_offset); + result.error_value = error_base[ep->index]; break; default: @@ -239,32 +215,6 @@ copy_data (stat_segment_directory_entry_t * ep, u64 * error_base, char *name) 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) { @@ -286,19 +236,48 @@ stat_segment_data_free (stat_segment_data_t * res) default: ; } + free (res[i].name); } vec_free (res); } -u8 ** -stat_segment_ls (u8 ** patterns) + +typedef struct +{ + uint64_t epoch; +} stat_segment_access_t; + +static void +stat_segment_access_start (stat_segment_access_t * sa) { stat_client_main_t *sm = &stat_client_main; - hash_pair_t *p; - u8 **dir = 0; + stat_segment_shared_header_t *shared_header = sm->shared_header; + sa->epoch = shared_header->epoch; + while (shared_header->in_progress != 0) + ; +} + +static bool +stat_segment_access_end (stat_segment_access_t * sa) +{ + stat_client_main_t *sm = &stat_client_main; + stat_segment_shared_header_t *shared_header = sm->shared_header; + + if (shared_header->epoch != sa->epoch || shared_header->in_progress) + return false; + return true; +} + +uint32_t * +stat_segment_ls (uint8_t ** patterns) +{ + stat_client_main_t *sm = &stat_client_main; + stat_segment_access_t sa; + + uint32_t *dir = 0; regex_t regex[vec_len (patterns)]; - int i; + int i, j; for (i = 0; i < vec_len (patterns); i++) { int rv = regcomp (®ex[i], (char *) patterns[i], 0); @@ -309,58 +288,67 @@ stat_segment_ls (u8 ** patterns) } } - 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* */ + stat_segment_access_start (&sa); - clib_spinlock_unlock (sm->stat_segment_lockp); + stat_segment_directory_entry_t *counter_vec = get_stat_vector (); + for (j = 0; j < vec_len (counter_vec); j++) + { + for (i = 0; i < vec_len (patterns); i++) + { + int rv = regexec (®ex[i], counter_vec[j].name, 0, NULL, 0); + if (rv == 0) + { + vec_add1 (dir, j); + break; + } + } + if (vec_len (patterns) == 0) + vec_add1 (dir, j); + } for (i = 0; i < vec_len (patterns); i++) regfree (®ex[i]); + if (!stat_segment_access_end (&sa)) + { + /* Failed, clean up */ + vec_free (dir); + return 0; + + } + + /* Update last version */ + sm->current_epoch = sa.epoch; return dir; } stat_segment_data_t * -stat_segment_dump (u8 * stats[]) +stat_segment_dump (uint32_t * 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; + stat_segment_access_t sa; - clib_spinlock_lock (sm->stat_segment_lockp); + /* Has directory been update? */ + if (sm->shared_header->epoch != sm->current_epoch) + return 0; - sm->error_base = get_error_base (0); + stat_segment_access_start (&sa); 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!\n", 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])); + ep = vec_elt_at_index (sm->directory_vector, stats[i]); + vec_add1 (res, copy_data (ep)); } - clib_spinlock_unlock (sm->stat_segment_lockp); - return res; + if (stat_segment_access_end (&sa)) + return res; + + fprintf (stderr, "Epoch changed while reading, invalid results\n"); + // TODO increase counter + return 0; } /* Wrapper for accessing vectors from other languages */ @@ -380,6 +368,36 @@ stat_segment_string_vector (u8 ** string_vector, char *string) return string_vector; } +stat_segment_data_t * +stat_segment_dump_entry (uint32_t index) +{ + stat_client_main_t *sm = &stat_client_main; + stat_segment_directory_entry_t *ep; + stat_segment_data_t *res = 0; + stat_segment_access_t sa; + + stat_segment_access_start (&sa); + + /* Collect counter */ + ep = vec_elt_at_index (sm->directory_vector, index); + vec_add1 (res, copy_data (ep)); + + if (stat_segment_access_end (&sa)) + return res; + return 0; +} + +char * +stat_segment_index_to_name (uint32_t index) +{ + char *name; + stat_segment_directory_entry_t *counter_vec = get_stat_vector (); + stat_segment_directory_entry_t *ep; + ep = vec_elt_at_index (counter_vec, index); + name = strdup (ep->name); + return name; +} + /* * fd.io coding-style-patch-verification: ON * |