summaryrefslogtreecommitdiffstats
path: root/test/test_stats_client.py
AgeCommit message (Expand)AuthorFilesLines
2021-06-25stats: revert "add a retry mechanism in a symlink test"Beno�t Ganne1-15/+1
2021-06-22stats: add a retry mechanism in a symlink testArthur de Kerhor1-1/+15
2021-05-04stats: adding symlinks for nodes and interfaces in the stat segmentArthur de Kerhor1-0/+96
2021-04-12tests: support attaching to existing vppKlement Sekera1-1/+1
2021-03-25stats: python vpp_stats rewrite to access stat segment directlyOle Troan1-2/+2
2021-02-11tests: tag the tests that do not work with multi-worker configurationAndrew Yourtchenko1-0/+2
2020-10-07api: add heap alloc to vpp statsOle Troan1-1/+0
2019-11-05misc: Fix python scripts shebang lineRenato Botelho do Couto1-1/+1
2019-10-28tests: switch test framework to python3 by defaultOle Troan1-1/+1
2019-06-18tests: fix checkstyle failure in test_stats_client.pySteven Luong1-1/+4
2019-06-18stats: fix memory leakage when adding / deleting interfacesOle Troan1-1/+18
2019-05-22stats: support multiple works for error countersOle Troan1-0/+6
2019-03-12VPP-1486: Unittest for stat segment file descriptor leak.Paul Vinciguerra1-0/+41
#n275'>275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
/*
 *------------------------------------------------------------------
 * 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 <vlib/vlib.h>
#include <vppinfra/socket.h>
#include <svm/ssvm.h>
#include <vpp/stats/stats.h>
#include <regex.h>
#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!\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;
}

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!\n", 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 (&regex[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(&regex[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 (&regex[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!\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]));
    }
  clib_spinlock_unlock (sm->stat_segment_lockp);

  return res;
}

/* Wrapper for accessing vectors from other languages */
int
stat_segment_vec_len (void *vec)
{
  return vec_len (vec);
}

/* Create a vector from a string (or add to existing) */
u8 **
stat_segment_string_vector (u8 ** string_vector, char *string)
{
  u8 *name = 0;
  name = vec_dup ((u8 *) string);
  vec_add1 (string_vector, (u8 *) name);
  return string_vector;
}

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */