FD.io VPP  v19.08-23-g4b943d6
Vector Packet Processing
stat_client.c
Go to the documentation of this file.
1 /*
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 <stdio.h>
21 #include <errno.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <sys/un.h>
25 #include <stdbool.h>
26 #include <sys/stat.h>
27 #include <regex.h>
28 #include <assert.h>
29 #include <vppinfra/vec.h>
30 #include <vppinfra/lock.h>
31 #include "stat_client.h"
32 #include <stdatomic.h>
33 #include <vpp/stats/stat_segment.h>
34 
36 {
37  uint64_t current_epoch;
40  ssize_t memory_size;
41 };
42 
44 
47 {
49  sm = (stat_client_main_t *) malloc (sizeof (stat_client_main_t));
50  clib_memset (sm, 0, sizeof (stat_client_main_t));
51  return sm;
52 }
53 
54 void
56 {
57  free (sm);
58 }
59 
60 static int
61 recv_fd (int sock)
62 {
63  struct msghdr msg = { 0 };
64  struct cmsghdr *cmsg;
65  int fd = -1;
66  char iobuf[1];
67  struct iovec io = {.iov_base = iobuf,.iov_len = sizeof (iobuf) };
68  union
69  {
70  char buf[CMSG_SPACE (sizeof (fd))];
71  struct cmsghdr align;
72  } u;
73  msg.msg_iov = &io;
74  msg.msg_iovlen = 1;
75  msg.msg_control = u.buf;
76  msg.msg_controllen = sizeof (u.buf);
77 
78  ssize_t size;
79  if ((size = recvmsg (sock, &msg, 0)) < 0)
80  {
81  perror ("recvmsg failed");
82  return -1;
83  }
84  cmsg = CMSG_FIRSTHDR (&msg);
85  if (cmsg && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
86  {
87  memmove (&fd, CMSG_DATA (cmsg), sizeof (fd));
88  }
89  return fd;
90 }
91 
94 {
95  ASSERT (sm->shared_header);
98 }
99 
100 int
101 stat_segment_connect_r (const char *socket_name, stat_client_main_t * sm)
102 {
103  int mfd = -1;
104  int sock;
105 
106  clib_memset (sm, 0, sizeof (*sm));
107  if ((sock = socket (AF_UNIX, SOCK_SEQPACKET, 0)) < 0)
108  {
109  perror ("Stat client couldn't open socket");
110  return -1;
111  }
112 
113  struct sockaddr_un un = { 0 };
114  un.sun_family = AF_UNIX;
115  strncpy ((char *) un.sun_path, socket_name, sizeof (un.sun_path) - 1);
116  if (connect (sock, (struct sockaddr *) &un, sizeof (struct sockaddr_un)) <
117  0)
118  {
119  close (sock);
120  return -2;
121  }
122 
123  if ((mfd = recv_fd (sock)) < 0)
124  {
125  close (sock);
126  fprintf (stderr, "Receiving file descriptor failed\n");
127  return -3;
128  }
129  close (sock);
130 
131  /* mmap shared memory segment. */
132  void *memaddr;
133  struct stat st = { 0 };
134 
135  if (fstat (mfd, &st) == -1)
136  {
137  close (mfd);
138  perror ("mmap fstat failed");
139  return -4;
140  }
141  if ((memaddr =
142  mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, mfd, 0)) == MAP_FAILED)
143  {
144  close (mfd);
145  perror ("mmap map failed");
146  return -5;
147  }
148 
149  close (mfd);
150  sm->memory_size = st.st_size;
151  sm->shared_header = memaddr;
152  sm->directory_vector =
154 
155  return 0;
156 }
157 
158 int
159 stat_segment_connect (const char *socket_name)
160 {
162  return stat_segment_connect_r (socket_name, sm);
163 }
164 
165 void
167 {
168  munmap (sm->shared_header, sm->memory_size);
169  return;
170 }
171 
172 typedef struct
173 {
174  uint64_t epoch;
176 
177 static void
179  stat_client_main_t * sm)
180 {
182  sa->epoch = shared_header->epoch;
183  while (shared_header->in_progress != 0)
184  ;
186  sm->
187  shared_header->directory_offset);
188 }
189 
190 static bool
192 {
194 
195  if (shared_header->epoch != sa->epoch || shared_header->in_progress)
196  return false;
197  return true;
198 }
199 
200 void
202 {
204  return stat_segment_disconnect_r (sm);
205 }
206 
207 double
209 {
212 
213  /* Has directory been update? */
214  if (sm->shared_header->epoch != sm->current_epoch)
215  return 0;
216  stat_segment_access_start (&sa, sm);
218  if (!stat_segment_access_end (&sa, sm))
219  return 0.0;
220  return ep->value;
221 }
222 
223 double
225 {
227  return stat_segment_heartbeat_r (sm);
228 }
229 
230 static stat_segment_data_t
232 {
233  stat_segment_data_t result = { 0 };
234  int i;
235  vlib_counter_t **combined_c; /* Combined counter */
236  counter_t **simple_c; /* Simple counter */
237  uint64_t *offset_vector;
238 
239  assert (sm->shared_header);
240 
241  result.type = ep->type;
242  result.name = strdup (ep->name);
243  switch (ep->type)
244  {
246  result.scalar_value = ep->value;
247  break;
248 
250  if (ep->offset == 0)
251  return result;
252  simple_c = stat_segment_pointer (sm->shared_header, ep->offset);
253  result.simple_counter_vec = vec_dup (simple_c);
254  offset_vector =
256  for (i = 0; i < vec_len (simple_c); i++)
257  {
258  counter_t *cb =
259  stat_segment_pointer (sm->shared_header, offset_vector[i]);
260  result.simple_counter_vec[i] = vec_dup (cb);
261  }
262  break;
263 
265  if (ep->offset == 0)
266  return result;
267  combined_c = stat_segment_pointer (sm->shared_header, ep->offset);
268  result.combined_counter_vec = vec_dup (combined_c);
269  offset_vector =
271  for (i = 0; i < vec_len (combined_c); i++)
272  {
273  vlib_counter_t *cb =
274  stat_segment_pointer (sm->shared_header, offset_vector[i]);
275  result.combined_counter_vec[i] = vec_dup (cb);
276  }
277  break;
278 
280  /* Gather errors from all threads into a vector */
281  offset_vector = stat_segment_pointer (sm->shared_header,
283  vec_validate (result.error_vector, vec_len (offset_vector) - 1);
284  for (i = 0; i < vec_len (offset_vector); i++)
285  {
286  counter_t *cb =
287  stat_segment_pointer (sm->shared_header, offset_vector[i]);
288  result.error_vector[i] = cb[ep->index];
289  }
290  break;
291 
293  if (ep->offset == 0)
294  return result;
295  uint8_t **name_vector =
297  result.name_vector = vec_dup (name_vector);
298  offset_vector =
300  for (i = 0; i < vec_len (name_vector); i++)
301  {
302  if (offset_vector[i])
303  {
304  u8 *name =
305  stat_segment_pointer (sm->shared_header, offset_vector[i]);
306  result.name_vector[i] = vec_dup (name);
307  }
308  else
309  result.name_vector[i] = 0;
310  }
311  break;
312 
313  default:
314  fprintf (stderr, "Unknown type: %d\n", ep->type);
315  }
316  return result;
317 }
318 
319 void
321 {
322  int i, j;
323  for (i = 0; i < vec_len (res); i++)
324  {
325  switch (res[i].type)
326  {
328  for (j = 0; j < vec_len (res[i].simple_counter_vec); j++)
329  vec_free (res[i].simple_counter_vec[j]);
330  vec_free (res[i].simple_counter_vec);
331  break;
333  for (j = 0; j < vec_len (res[i].combined_counter_vec); j++)
334  vec_free (res[i].combined_counter_vec[j]);
335  vec_free (res[i].combined_counter_vec);
336  break;
337  default:
338  ;
339  }
340  free (res[i].name);
341  }
342  vec_free (res);
343 }
344 
345 uint32_t *
346 stat_segment_ls_r (uint8_t ** patterns, stat_client_main_t * sm)
347 {
349 
350  uint32_t *dir = 0;
351  regex_t regex[vec_len (patterns)];
352 
353  int i, j;
354  for (i = 0; i < vec_len (patterns); i++)
355  {
356  int rv = regcomp (&regex[i], (const char *) patterns[i], 0);
357  if (rv)
358  {
359  fprintf (stderr, "Could not compile regex %s\n", patterns[i]);
360  return dir;
361  }
362  }
363 
364  stat_segment_access_start (&sa, sm);
365 
367  for (j = 0; j < vec_len (counter_vec); j++)
368  {
369  for (i = 0; i < vec_len (patterns); i++)
370  {
371  int rv = regexec (&regex[i], counter_vec[j].name, 0, NULL, 0);
372  if (rv == 0)
373  {
374  vec_add1 (dir, j);
375  break;
376  }
377  }
378  if (vec_len (patterns) == 0)
379  vec_add1 (dir, j);
380  }
381 
382  for (i = 0; i < vec_len (patterns); i++)
383  regfree (&regex[i]);
384 
385  if (!stat_segment_access_end (&sa, sm))
386  {
387  /* Failed, clean up */
388  vec_free (dir);
389  return 0;
390 
391  }
392 
393  /* Update last version */
394  sm->current_epoch = sa.epoch;
395  return dir;
396 }
397 
398 uint32_t *
399 stat_segment_ls (uint8_t ** patterns)
400 {
402  return stat_segment_ls_r ((uint8_t **) patterns, sm);
403 }
404 
406 stat_segment_dump_r (uint32_t * stats, stat_client_main_t * sm)
407 {
408  int i;
410  stat_segment_data_t *res = 0;
412 
413  /* Has directory been update? */
414  if (sm->shared_header->epoch != sm->current_epoch)
415  return 0;
416 
417  stat_segment_access_start (&sa, sm);
418  for (i = 0; i < vec_len (stats); i++)
419  {
420  /* Collect counter */
421  ep = vec_elt_at_index (sm->directory_vector, stats[i]);
422  vec_add1 (res, copy_data (ep, sm));
423  }
424 
425  if (stat_segment_access_end (&sa, sm))
426  return res;
427 
428  fprintf (stderr, "Epoch changed while reading, invalid results\n");
429  // TODO increase counter
430  return 0;
431 }
432 
434 stat_segment_dump (uint32_t * stats)
435 {
437  return stat_segment_dump_r (stats, sm);
438 }
439 
440 /* Wrapper for accessing vectors from other languages */
441 int
443 {
444  return vec_len (vec);
445 }
446 
447 void
449 {
450  vec_free (vec);
451 }
452 
453 /* Create a vector from a string (or add to existing) */
454 uint8_t **
455 stat_segment_string_vector (uint8_t ** string_vector, const char *string)
456 {
457  uint8_t *name = 0;
458  size_t len = strlen (string);
459 
460  vec_validate_init_c_string (name, string, len);
461  vec_add1 (string_vector, name);
462  return string_vector;
463 }
464 
467 {
469  stat_segment_data_t *res = 0;
471 
472  stat_segment_access_start (&sa, sm);
473 
474  /* Collect counter */
475  ep = vec_elt_at_index (sm->directory_vector, index);
476  vec_add1 (res, copy_data (ep, sm));
477 
478  if (stat_segment_access_end (&sa, sm))
479  return res;
480  return 0;
481 }
482 
484 stat_segment_dump_entry (uint32_t index)
485 {
487  return stat_segment_dump_entry_r (index, sm);
488 }
489 
490 char *
492 {
496 
497  /* Has directory been update? */
498  if (sm->shared_header->epoch != sm->current_epoch)
499  return 0;
500  stat_segment_access_start (&sa, sm);
501  vec = get_stat_vector_r (sm);
502  ep = vec_elt_at_index (vec, index);
503  if (!stat_segment_access_end (&sa, sm))
504  return 0;
505  return strdup (ep->name);
506 }
507 
508 char *
510 {
512  return stat_segment_index_to_name_r (index, sm);
513 }
514 
515 uint64_t
517 {
518  ASSERT (sm->shared_header);
519  return sm->shared_header->version;
520 }
521 
522 uint64_t
524 {
526  return stat_segment_version_r (sm);
527 }
528 
529 /*
530  * fd.io coding-style-patch-verification: ON
531  *
532  * Local Variables:
533  * eval: (c-set-style "gnu")
534  * End:
535  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:439
int stat_segment_connect_r(const char *socket_name, stat_client_main_t *sm)
Definition: stat_client.c:101
Definition: stat_segment.h:62
int stat_segment_connect(const char *socket_name)
Definition: stat_client.c:159
stat_segment_data_t * stat_segment_dump_entry(uint32_t index)
Definition: stat_client.c:484
uint64_t index
Definition: stat_segment.h:67
stat_segment_directory_entry_t * directory_vector
Definition: stat_client.c:39
void stat_client_free(stat_client_main_t *sm)
Definition: stat_client.c:55
char * stat_segment_index_to_name_r(uint32_t index, stat_client_main_t *sm)
Definition: stat_client.c:491
void stat_segment_data_free(stat_segment_data_t *res)
Definition: stat_client.c:320
counter_t ** simple_counter_vec
Definition: stat_client.h:50
#define NULL
Definition: clib.h:58
stat_client_main_t stat_client_main
Definition: stat_client.c:43
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:522
Combined counter to hold both packets and byte differences.
Definition: counter_types.h:26
int i
clib_memset(h->entries, 0, sizeof(h->entries[0])*entries)
unsigned char u8
Definition: types.h:56
atomic_int_fast64_t in_progress
Definition: stat_segment.h:87
#define assert(x)
Definition: dlmalloc.c:30
stat_segment_shared_header_t * shared_header
Definition: stat_client.c:38
uint32_t * stat_segment_ls_r(uint8_t **patterns, stat_client_main_t *sm)
Definition: stat_client.c:346
uint64_t value
Definition: stat_segment.h:68
uint64_t current_epoch
Definition: stat_client.c:37
uint64_t counter_t
64bit counters
Definition: counter_types.h:22
#define vec_elt_at_index(v, i)
Get vector value at index i checking that i is in bounds.
uint8_t ** name_vector
Definition: stat_client.h:52
stat_segment_data_t * stat_segment_dump_entry_r(uint32_t index, stat_client_main_t *sm)
Definition: stat_client.c:466
double stat_segment_heartbeat_r(stat_client_main_t *sm)
Definition: stat_client.c:208
vl_api_fib_path_type_t type
Definition: fib_types.api:123
uword size
static void * stat_segment_pointer(void *start, uint64_t offset)
Definition: stat_segment.h:100
uint64_t offset
Definition: stat_segment.h:66
#define vec_dup(V)
Return copy of vector (no header, no alignment)
Definition: vec.h:375
stat_directory_type_t type
Definition: stat_client.h:45
stat_segment_data_t * stat_segment_dump_r(uint32_t *stats, stat_client_main_t *sm)
Definition: stat_client.c:406
u8 name[64]
Definition: memclnt.api:152
void stat_segment_disconnect(void)
Definition: stat_client.c:201
u8 len
Definition: ip_types.api:90
uint8_t ** stat_segment_string_vector(uint8_t **string_vector, const char *string)
Definition: stat_client.c:455
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:341
char * stat_segment_index_to_name(uint32_t index)
Definition: stat_client.c:509
uint64_t offset_vector
Definition: stat_segment.h:70
#define vec_validate_init_c_string(V, S, L)
Make a vector containing a NULL terminated c-string.
Definition: vec.h:991
char name[128]
Definition: stat_segment.h:71
uint32_t * stat_segment_ls(uint8_t **patterns)
Definition: stat_client.c:399
#define ASSERT(truth)
static stat_segment_directory_entry_t * get_stat_vector_r(stat_client_main_t *sm)
Definition: stat_client.c:93
stat_segment_data_t * stat_segment_dump(uint32_t *stats)
Definition: stat_client.c:434
static bool stat_segment_access_end(stat_segment_access_t *sa, stat_client_main_t *sm)
Definition: stat_client.c:191
atomic_int_fast64_t epoch
Definition: stat_segment.h:86
static int recv_fd(int sock)
Definition: stat_client.c:61
vlib_counter_t ** combined_counter_vec
Definition: stat_client.h:51
uint64_t stat_segment_version(void)
Definition: stat_client.c:523
stat_client_main_t * stat_client_get(void)
Definition: stat_client.c:46
void stat_segment_vec_free(void *vec)
Definition: stat_client.c:448
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
static void stat_segment_access_start(stat_segment_access_t *sa, stat_client_main_t *sm)
Definition: stat_client.c:178
int stat_segment_vec_len(void *vec)
Definition: stat_client.c:442
static stat_segment_data_t copy_data(stat_segment_directory_entry_t *ep, stat_client_main_t *sm)
Definition: stat_client.c:231
uint64_t stat_segment_version_r(stat_client_main_t *sm)
Definition: stat_client.c:516
counter_t * error_vector
Definition: stat_client.h:49
atomic_int_fast64_t error_offset
Definition: stat_segment.h:89
double stat_segment_heartbeat(void)
Definition: stat_client.c:224
void stat_segment_disconnect_r(stat_client_main_t *sm)
Definition: stat_client.c:166
CLIB vectors are ubiquitous dynamically resized arrays with by user defined "headers".
atomic_int_fast64_t directory_offset
Definition: stat_segment.h:88
stat_directory_type_t type
Definition: stat_segment.h:64