X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvpp-api%2Fclient%2Fstat_client.c;h=0eaec15ffb6c6416300000fcc0d2ec116ad7a447;hb=3ffe6cadf;hp=ad7078ea74531548a8ba57e6c947744969a6f842;hpb=732021070fa0c731896ab3e29f802d3834c72ab7;p=vpp.git diff --git a/src/vpp-api/client/stat_client.c b/src/vpp-api/client/stat_client.c index ad7078ea745..0eaec15ffb6 100644 --- a/src/vpp-api/client/stat_client.c +++ b/src/vpp-api/client/stat_client.c @@ -17,254 +17,305 @@ *------------------------------------------------------------------ */ -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include #include "stat_client.h" +#include +#include -typedef struct +struct stat_client_main_t { - u64 current_epoch; - volatile int segment_ready; - ssvm_private_t stat_segment; /* mapped stats segment object */ - ssvm_shared_header_t *shared_header; - clib_spinlock_t *stat_segment_lockp; /* Spinlock for the stats segment */ - uword *counter_vector_by_name; - u64 *error_base; -} stat_client_main_t; + 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; -int -stat_segment_connect (char *socket_name) +stat_client_main_t * +stat_client_get (void) { - stat_client_main_t *sm = &stat_client_main; - ssvm_private_t *ssvmp = &sm->stat_segment; - clib_socket_t s = { 0 }; - clib_error_t *err; - int fd = -1, retval; - - memset (sm, 0, sizeof (*sm)); - s.config = socket_name; - s.flags = CLIB_SOCKET_F_IS_CLIENT | CLIB_SOCKET_F_SEQPACKET; - err = clib_socket_init (&s); - if (err) + stat_client_main_t *sm; + sm = (stat_client_main_t *) malloc (sizeof (stat_client_main_t)); + clib_memset (sm, 0, sizeof (stat_client_main_t)); + return sm; +} + +void +stat_client_free (stat_client_main_t * sm) +{ + free (sm); +} + +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) { - clib_error_report (err); + perror ("recvmsg failed"); return -1; } - err = clib_socket_recvmsg (&s, 0, 0, &fd, 1); - if (err) + cmsg = CMSG_FIRSTHDR (&msg); + if (cmsg && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { - clib_error_report (err); - return -1; + memmove (&fd, CMSG_DATA (cmsg), sizeof (fd)); } - clib_socket_close (&s); + return fd; +} + +static stat_segment_directory_entry_t * +get_stat_vector_r (stat_client_main_t * sm) +{ + ASSERT (sm->shared_header); + return stat_segment_pointer (sm->shared_header, + sm->shared_header->directory_offset); +} - memset (ssvmp, 0, sizeof (*ssvmp)); - ssvmp->fd = fd; +int +stat_segment_connect_r (const char *socket_name, stat_client_main_t * sm) +{ + int mfd = -1; + int sock; - /* Note: this closes memfd.fd */ - retval = ssvm_slave_init_memfd (ssvmp); - if (retval) + clib_memset (sm, 0, sizeof (*sm)); + if ((sock = socket (AF_UNIX, SOCK_SEQPACKET, 0)) < 0) { - fprintf (stderr, "WARNING: segment map returned %d\n", retval); + perror ("Stat client couldn't open socket"); return -1; } - ASSERT (ssvmp && ssvmp->sh); + 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) + { + close (sock); + return -2; + } + + if ((mfd = recv_fd (sock)) < 0) + { + close (sock); + fprintf (stderr, "Receiving file descriptor failed\n"); + return -3; + } + close (sock); + + /* 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) + { + close (mfd); + perror ("mmap fstat failed"); + return -4; + } + if ((memaddr = + mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, mfd, 0)) == MAP_FAILED) + { + close (mfd); + perror ("mmap map failed"); + return -5; + } - sm->counter_vector_by_name = - (uword *) sm->shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR]; + close (mfd); + sm->memory_size = st.st_size; + sm->shared_header = memaddr; + sm->directory_vector = + stat_segment_pointer (memaddr, sm->shared_header->directory_offset); return 0; } -void -stat_segment_disconnect (void) +int +stat_segment_connect (const char *socket_name) { stat_client_main_t *sm = &stat_client_main; - ssvm_delete_memfd (&sm->stat_segment); + return stat_segment_connect_r (socket_name, sm); +} + +void +stat_segment_disconnect_r (stat_client_main_t * sm) +{ + munmap (sm->shared_header, sm->memory_size); return; } -/* - * The application needs to register which counters it is interested - * in. - */ -stat_segment_cached_pointer_t * -stat_segment_register (u8 * stats[]) +typedef struct { - int i; - uword *p; - stat_client_main_t *sm = &stat_client_main; - stat_segment_cached_pointer_t *cp, *cached_pointer_vec = 0; + uint64_t epoch; +} stat_segment_access_t; - 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 void +stat_segment_access_start (stat_segment_access_t * sa, + stat_client_main_t * sm) +{ + stat_segment_shared_header_t *shared_header = sm->shared_header; + sa->epoch = shared_header->epoch; + while (shared_header->in_progress != 0) + ; + sm->directory_vector = stat_segment_pointer (sm->shared_header, + sm-> + shared_header->directory_offset); } -static u64 * -get_error_base (u32 thread_index) +static bool +stat_segment_access_end (stat_segment_access_t * sa, stat_client_main_t * sm) { - u64 *error_base = 0; - uword *p; - stat_client_main_t *sm = &stat_client_main; - stat_segment_directory_entry_t *ep; + stat_segment_shared_header_t *shared_header = sm->shared_header; - /* 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; + if (shared_header->epoch != sa->epoch || shared_header->in_progress) + return false; + return true; } -f64 -stat_segment_heartbeat (void) +void +stat_segment_disconnect (void) { - f64 *heartbeat = 0; - uword *p; stat_client_main_t *sm = &stat_client_main; + return stat_segment_disconnect_r (sm); +} + +double +stat_segment_heartbeat_r (stat_client_main_t * sm) +{ + stat_segment_access_t sa; 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; + /* Has directory been update? */ + if (sm->shared_header->epoch != sm->current_epoch) + return 0; + stat_segment_access_start (&sa, sm); + ep = vec_elt_at_index (sm->directory_vector, STAT_COUNTER_HEARTBEAT); + if (!stat_segment_access_end (&sa, sm)) + return 0.0; + return ep->value; } -static void -maybe_update_cached_pointers (stat_segment_cached_pointer_t * cached_pointers) +double +stat_segment_heartbeat (void) { 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]; + return stat_segment_heartbeat_r (sm); } -stat_segment_data_t -copy_data (stat_segment_directory_entry_t * ep, u64 * error_base, char *name) +static stat_segment_data_t +copy_data (stat_segment_directory_entry_t * ep, stat_client_main_t * sm) { stat_segment_data_t result = { 0 }; - u32 error_index; int i; vlib_counter_t **combined_c; /* Combined counter */ counter_t **simple_c; /* Simple counter */ + 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; + if (ep->offset == 0) + return result; + 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; + if (ep->offset == 0) + return result; + 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]; + /* Gather errors from all threads into a vector */ + offset_vector = stat_segment_pointer (sm->shared_header, + sm->shared_header->error_offset); + vec_validate (result.error_vector, vec_len (offset_vector) - 1); + for (i = 0; i < vec_len (offset_vector); i++) + { + counter_t *cb = + stat_segment_pointer (sm->shared_header, offset_vector[i]); + result.error_vector[i] = cb[ep->index]; + } + break; + + case STAT_DIR_TYPE_NAME_VECTOR: + if (ep->offset == 0) + return result; + uint8_t **name_vector = + stat_segment_pointer (sm->shared_header, ep->offset); + result.name_vector = vec_dup (name_vector); + offset_vector = + stat_segment_pointer (sm->shared_header, ep->offset_vector); + for (i = 0; i < vec_len (name_vector); i++) + { + if (offset_vector[i]) + { + u8 *name = + stat_segment_pointer (sm->shared_header, offset_vector[i]); + result.name_vector[i] = vec_dup (name); + } + else + result.name_vector[i] = 0; + } break; default: - fprintf (stderr, "Unknown type: %d", ep->type); + fprintf (stderr, "Unknown type: %d\n", ep->type); } return result; } -stat_segment_data_t * -stat_segment_collect (stat_segment_cached_pointer_t * cached_pointers) -{ - stat_client_main_t *sm = &stat_client_main; - stat_segment_data_t *res = 0; - int i; - - /* Grab the stats segment lock */ - clib_spinlock_lock (sm->stat_segment_lockp); - - /* see if we need to update cached pointers */ - maybe_update_cached_pointers (cached_pointers); - - for (i = 0; i < vec_len (cached_pointers); i++) - { - vec_add1 (res, - copy_data (cached_pointers[i].ep, sm->error_base, - cached_pointers[i].name)); - } - - /* Drop the lock */ - clib_spinlock_unlock (sm->stat_segment_lockp); - - return res; -} - void stat_segment_data_free (stat_segment_data_t * res) { @@ -286,22 +337,23 @@ stat_segment_data_free (stat_segment_data_t * res) default: ; } + free (res[i].name); } vec_free (res); } -u8 ** -stat_segment_ls (u8 ** patterns) +uint32_t * +stat_segment_ls_r (uint8_t ** patterns, stat_client_main_t * sm) { - stat_client_main_t *sm = &stat_client_main; - hash_pair_t *p; - u8 **dir = 0; + 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); + int rv = regcomp (®ex[i], (const char *) patterns[i], 0); if (rv) { fprintf (stderr, "Could not compile regex %s\n", patterns[i]); @@ -309,58 +361,80 @@ 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, sm); - clib_spinlock_unlock (sm->stat_segment_lockp); + stat_segment_directory_entry_t *counter_vec = get_stat_vector_r (sm); + 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, sm)) + { + /* Failed, clean up */ + vec_free (dir); + return 0; + + } + + /* Update last version */ + sm->current_epoch = sa.epoch; return dir; } +uint32_t * +stat_segment_ls (uint8_t ** patterns) +{ + stat_client_main_t *sm = &stat_client_main; + return stat_segment_ls_r ((uint8_t **) patterns, sm); +} + stat_segment_data_t * -stat_segment_dump (u8 * stats[]) +stat_segment_dump_r (uint32_t * stats, stat_client_main_t * sm) { 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, sm); 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, sm)); } - clib_spinlock_unlock (sm->stat_segment_lockp); - return res; + if (stat_segment_access_end (&sa, sm)) + return res; + + fprintf (stderr, "Epoch changed while reading, invalid results\n"); + // TODO increase counter + return 0; +} + +stat_segment_data_t * +stat_segment_dump (uint32_t * stats) +{ + stat_client_main_t *sm = &stat_client_main; + return stat_segment_dump_r (stats, sm); } /* Wrapper for accessing vectors from other languages */ @@ -370,16 +444,88 @@ stat_segment_vec_len (void *vec) return vec_len (vec); } +void +stat_segment_vec_free (void *vec) +{ + vec_free (vec); +} + /* Create a vector from a string (or add to existing) */ -u8 ** -stat_segment_string_vector (u8 ** string_vector, char *string) +uint8_t ** +stat_segment_string_vector (uint8_t ** string_vector, const char *string) { - u8 *name = 0; - name = vec_dup ((u8 *) string); - vec_add1 (string_vector, (u8 *) name); + uint8_t *name = 0; + size_t len = strlen (string); + + vec_validate_init_c_string (name, string, len); + vec_add1 (string_vector, name); return string_vector; } +stat_segment_data_t * +stat_segment_dump_entry_r (uint32_t index, stat_client_main_t * sm) +{ + stat_segment_directory_entry_t *ep; + stat_segment_data_t *res = 0; + stat_segment_access_t sa; + + stat_segment_access_start (&sa, sm); + + /* Collect counter */ + ep = vec_elt_at_index (sm->directory_vector, index); + vec_add1 (res, copy_data (ep, sm)); + + if (stat_segment_access_end (&sa, sm)) + return res; + return 0; +} + +stat_segment_data_t * +stat_segment_dump_entry (uint32_t index) +{ + stat_client_main_t *sm = &stat_client_main; + return stat_segment_dump_entry_r (index, sm); +} + +char * +stat_segment_index_to_name_r (uint32_t index, stat_client_main_t * sm) +{ + stat_segment_directory_entry_t *ep; + stat_segment_access_t sa; + stat_segment_directory_entry_t *vec; + + /* Has directory been update? */ + if (sm->shared_header->epoch != sm->current_epoch) + return 0; + stat_segment_access_start (&sa, sm); + vec = get_stat_vector_r (sm); + ep = vec_elt_at_index (vec, index); + if (!stat_segment_access_end (&sa, sm)) + return 0; + return strdup (ep->name); +} + +char * +stat_segment_index_to_name (uint32_t index) +{ + stat_client_main_t *sm = &stat_client_main; + return stat_segment_index_to_name_r (index, sm); +} + +uint64_t +stat_segment_version_r (stat_client_main_t * sm) +{ + ASSERT (sm->shared_header); + return sm->shared_header->version; +} + +uint64_t +stat_segment_version (void) +{ + stat_client_main_t *sm = &stat_client_main; + return stat_segment_version_r (sm); +} + /* * fd.io coding-style-patch-verification: ON *