/* *------------------------------------------------------------------ * stat_client.c - Library for access to VPP statistics segment * * 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 #include #include #include #include #include "stat_client.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; } stat_client_main_t; stat_client_main_t stat_client_main; 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; 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) { clib_error_report (err); return -1; } err = clib_socket_recvmsg (&s, 0, 0, &fd, 1); if (err) { clib_error_report (err); 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) { fprintf (stderr, "WARNING: segment map returned %d\n", retval); return -1; } ASSERT (ssvmp && ssvmp->sh); /* 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; sm->counter_vector_by_name = (uword *) sm->shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR]; return 0; } 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; 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!", 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; } f64 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!", 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_data_t copy_data (stat_segment_directory_entry_t * ep, u64 * error_base, char *name) { stat_segment_data_t result = { 0 }; u32 error_index; int i; vlib_counter_t **combined_c; /* Combined counter */ counter_t **simple_c; /* Simple counter */ result.type = ep->type; result.name = 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; break; case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE: simple_c = ep->value; result.simple_counter_vec = vec_dup (simple_c); for (i = 0; i < vec_len (simple_c); i++) result.simple_counter_vec[i] = vec_dup (simple_c[i]); break; case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED: combined_c = ep->value; result.combined_counter_vec = vec_dup (combined_c); for (i = 0; i < vec_len (combined_c); i++) result.combined_counter_vec[i] = vec_dup (combined_c[i]); break; case STAT_DIR_TYPE_ERROR_INDEX: error_index = (uintptr_t) ep->value; result.error_value = error_base[error_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; /* 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) { int i, j; for (i = 0; i < vec_len (res); i++) { switch (res[i].type) { case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE: for (j = 0; j < vec_len (res[i].simple_counter_vec); j++) vec_free (res[i].simple_counter_vec[j]); vec_free (res[i].simple_counter_vec); break; case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED: for (j = 0; j < vec_len (res[i].combined_counter_vec); j++) vec_free (res[i].combined_counter_vec[j]); vec_free (res[i].combined_counter_vec); break; default: ; } } vec_free (res); } u8 ** stat_segment_ls (u8 ** patterns) { stat_client_main_t *sm = &stat_client_main; hash_pair_t *p; u8 **dir = 0; regex_t regex[vec_len (patterns)]; int i; for (i = 0; i < vec_len (patterns); i++) { int rv = regcomp (®ex[i], (char *) patterns[i], 0); if (rv) { fprintf (stderr, "Could not compile regex %s\n", patterns[i]); return dir; } } 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* */ clib_spinlock_unlock (sm->stat_segment_lockp); for (i = 0; i < vec_len (patterns); i++) regfree (®ex[i]); return dir; } stat_segment_data_t * stat_segment_dump (u8 * 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; clib_spinlock_lock (sm->stat_segment_lockp); 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!", 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])); } clib_spinlock_unlock (sm->stat_segment_lockp); return res; } /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */