blob: ad7078ea74531548a8ba57e6c947744969a6f842 [file] [log] [blame]
Ole Troan2fee1672018-08-23 13:00:53 +02001/*
2 *------------------------------------------------------------------
3 * stat_client.c - Library for access to VPP statistics segment
4 *
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:
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
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 *------------------------------------------------------------------
18 */
19
20#include <vlib/vlib.h>
21#include <vppinfra/socket.h>
22#include <svm/ssvm.h>
23#include <vpp/stats/stats.h>
24#include <regex.h>
25#include "stat_client.h"
26
27typedef struct
28{
29 u64 current_epoch;
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;
35 u64 *error_base;
36} stat_client_main_t;
37
38stat_client_main_t stat_client_main;
39
40int
41stat_segment_connect (char *socket_name)
42{
43 stat_client_main_t *sm = &stat_client_main;
44 ssvm_private_t *ssvmp = &sm->stat_segment;
45 clib_socket_t s = { 0 };
46 clib_error_t *err;
47 int fd = -1, retval;
48
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);
53 if (err)
54 {
55 clib_error_report (err);
56 return -1;
57 }
58 err = clib_socket_recvmsg (&s, 0, 0, &fd, 1);
59 if (err)
60 {
61 clib_error_report (err);
62 return -1;
63 }
64 clib_socket_close (&s);
65
66 memset (ssvmp, 0, sizeof (*ssvmp));
67 ssvmp->fd = fd;
68
69 /* Note: this closes memfd.fd */
70 retval = ssvm_slave_init_memfd (ssvmp);
71 if (retval)
72 {
73 fprintf (stderr, "WARNING: segment map returned %d\n", retval);
74 return -1;
75 }
76
77 ASSERT (ssvmp && ssvmp->sh);
78
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;
83
84 sm->counter_vector_by_name =
85 (uword *) sm->shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR];
86
87 return 0;
88}
89
90void
91stat_segment_disconnect (void)
92{
93 stat_client_main_t *sm = &stat_client_main;
94 ssvm_delete_memfd (&sm->stat_segment);
95 return;
96}
97
98/*
99 * The application needs to register which counters it is interested
100 * in.
101 */
102stat_segment_cached_pointer_t *
103stat_segment_register (u8 * stats[])
104{
105 int i;
106 uword *p;
107 stat_client_main_t *sm = &stat_client_main;
108 stat_segment_cached_pointer_t *cp, *cached_pointer_vec = 0;
109
110 for (i = 0; i < vec_len (stats); i++)
111 {
112 p = hash_get_mem (sm->counter_vector_by_name, stats[i]);
113 if (p == 0)
114 {
Ole Troan73202102018-08-31 00:29:48 +0200115 fprintf (stderr, "WARN: %s not in directory!\n", stats[i]);
Ole Troan2fee1672018-08-23 13:00:53 +0200116 continue;
117 }
118 vec_add2 (cached_pointer_vec, cp, 1);
119 cp->name = strdup ((char *) stats[i]); // Point to p->key instead?
120 }
121 return cached_pointer_vec;
122}
123
124static u64 *
125get_error_base (u32 thread_index)
126{
127 u64 *error_base = 0;
128 uword *p;
129 stat_client_main_t *sm = &stat_client_main;
130 stat_segment_directory_entry_t *ep;
131
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));
135 if (p)
136 {
137 ep = (stat_segment_directory_entry_t *) (p[0]);
138 error_base = ep->value;
139 }
140 return error_base;
141}
142
143f64
144stat_segment_heartbeat (void)
145{
146 f64 *heartbeat = 0;
147 uword *p;
148 stat_client_main_t *sm = &stat_client_main;
149 stat_segment_directory_entry_t *ep;
150
151 /* Special case /err/0/counter_vector */
152 p = hash_get_mem (sm->counter_vector_by_name,
153 format (0, "/sys/heartbeat%c", 0));
154 if (p)
155 {
156 ep = (stat_segment_directory_entry_t *) (p[0]);
157 heartbeat = ep->value;
158 }
159 return *heartbeat;
160}
161
162static void
163maybe_update_cached_pointers (stat_segment_cached_pointer_t * cached_pointers)
164{
165 stat_client_main_t *sm = &stat_client_main;
166 stat_segment_cached_pointer_t *cp;
167 uword *p;
168 int i;
169
170 /* Cached pointers OK? */
171 if (sm->current_epoch ==
172 (u64) sm->shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH])
173 return;
174
175 /* Special case /err/0/counter_vector */
176 sm->error_base = get_error_base (0);
177
178 /* Nope, fix them... */
179 for (i = 0; i < vec_len (cached_pointers); i++)
180 {
181 cp = &cached_pointers[i];
182
183 p = hash_get_mem (sm->counter_vector_by_name, cp->name);
184 if (p == 0)
185 {
Ole Troan73202102018-08-31 00:29:48 +0200186 fprintf (stderr, "WARN: %s not in directory!\n", cp->name);
Ole Troan2fee1672018-08-23 13:00:53 +0200187 continue;
188 }
189 cp->ep = (stat_segment_directory_entry_t *) (p[0]);
190 }
191
192 /* And remember that we did... */
193 sm->current_epoch =
194 (u64) sm->shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH];
195}
196
197stat_segment_data_t
198copy_data (stat_segment_directory_entry_t * ep, u64 * error_base, char *name)
199{
200 stat_segment_data_t result = { 0 };
201 u32 error_index;
202 int i;
203 vlib_counter_t **combined_c; /* Combined counter */
204 counter_t **simple_c; /* Simple counter */
205 result.type = ep->type;
206 result.name = name;
207 switch (ep->type)
208 {
209 case STAT_DIR_TYPE_SCALAR_POINTER:
210 result.scalar_value = *(f64 *) ep->value;
211 break;
212
213 case STAT_DIR_TYPE_VECTOR_POINTER:
214 result.vector_pointer = ep->value;
215 break;
216
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]);
222 break;
223
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]);
229 break;
230
231 case STAT_DIR_TYPE_ERROR_INDEX:
232 error_index = (uintptr_t) ep->value;
233 result.error_value = error_base[error_index];
234 break;
235
236 default:
237 fprintf (stderr, "Unknown type: %d", ep->type);
238 }
239 return result;
240}
241
242stat_segment_data_t *
243stat_segment_collect (stat_segment_cached_pointer_t * cached_pointers)
244{
245 stat_client_main_t *sm = &stat_client_main;
246 stat_segment_data_t *res = 0;
247 int i;
248
249 /* Grab the stats segment lock */
250 clib_spinlock_lock (sm->stat_segment_lockp);
251
252 /* see if we need to update cached pointers */
253 maybe_update_cached_pointers (cached_pointers);
254
255 for (i = 0; i < vec_len (cached_pointers); i++)
256 {
257 vec_add1 (res,
258 copy_data (cached_pointers[i].ep, sm->error_base,
259 cached_pointers[i].name));
260 }
261
262 /* Drop the lock */
263 clib_spinlock_unlock (sm->stat_segment_lockp);
264
265 return res;
266}
267
268void
269stat_segment_data_free (stat_segment_data_t * res)
270{
271 int i, j;
272 for (i = 0; i < vec_len (res); i++)
273 {
274 switch (res[i].type)
275 {
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);
280 break;
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);
285 break;
286 default:
287 ;
288 }
289 }
290 vec_free (res);
291}
292
293u8 **
294stat_segment_ls (u8 ** patterns)
295{
296 stat_client_main_t *sm = &stat_client_main;
297 hash_pair_t *p;
298 u8 **dir = 0;
299 regex_t regex[vec_len (patterns)];
300
301 int i;
302 for (i = 0; i < vec_len (patterns); i++)
303 {
304 int rv = regcomp (&regex[i], (char *) patterns[i], 0);
305 if (rv)
306 {
307 fprintf (stderr, "Could not compile regex %s\n", patterns[i]);
308 return dir;
309 }
310 }
311
312 clib_spinlock_lock (sm->stat_segment_lockp);
313
314 /* *INDENT-OFF* */
315 hash_foreach_pair (p, sm->counter_vector_by_name,
316 ({
317 for (i = 0; i < vec_len(patterns); i++) {
318 int rv = regexec(&regex[i], (char *)p->key, 0, NULL, 0);
319 if (rv == 0) {
320 vec_add1 (dir, (u8 *)p->key);
321 break;
322 }
323 }
324 if (vec_len(patterns) == 0)
325 vec_add1 (dir, (u8 *)p->key);
326 }));
327 /* *INDENT-ON* */
328
329 clib_spinlock_unlock (sm->stat_segment_lockp);
330
331 for (i = 0; i < vec_len (patterns); i++)
332 regfree (&regex[i]);
333
334 return dir;
335}
336
337stat_segment_data_t *
338stat_segment_dump (u8 * stats[])
339{
340 int i;
341 uword *p;
342 stat_client_main_t *sm = &stat_client_main;
343 stat_segment_directory_entry_t *ep;
344 stat_segment_data_t *res = 0;
345
346 clib_spinlock_lock (sm->stat_segment_lockp);
347
348 sm->error_base = get_error_base (0);
349 for (i = 0; i < vec_len (stats); i++)
350 {
351 p = hash_get_mem (sm->counter_vector_by_name, stats[i]);
352 if (p == 0)
353 {
Ole Troan73202102018-08-31 00:29:48 +0200354 fprintf (stderr, "WARN: %s not in directory!\n", stats[i]);
Ole Troan2fee1672018-08-23 13:00:53 +0200355 continue;
356 }
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]));
360 }
361 clib_spinlock_unlock (sm->stat_segment_lockp);
362
363 return res;
364}
365
Ole Troan73202102018-08-31 00:29:48 +0200366/* Wrapper for accessing vectors from other languages */
367int
368stat_segment_vec_len (void *vec)
369{
370 return vec_len (vec);
371}
372
373/* Create a vector from a string (or add to existing) */
374u8 **
375stat_segment_string_vector (u8 ** string_vector, char *string)
376{
377 u8 *name = 0;
378 name = vec_dup ((u8 *) string);
379 vec_add1 (string_vector, (u8 *) name);
380 return string_vector;
381}
382
Ole Troan2fee1672018-08-23 13:00:53 +0200383/*
384 * fd.io coding-style-patch-verification: ON
385 *
386 * Local Variables:
387 * eval: (c-set-style "gnu")
388 * End:
389 */