X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvpp-api%2Fclient%2Fstat_client.c;h=2c30be6232668cac3f18118bd57c2a00ba8ff95c;hb=db0238090;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..2c30be62326 100644 --- a/src/vpp-api/client/stat_client.c +++ b/src/vpp-api/client/stat_client.c @@ -17,252 +17,298 @@ *------------------------------------------------------------------ */ -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include -#include "stat_client.h" +#include +#include +#include +#include +#include +#include -typedef struct +stat_client_main_t stat_client_main; + +stat_client_main_t * +stat_client_get (void) { - 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; + 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; +} -stat_client_main_t stat_client_main; +void +stat_client_free (stat_client_main_t * sm) +{ + free (sm); +} -int -stat_segment_connect (char *socket_name) +static int +recv_fd (int sock) { - 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) + 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; +} - memset (ssvmp, 0, sizeof (*ssvmp)); - ssvmp->fd = fd; +static stat_segment_directory_entry_t * +get_stat_vector_r (stat_client_main_t * sm) +{ + ASSERT (sm->shared_header); + return stat_segment_adjust (sm, + (void *) sm->shared_header->directory_vector); +} + +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; + } - /* 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 ((mfd = recv_fd (sock)) < 0) + { + close (sock); + fprintf (stderr, "Receiving file descriptor failed\n"); + return -3; + } + close (sock); - sm->counter_vector_by_name = - (uword *) sm->shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR]; + /* mmap shared memory segment. */ + void *memaddr; + struct stat st = { 0 }; + + 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; + } + + close (mfd); + sm->memory_size = st.st_size; + sm->shared_header = memaddr; + sm->directory_vector = + stat_segment_adjust (sm, (void *) sm->shared_header->directory_vector); 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[]) +void +stat_segment_disconnect (void) { - int i; - uword *p; stat_client_main_t *sm = &stat_client_main; - stat_segment_cached_pointer_t *cp, *cached_pointer_vec = 0; - - 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; + return stat_segment_disconnect_r (sm); } -static u64 * -get_error_base (u32 thread_index) +double +stat_segment_heartbeat_r (stat_client_main_t * sm) { - u64 *error_base = 0; - uword *p; - stat_client_main_t *sm = &stat_client_main; + 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, "/err/%d/counter_vector", thread_index)); - if (p) - { - ep = (stat_segment_directory_entry_t *) (p[0]); - error_base = ep->value; - } - return error_base; + /* Has directory been updated? */ + if (sm->shared_header->epoch != sm->current_epoch) + return 0; + if (stat_segment_access_start (&sa, sm)) + return 0; + ep = vec_elt_at_index (sm->directory_vector, STAT_COUNTER_HEARTBEAT); + if (!stat_segment_access_end (&sa, sm)) + return 0.0; + return ep->value; } -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; + return stat_segment_heartbeat_r (sm); } -static void -maybe_update_cached_pointers (stat_segment_cached_pointer_t * cached_pointers) +#define stat_vec_dup(S,V) \ + ({ \ + __typeof__ ((V)[0]) * _v(v) = 0; \ + if (V && ((void *)V > (void *)S->shared_header) && \ + (((void*)V + vec_bytes(V)) < \ + ((void *)S->shared_header + S->memory_size))) \ + _v(v) = vec_dup(V); \ + _v(v); \ +}) + +static counter_t * +stat_vec_simple_init (counter_t c) { - 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]); - } + counter_t *v = 0; + vec_add1 (v, c); + return v; +} - /* And remember that we did... */ - sm->current_epoch = - (u64) sm->shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH]; +static vlib_counter_t * +stat_vec_combined_init (vlib_counter_t c) +{ + vlib_counter_t *v = 0; + vec_add1 (v, c); + return v; } -stat_segment_data_t -copy_data (stat_segment_directory_entry_t * ep, u64 * error_base, char *name) +/* + * If index2 is specified copy out the column (the indexed value across all + * threads), otherwise copy out all values. + */ +static stat_segment_data_t +copy_data (stat_segment_directory_entry_t *ep, u32 index2, char *name, + 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 *error_vector; + + assert (sm->shared_header); + result.type = ep->type; - result.name = name; + result.name = strdup (name ? name : 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; - result.simple_counter_vec = vec_dup (simple_c); + simple_c = stat_segment_adjust (sm, ep->data); + result.simple_counter_vec = stat_vec_dup (sm, simple_c); for (i = 0; i < vec_len (simple_c); i++) - result.simple_counter_vec[i] = vec_dup (simple_c[i]); + { + counter_t *cb = stat_segment_adjust (sm, simple_c[i]); + if (index2 != ~0) + result.simple_counter_vec[i] = stat_vec_simple_init (cb[index2]); + else + result.simple_counter_vec[i] = stat_vec_dup (sm, cb); + } break; case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED: - combined_c = ep->value; - result.combined_counter_vec = vec_dup (combined_c); + combined_c = stat_segment_adjust (sm, ep->data); + result.combined_counter_vec = stat_vec_dup (sm, combined_c); 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_adjust (sm, combined_c[i]); + if (index2 != ~0) + result.combined_counter_vec[i] = + stat_vec_combined_init (cb[index2]); + else + result.combined_counter_vec[i] = stat_vec_dup (sm, 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 */ + error_vector = + stat_segment_adjust (sm, (void *) sm->shared_header->error_vector); + vec_validate (result.error_vector, vec_len (error_vector) - 1); + for (i = 0; i < vec_len (error_vector); i++) + { + counter_t *cb = stat_segment_adjust (sm, (void *) error_vector[i]); + result.error_vector[i] = cb[ep->index]; + } break; - default: - fprintf (stderr, "Unknown type: %d", 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; + case STAT_DIR_TYPE_NAME_VECTOR: + { + uint8_t **name_vector = stat_segment_adjust (sm, ep->data); + result.name_vector = stat_vec_dup (sm, name_vector); + for (i = 0; i < vec_len (name_vector); i++) + { + u8 *name = stat_segment_adjust (sm, name_vector[i]); + result.name_vector[i] = stat_vec_dup (sm, name); + } + } + break; - /* Grab the stats segment lock */ - clib_spinlock_lock (sm->stat_segment_lockp); + case STAT_DIR_TYPE_SYMLINK: + /* Gather info from all threads into a vector */ + { + stat_segment_directory_entry_t *ep2; + ep2 = vec_elt_at_index (sm->directory_vector, ep->index1); + return copy_data (ep2, ep->index2, ep->name, sm); + } - /* see if we need to update cached pointers */ - maybe_update_cached_pointers (cached_pointers); + case STAT_DIR_TYPE_EMPTY: + break; - 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)); + default: + fprintf (stderr, "Unknown type: %d\n", ep->type); } - - /* Drop the lock */ - clib_spinlock_unlock (sm->stat_segment_lockp); - - return res; + return result; } void @@ -283,25 +329,36 @@ stat_segment_data_free (stat_segment_data_t * res) vec_free (res[i].combined_counter_vec[j]); vec_free (res[i].combined_counter_vec); break; + case STAT_DIR_TYPE_NAME_VECTOR: + for (j = 0; j < vec_len (res[i].name_vector); j++) + vec_free (res[i].name_vector[j]); + vec_free (res[i].name_vector); + break; + case STAT_DIR_TYPE_ERROR_INDEX: + vec_free (res[i].error_vector); + break; + case STAT_DIR_TYPE_SCALAR_INDEX: + break; default: - ; + assert (0); } + 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 +366,83 @@ stat_segment_ls (u8 ** patterns) } } - clib_spinlock_lock (sm->stat_segment_lockp); + if (stat_segment_access_start (&sa, sm)) + return 0; - /* *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; - } + 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); } - if (vec_len(patterns) == 0) - vec_add1 (dir, (u8 *)p->key); - })); - /* *INDENT-ON* */ - - clib_spinlock_unlock (sm->stat_segment_lockp); 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; + + /* Has directory been update? */ + if (sm->shared_header->epoch != sm->current_epoch) + return 0; - clib_spinlock_lock (sm->stat_segment_lockp); + if (stat_segment_access_start (&sa, sm)) + return 0; - sm->error_base = get_error_base (0); 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, ~0, 0, 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 +452,94 @@ 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; + + /* Has directory been update? */ + if (sm->shared_header->epoch != sm->current_epoch) + return 0; + + if (stat_segment_access_start (&sa, sm)) + return 0; + + /* Collect counter */ + ep = vec_elt_at_index (sm->directory_vector, index); + vec_add1 (res, copy_data (ep, ~0, 0, 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; + if (stat_segment_access_start (&sa, sm)) + return 0; + 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 *