2 *------------------------------------------------------------------
3 * stat_client.c - Library for access to VPP statistics segment
5 * Copyright (c) 2018 Cisco and/or its affiliates.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at:
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *------------------------------------------------------------------
20 #include <vlib/vlib.h>
21 #include <vppinfra/socket.h>
23 #include <vpp/stats/stats.h>
25 #include "stat_client.h"
30 volatile int segment_ready;
31 ssvm_private_t stat_segment; /* mapped stats segment object */
32 ssvm_shared_header_t *shared_header;
33 clib_spinlock_t *stat_segment_lockp; /* Spinlock for the stats segment */
34 uword *counter_vector_by_name;
38 stat_client_main_t stat_client_main;
41 stat_segment_connect (char *socket_name)
43 stat_client_main_t *sm = &stat_client_main;
44 ssvm_private_t *ssvmp = &sm->stat_segment;
45 clib_socket_t s = { 0 };
49 memset (sm, 0, sizeof (*sm));
50 s.config = socket_name;
51 s.flags = CLIB_SOCKET_F_IS_CLIENT | CLIB_SOCKET_F_SEQPACKET;
52 err = clib_socket_init (&s);
55 clib_error_report (err);
58 err = clib_socket_recvmsg (&s, 0, 0, &fd, 1);
61 clib_error_report (err);
64 clib_socket_close (&s);
66 memset (ssvmp, 0, sizeof (*ssvmp));
69 /* Note: this closes memfd.fd */
70 retval = ssvm_slave_init_memfd (ssvmp);
73 fprintf (stderr, "WARNING: segment map returned %d\n", retval);
77 ASSERT (ssvmp && ssvmp->sh);
79 /* Pick up the segment lock from the shared memory header */
80 sm->shared_header = ssvmp->sh;
81 sm->stat_segment_lockp = (clib_spinlock_t *) (sm->shared_header->opaque[0]);
82 sm->segment_ready = 1;
84 sm->counter_vector_by_name =
85 (uword *) sm->shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR];
91 stat_segment_disconnect (void)
93 stat_client_main_t *sm = &stat_client_main;
94 ssvm_delete_memfd (&sm->stat_segment);
99 * The application needs to register which counters it is interested
102 stat_segment_cached_pointer_t *
103 stat_segment_register (u8 * stats[])
107 stat_client_main_t *sm = &stat_client_main;
108 stat_segment_cached_pointer_t *cp, *cached_pointer_vec = 0;
110 for (i = 0; i < vec_len (stats); i++)
112 p = hash_get_mem (sm->counter_vector_by_name, stats[i]);
115 fprintf (stderr, "WARN: %s not in directory!", stats[i]);
118 vec_add2 (cached_pointer_vec, cp, 1);
119 cp->name = strdup ((char *) stats[i]); // Point to p->key instead?
121 return cached_pointer_vec;
125 get_error_base (u32 thread_index)
129 stat_client_main_t *sm = &stat_client_main;
130 stat_segment_directory_entry_t *ep;
132 /* Special case /err/0/counter_vector */
133 p = hash_get_mem (sm->counter_vector_by_name,
134 format (0, "/err/%d/counter_vector", thread_index));
137 ep = (stat_segment_directory_entry_t *) (p[0]);
138 error_base = ep->value;
144 stat_segment_heartbeat (void)
148 stat_client_main_t *sm = &stat_client_main;
149 stat_segment_directory_entry_t *ep;
151 /* Special case /err/0/counter_vector */
152 p = hash_get_mem (sm->counter_vector_by_name,
153 format (0, "/sys/heartbeat%c", 0));
156 ep = (stat_segment_directory_entry_t *) (p[0]);
157 heartbeat = ep->value;
163 maybe_update_cached_pointers (stat_segment_cached_pointer_t * cached_pointers)
165 stat_client_main_t *sm = &stat_client_main;
166 stat_segment_cached_pointer_t *cp;
170 /* Cached pointers OK? */
171 if (sm->current_epoch ==
172 (u64) sm->shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH])
175 /* Special case /err/0/counter_vector */
176 sm->error_base = get_error_base (0);
178 /* Nope, fix them... */
179 for (i = 0; i < vec_len (cached_pointers); i++)
181 cp = &cached_pointers[i];
183 p = hash_get_mem (sm->counter_vector_by_name, cp->name);
186 fprintf (stderr, "WARN: %s not in directory!", cp->name);
189 cp->ep = (stat_segment_directory_entry_t *) (p[0]);
192 /* And remember that we did... */
194 (u64) sm->shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH];
198 copy_data (stat_segment_directory_entry_t * ep, u64 * error_base, char *name)
200 stat_segment_data_t result = { 0 };
203 vlib_counter_t **combined_c; /* Combined counter */
204 counter_t **simple_c; /* Simple counter */
205 result.type = ep->type;
209 case STAT_DIR_TYPE_SCALAR_POINTER:
210 result.scalar_value = *(f64 *) ep->value;
213 case STAT_DIR_TYPE_VECTOR_POINTER:
214 result.vector_pointer = ep->value;
217 case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
218 simple_c = ep->value;
219 result.simple_counter_vec = vec_dup (simple_c);
220 for (i = 0; i < vec_len (simple_c); i++)
221 result.simple_counter_vec[i] = vec_dup (simple_c[i]);
224 case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
225 combined_c = ep->value;
226 result.combined_counter_vec = vec_dup (combined_c);
227 for (i = 0; i < vec_len (combined_c); i++)
228 result.combined_counter_vec[i] = vec_dup (combined_c[i]);
231 case STAT_DIR_TYPE_ERROR_INDEX:
232 error_index = (uintptr_t) ep->value;
233 result.error_value = error_base[error_index];
237 fprintf (stderr, "Unknown type: %d", ep->type);
242 stat_segment_data_t *
243 stat_segment_collect (stat_segment_cached_pointer_t * cached_pointers)
245 stat_client_main_t *sm = &stat_client_main;
246 stat_segment_data_t *res = 0;
249 /* Grab the stats segment lock */
250 clib_spinlock_lock (sm->stat_segment_lockp);
252 /* see if we need to update cached pointers */
253 maybe_update_cached_pointers (cached_pointers);
255 for (i = 0; i < vec_len (cached_pointers); i++)
258 copy_data (cached_pointers[i].ep, sm->error_base,
259 cached_pointers[i].name));
263 clib_spinlock_unlock (sm->stat_segment_lockp);
269 stat_segment_data_free (stat_segment_data_t * res)
272 for (i = 0; i < vec_len (res); i++)
276 case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
277 for (j = 0; j < vec_len (res[i].simple_counter_vec); j++)
278 vec_free (res[i].simple_counter_vec[j]);
279 vec_free (res[i].simple_counter_vec);
281 case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
282 for (j = 0; j < vec_len (res[i].combined_counter_vec); j++)
283 vec_free (res[i].combined_counter_vec[j]);
284 vec_free (res[i].combined_counter_vec);
294 stat_segment_ls (u8 ** patterns)
296 stat_client_main_t *sm = &stat_client_main;
299 regex_t regex[vec_len (patterns)];
302 for (i = 0; i < vec_len (patterns); i++)
304 int rv = regcomp (®ex[i], (char *) patterns[i], 0);
307 fprintf (stderr, "Could not compile regex %s\n", patterns[i]);
312 clib_spinlock_lock (sm->stat_segment_lockp);
315 hash_foreach_pair (p, sm->counter_vector_by_name,
317 for (i = 0; i < vec_len(patterns); i++) {
318 int rv = regexec(®ex[i], (char *)p->key, 0, NULL, 0);
320 vec_add1 (dir, (u8 *)p->key);
324 if (vec_len(patterns) == 0)
325 vec_add1 (dir, (u8 *)p->key);
329 clib_spinlock_unlock (sm->stat_segment_lockp);
331 for (i = 0; i < vec_len (patterns); i++)
337 stat_segment_data_t *
338 stat_segment_dump (u8 * stats[])
342 stat_client_main_t *sm = &stat_client_main;
343 stat_segment_directory_entry_t *ep;
344 stat_segment_data_t *res = 0;
346 clib_spinlock_lock (sm->stat_segment_lockp);
348 sm->error_base = get_error_base (0);
349 for (i = 0; i < vec_len (stats); i++)
351 p = hash_get_mem (sm->counter_vector_by_name, stats[i]);
354 fprintf (stderr, "WARN: %s not in directory!", stats[i]);
357 /* Collect counter */
358 ep = (stat_segment_directory_entry_t *) (p[0]);
359 vec_add1 (res, copy_data (ep, sm->error_base, (char *) stats[i]));
361 clib_spinlock_unlock (sm->stat_segment_lockp);
367 * fd.io coding-style-patch-verification: ON
370 * eval: (c-set-style "gnu")