summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--test/Makefile.am23
-rw-r--r--test/cfg-dd.json5
-rw-r--r--test/cfg-jaeger.yml34
-rw-r--r--test/cfg-zipkin.json4
-rw-r--r--test/debug.h58
-rw-r--r--test/define.h61
-rwxr-xr-xtest/get-opentracing-plugins.sh45
-rw-r--r--test/include.h67
-rw-r--r--test/opentracing.c771
-rw-r--r--test/opentracing.h42
-rw-r--r--test/test.c485
-rw-r--r--test/util.c118
-rw-r--r--test/util.h33
13 files changed, 1746 insertions, 0 deletions
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644
index 0000000..72b61e7
--- /dev/null
+++ b/test/Makefile.am
@@ -0,0 +1,23 @@
+## Process this file with automake to produce Makefile.in
+##
+ AM_CPPFLAGS = -I\$(top_builddir)/include -I$(srcdir)/../include @OPENTRACING_C_WRAPPER_CPPFLAGS@
+ AM_CFLAGS = @OPENTRACING_C_WRAPPER_CFLAGS@
+ AM_LDFLAGS = @OPENTRACING_C_WRAPPER_LDFLAGS@
+
+if WANT_DEBUG
+ bin_PROGRAMS = ot-c-wrapper-test_dbg
+ot_c_wrapper_test_dbg_SOURCES = opentracing.c test.c util.c
+ ot_c_wrapper_test_dbg_LDADD = -lstdc++ -lm @OPENTRACING_C_WRAPPER_LIBS@ $(top_builddir)/src/libopentracing-c-wrapper_dbg.la
+ot_c_wrapper_test_dbg_LDFLAGS = @OPENTRACING_C_WRAPPER_LDFLAGS@
+
+else
+
+ bin_PROGRAMS = ot-c-wrapper-test
+ot_c_wrapper_test_SOURCES = opentracing.c test.c util.c
+ ot_c_wrapper_test_LDADD = -lstdc++ -lm @OPENTRACING_C_WRAPPER_LIBS@ $(top_builddir)/src/libopentracing-c-wrapper.la
+ot_c_wrapper_test_LDFLAGS = @OPENTRACING_C_WRAPPER_LDFLAGS@
+endif
+
+CLEANFILES = a.out
+##
+## Makefile.am ends here
diff --git a/test/cfg-dd.json b/test/cfg-dd.json
new file mode 100644
index 0000000..5763d06
--- /dev/null
+++ b/test/cfg-dd.json
@@ -0,0 +1,5 @@
+{
+ "service": "opentracing-c-wrapper-test",
+ "agent_host": "localhost",
+ "agent_port": 8126
+}
diff --git a/test/cfg-jaeger.yml b/test/cfg-jaeger.yml
new file mode 100644
index 0000000..ca3904f
--- /dev/null
+++ b/test/cfg-jaeger.yml
@@ -0,0 +1,34 @@
+service_name:
+ opentracing-c-wrapper-test
+
+###
+# When using configuration object to instantiate the tracer, the type of
+# sampling can be selected via sampler.type and sampler.param properties.
+# Jaeger libraries support the following samplers:
+#
+# - Constant (sampler.type=const) sampler always makes the same decision for
+# all traces. It either samples all traces (sampler.param=1) or none of
+# them (sampler.param=0).
+#
+# - Probabilistic (sampler.type=probabilistic) sampler makes a random sampling
+# decision with the probability of sampling equal to the value of
+# sampler.param property. For example, with sampler.param=0.1 approximately
+# 1 in 10 traces will be sampled.
+#
+# - Rate Limiting (sampler.type=ratelimiting) sampler uses a leaky bucket rate
+# limiter to ensure that traces are sampled with a certain constant rate.
+# For example, when sampler.param=2.0 it will sample requests with the rate
+# of 2 traces per second.
+#
+# - Remote (sampler.type=remote, which is also the default) sampler consults
+# Jaeger agent for the appropriate sampling strategy to use in the current
+# service. This allows controlling the sampling strategies in the services
+# from a central configuration in Jaeger backend, or even dynamically.
+#
+sampler:
+ type: ratelimiting
+ param: 10.0
+
+reporter:
+ logSpans: true
+ localAgentHostPort: localhost:6831
diff --git a/test/cfg-zipkin.json b/test/cfg-zipkin.json
new file mode 100644
index 0000000..e25634f
--- /dev/null
+++ b/test/cfg-zipkin.json
@@ -0,0 +1,4 @@
+{
+ "service_name": "opentracing-c-wrapper-test",
+ "collector_host": "localhost"
+}
diff --git a/test/debug.h b/test/debug.h
new file mode 100644
index 0000000..c8d22b8
--- /dev/null
+++ b/test/debug.h
@@ -0,0 +1,58 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * 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.
+ */
+#ifndef TEST_DEBUG_H
+#define TEST_DEBUG_H
+
+#define OT_LOG(f,...) (void)printf("[%4d] " f "\n", thread_id(), ##__VA_ARGS__)
+
+#ifdef DEBUG
+enum DBG_LEVEL_enum {
+ DBG_LEVEL_FUNC = 0, /* Function debug level. */
+ DBG_LEVEL_INFO, /* Generic info level. */
+ DBG_LEVEL_DEBUG, /* Generic debug level. */
+ DBG_LEVEL_OT, /* OpenTracing debug level. */
+ DBG_LEVEL_WORKER, /* Worker debug level. */
+ DBG_LEVEL_ENABLED, /* This have to be the last entry. */
+};
+
+# define OT_FUNC(f,...) \
+ do { \
+ if (_nNULL(cfg_debug_level) && (*cfg_debug_level > 0)) \
+ OT_LOG("%s(" f ")", __func__, ##__VA_ARGS__); \
+ } while (0)
+# define OT_DBG(l,f,...) \
+ do { \
+ if (_nNULL(cfg_debug_level) && (*cfg_debug_level & (1 << DBG_LEVEL_##l))) \
+ OT_LOG(" " f, ##__VA_ARGS__); \
+ } while (0)
+
+
+extern uint8_t *cfg_debug_level;
+#else
+# define OT_FUNC(...) do { } while (0)
+# define OT_DBG(...) do { } while (0)
+#endif /* DEBUG */
+
+#endif /* TEST_DEBUG_H */
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/test/define.h b/test/define.h
new file mode 100644
index 0000000..68dcf60
--- /dev/null
+++ b/test/define.h
@@ -0,0 +1,61 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * 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.
+ */
+#ifndef TEST_DEFINE_H
+#define TEST_DEFINE_H
+
+#ifdef DEBUG
+# define OTC_DBG_MEM
+#endif
+
+#ifndef PACKAGE_BUILD
+# define PACKAGE_BUILD 0
+#endif
+
+#define OT_USE_INJECT_CB
+#define OT_USE_EXTRACT_CB
+
+#ifdef __linux__
+# define PRI_PIDT "d"
+# define PRI_PTHREADT "lu"
+#else
+# define PRI_PIDT "ld"
+# define PRI_PTHREADT "u"
+#endif
+
+#ifndef MIN
+# define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#define _NULL(a) ((a) == NULL)
+#define _nNULL(a) ((a) != NULL)
+#define TABLESIZE(a) ((int)(sizeof(a) / sizeof((a)[0])))
+#define IN_RANGE(v,a,b) (((v) >= (a)) && ((v) <= (b)))
+#define TIMEVAL_DIFF_MS(a,b) (((a)->tv_sec - (b)->tv_sec) * 1000ULL + ((a)->tv_usec - (b)->tv_usec + 500) / 1000)
+#define TIMEVAL_DIFF_US(a,b) (((a)->tv_sec - (b)->tv_sec) * 1000000ULL + (a)->tv_usec - (b)->tv_usec)
+#define NIBBLE_TO_HEX(a) ((a) + (((a) < 10) ? '0' : ('a' - 10)))
+#define SWAP(a,b) do { typeof(a) _a = (a); (a) = (b); (b) = _a; } while (0)
+#define OT_VARGS(t,v) otc_value_##t, (v)
+
+#endif /* TEST_DEFINE_H */
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/test/get-opentracing-plugins.sh b/test/get-opentracing-plugins.sh
new file mode 100755
index 0000000..f2fe2d6
--- /dev/null
+++ b/test/get-opentracing-plugins.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+#
+_ARG_DIR="${1:-.}"
+
+
+get ()
+{
+ local _arg_tracer="${1}"
+ local _arg_version="${2}"
+ local _arg_url="${3}"
+ local _arg_file="${4}"
+ local _var_tmpfile="_tmpfile_"
+ local _var_plugin="lib${_arg_tracer}_opentracing_plugin-${_arg_version}.so"
+
+ test -e "${_var_plugin}" && return 0
+
+ wget "https://github.com/${_arg_url}/releases/download/v${_arg_version}/${_arg_file}" -O "${_var_tmpfile}" || {
+ rm "${_var_tmpfile}"
+ return 1
+ }
+
+ case "$(file ${_var_tmpfile})" in
+ *shared\ object*)
+ mv "${_var_tmpfile}" "${_var_plugin}" ;;
+
+ *gzip\ compressed\ data*)
+ gzip -cd "${_var_tmpfile}" > "${_var_plugin}"
+ rm "${_var_tmpfile}" ;;
+ esac
+}
+
+
+mkdir -p "${_ARG_DIR}" && cd "${_ARG_DIR}" || exit 1
+
+get dd 1.1.2 DataDog/dd-opentracing-cpp linux-amd64-libdd_opentracing_plugin.so.gz
+get dd 1.2.0 DataDog/dd-opentracing-cpp linux-amd64-libdd_opentracing_plugin.so.gz
+
+get jaeger 0.4.2 jaegertracing/jaeger-client-cpp libjaegertracing_plugin.linux_amd64.so
+#et jaeger 0.5.0 jaegertracing/jaeger-client-cpp libjaegertracing_plugin.linux_amd64.so
+#et jaeger 0.6.0 jaegertracing/jaeger-client-cpp libjaegertracing_plugin.linux_amd64.so
+
+get lightstep 0.12.0 lightstep/lightstep-tracer-cpp linux-amd64-liblightstep_tracer_plugin.so.gz
+get lightstep 0.13.0 lightstep/lightstep-tracer-cpp linux-amd64-liblightstep_tracer_plugin.so.gz
+
+get zipkin 0.5.2 rnburn/zipkin-cpp-opentracing linux-amd64-libzipkin_opentracing_plugin.so.gz
diff --git a/test/include.h b/test/include.h
new file mode 100644
index 0000000..328ba72
--- /dev/null
+++ b/test/include.h
@@ -0,0 +1,67 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * 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.
+ */
+#ifndef TEST_INCLUDE_H
+#define TEST_INCLUDE_H
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <sysexits.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef __linux__
+# include <sys/syscall.h>
+#endif
+
+#include "config.h"
+#include "define.h"
+
+#include "opentracing-c-wrapper/define.h"
+#include "opentracing-c-wrapper/dbg_malloc.h"
+#include "opentracing-c-wrapper/common.h"
+#include "opentracing-c-wrapper/util.h"
+#include "opentracing-c-wrapper/value.h"
+#include "opentracing-c-wrapper/span.h"
+#include "opentracing-c-wrapper/propagation.h"
+#include "opentracing-c-wrapper/tracer.h"
+
+#include "version.h"
+#include "debug.h"
+#include "opentracing.h"
+#include "util.h"
+
+#endif /* TEST_INCLUDE_H */
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/test/opentracing.c b/test/opentracing.c
new file mode 100644
index 0000000..2e30fe5
--- /dev/null
+++ b/test/opentracing.c
@@ -0,0 +1,771 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * 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.h"
+
+
+static void ot_text_map_show(const struct otc_text_map *text_map)
+{
+ OT_FUNC("%p", text_map);
+
+ if (_NULL(text_map))
+ return;
+
+ OT_DBG(OT, "%p:{ %p %p %zu/%zu %hhu }", text_map, text_map->key, text_map->value, text_map->count, text_map->size, text_map->is_dynamic);
+
+ if (_nNULL(text_map->key) && _nNULL(text_map->value) && (text_map->count > 0)) {
+ size_t i;
+
+ for (i = 0; i < text_map->count; i++)
+ OT_DBG(OT, " \"%s\" -> \"%s\"", text_map->key[i], text_map->value[i]);
+ }
+}
+
+
+/***
+ * NAME
+ * ot_span_init -
+ *
+ * ARGUMENTS
+ * operation_name -
+ * ref_type -
+ * ref_ctx_idx -
+ * ref_span -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+struct otc_span *ot_span_init(struct otc_tracer *tracer, const char *operation_name, int ref_type, int ref_ctx_idx, const struct otc_span *ref_span)
+{
+ struct otc_start_span_options options;
+ struct otc_span_context context = { .idx = ref_ctx_idx, .span = ref_span };
+ struct otc_span_reference references = { ref_type, &context };
+ struct otc_span *retptr = NULL;
+
+ OT_FUNC("%p, \"%s\", %d, %d, %p", tracer, operation_name, ref_type, ref_ctx_idx, ref_span);
+
+ (void)memset(&options, 0, sizeof(options));
+
+ if (IN_RANGE(ref_type, otc_span_reference_child_of, otc_span_reference_follows_from)) {
+ options.references = &references;
+ options.num_references = 1;
+ }
+
+ retptr = tracer->start_span_with_options(tracer, operation_name, &options);
+ if (_NULL(retptr))
+ OT_DBG(OT, "cannot init new span");
+ else
+ OT_DBG(OT, "span %p:%zu initialized", retptr, retptr->idx);
+
+ return retptr;
+}
+
+
+/***
+ * NAME
+ * ot_span_tag -
+ *
+ * ARGUMENTS
+ * span -
+ * key -
+ * type -
+ * value -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+int ot_span_tag(struct otc_span *span, const char *key, int type, ...)
+{
+ va_list ap;
+ struct otc_value ot_value;
+ int retval = -1;
+
+ OT_FUNC("%p, \"%s\", %d, ...", span, key, type);
+
+ if (_NULL(span))
+ return retval;
+
+ va_start(ap, type);
+ for (retval = 0; _nNULL(key) && IN_RANGE(type, otc_value_bool, otc_value_null); retval++) {
+ ot_value.type = type;
+ if (type == otc_value_bool)
+ ot_value.value.bool_value = va_arg(ap, typeof(ot_value.value.bool_value));
+ else if (type == otc_value_double)
+ ot_value.value.double_value = va_arg(ap, typeof(ot_value.value.double_value));
+ else if (type == otc_value_int64)
+ ot_value.value.int64_value = va_arg(ap, typeof(ot_value.value.int64_value));
+ else if (type == otc_value_uint64)
+ ot_value.value.uint64_value = va_arg(ap, typeof(ot_value.value.uint64_value));
+ else if (type == otc_value_string)
+ ot_value.value.string_value = va_arg(ap, typeof(ot_value.value.string_value));
+ else if (type == otc_value_null)
+ ot_value.value.string_value = va_arg(ap, typeof(ot_value.value.string_value));
+ span->set_tag(span, key, &ot_value);
+
+ if (_nNULL(key = va_arg(ap, typeof(key))))
+ type = va_arg(ap, typeof(type));
+ }
+ va_end(ap);
+
+ return retval;
+}
+
+
+/***
+ * NAME
+ * ot_span_set_baggage -
+ *
+ * ARGUMENTS
+ * span -
+ * key -
+ * value -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+int ot_span_set_baggage(struct otc_span *span, const char *key, const char *value, ...)
+{
+ va_list ap;
+ int retval = -1;
+
+ OT_FUNC("%p, \"%s\", \"%s\", ...", span, key, value);
+
+ if (_NULL(span))
+ return retval;
+
+ va_start(ap, value);
+ for (retval = 0; _nNULL(key); retval++) {
+ OT_DBG(OT, "set baggage: \"%s\" \"%s\"", key, value);
+
+ span->set_baggage_item(span, key, value);
+
+ if (_nNULL(key = va_arg(ap, typeof(key))))
+ value = va_arg(ap, typeof(value));
+ }
+ va_end(ap);
+
+ return retval;
+}
+
+
+/***
+ * NAME
+ * ot_span_baggage -
+ *
+ * ARGUMENTS
+ * span -
+ * key -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+struct otc_text_map *ot_span_baggage(const struct otc_span *span, const char *key, ...)
+{
+ va_list ap;
+ struct otc_text_map *retptr = NULL;
+ int i, n;
+
+ OT_FUNC("%p, \"%s\", ...", span, key);
+
+ if (_NULL(span) || _NULL(key))
+ return retptr;
+
+ va_start(ap, key);
+ for (n = 1; _nNULL(va_arg(ap, typeof(key))); n++);
+ va_end(ap);
+
+ if (_NULL(retptr = otc_text_map_new(NULL, n)))
+ return retptr;
+
+ va_start(ap, key);
+ for (i = 0; (i < n) && _nNULL(key); i++) {
+ char *value;
+
+ if (_nNULL(value = (char *)span->baggage_item(span, key))) {
+ (void)otc_text_map_add(retptr, key, 0, value, 0, OTC_TEXT_MAP_DUP_KEY);
+
+ OT_DBG(OT, "get baggage[%d]: \"%s\" -> \"%s\"", i, retptr->key[i], retptr->value[i]);
+ } else {
+ OT_DBG(OT, "get baggage[%d]: \"%s\" -> invalid key", i, key);
+ }
+
+ key = va_arg(ap, typeof(key));
+ }
+ va_end(ap);
+
+ return retptr;
+}
+
+
+/***
+ * NAME
+ * ot_span_log_kv -
+ *
+ * ARGUMENTS
+ * span -
+ * key -
+ * value -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+int ot_span_log_kv(struct otc_span *span, const char *key, const char *value, ...)
+{
+ va_list ap;
+ struct otc_log_field log_data[OTC_MAXLOGFIELDS];
+ int retval = -1;
+
+ OT_FUNC("%p, \"%s\", \"%s\", ...", span, key, value);
+
+ if (_NULL(span) || _NULL(key) || _NULL(value))
+ return retval;
+
+ va_start(ap, value);
+ for (retval = 0; (retval < TABLESIZE(log_data)) && _nNULL(key); retval++) {
+ log_data[retval].key = key;
+ log_data[retval].value.type = otc_value_string;
+ log_data[retval].value.value.string_value = value;
+
+ if (_nNULL(key = va_arg(ap, typeof(key))))
+ value = va_arg(ap, typeof(value));
+ }
+ va_end(ap);
+
+ span->log_fields(span, log_data, retval);
+
+ return retval;
+}
+
+
+/***
+ * NAME
+ * ot_span_log -
+ *
+ * ARGUMENTS
+ * span -
+ * key -
+ * format -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+int ot_span_log(struct otc_span *span, const char *key, const char *format, ...)
+{
+ va_list ap;
+ char value[BUFSIZ];
+
+ OT_FUNC("%p, \"%s\", \"%s\", ...", span, key, format);
+
+ if (_NULL(span) || _NULL(key) || _NULL(format))
+ return -1;
+
+ va_start(ap, format);
+ (void)vsnprintf(value, sizeof(value), format, ap);
+ va_end(ap);
+
+ return ot_span_log_kv(span, key, value, NULL);
+}
+
+
+#ifdef OT_USE_INJECT_CB
+
+/***
+ * NAME
+ * ot_text_map_writer_set_cb -
+ *
+ * ARGUMENTS
+ * writer -
+ * key -
+ * value -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+static otc_propagation_error_code_t ot_text_map_writer_set_cb(struct otc_text_map_writer *writer, const char *key, const char *value)
+{
+ otc_propagation_error_code_t retval = otc_propagation_error_code_success;
+
+ OT_FUNC("%p, \"%s\", \"%s\"", writer, key, value);
+
+ if (otc_text_map_add(&(writer->text_map), key, 0, value, 0, OTC_TEXT_MAP_DUP_KEY | OTC_TEXT_MAP_DUP_VALUE) == -1)
+ retval = otc_propagation_error_code_unknown;
+
+ return retval;
+}
+
+#endif /* OT_USE_INJECT_CB */
+
+
+#ifdef OT_USE_EXTRACT_CB
+
+/***
+ * NAME
+ * ot_text_map_reader_foreach_key_cb -
+ *
+ * ARGUMENTS
+ * reader -
+ * handler -
+ * arg -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+static otc_propagation_error_code_t ot_text_map_reader_foreach_key_cb(struct otc_text_map_reader *reader, otc_propagation_error_code_t (*handler)(void *arg, const char *key, const char *value), void *arg)
+{
+ size_t i;
+ otc_propagation_error_code_t retval = otc_propagation_error_code_success;
+
+ OT_FUNC("%p, %p, %p", reader, handler, arg);
+
+ for (i = 0; (retval == otc_propagation_error_code_success) && (i < reader->text_map.count); i++) {
+ OT_DBG(OT, "\"%s\" -> \"%s\"", reader->text_map.key[i], reader->text_map.value[i]);
+
+ retval = handler(arg, reader->text_map.key[i], reader->text_map.value[i]);
+ }
+
+ return retval;
+}
+
+#endif /* OT_USE_EXTRACT_CB */
+
+
+/***
+ * NAME
+ * ot_inject_text_map -
+ *
+ * ARGUMENTS
+ * tracer -
+ * span -
+ * carrier -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+struct otc_span_context *ot_inject_text_map(struct otc_tracer *tracer, const struct otc_span *span, struct otc_text_map_writer *carrier)
+{
+ struct otc_span_context *retptr = NULL;
+ int rc;
+
+ OT_FUNC("%p, %p, %p", tracer, span, carrier);
+
+ if (_NULL(span))
+ return retptr;
+
+ if (_NULL(retptr = span->span_context((struct otc_span *)span)))
+ return retptr;
+
+ (void)memset(carrier, 0, sizeof(*carrier));
+#ifdef OT_USE_INJECT_CB
+ carrier->set = ot_text_map_writer_set_cb;
+#endif
+
+ rc = tracer->inject_text_map(tracer, carrier, retptr);
+ if (rc != otc_propagation_error_code_success) {
+ OT_LOG(" ERROR: inject_text_map() failed: %d", rc);
+
+ OTC_DBG_FREE(retptr);
+ } else {
+ OT_DBG(OT, "context %p: { %" PRId64 " %p %p }", retptr, retptr->idx, retptr->span, retptr->destroy);
+ }
+
+ OT_DBG(OT, "carrier %p: { { %p %p %zu/%zu %hhu } %p }", carrier, carrier->text_map.key, carrier->text_map.value, carrier->text_map.count, carrier->text_map.size, carrier->text_map.is_dynamic, carrier->set);
+ ot_text_map_show(&(carrier->text_map));
+
+ return retptr;
+}
+
+
+/***
+ * NAME
+ * ot_extract_text_map -
+ *
+ * ARGUMENTS
+ * tracer -
+ * carrier -
+ * text_map -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+struct otc_span_context *ot_extract_text_map(struct otc_tracer *tracer, struct otc_text_map_reader *carrier, const struct otc_text_map *text_map)
+{
+ struct otc_span_context *retptr = NULL;
+ int rc;
+
+ OT_FUNC("%p, %p, %p", tracer, carrier, text_map);
+
+ (void)memset(carrier, 0, sizeof(*carrier));
+#ifdef OT_USE_EXTRACT_CB
+ carrier->foreach_key = ot_text_map_reader_foreach_key_cb;
+#endif
+
+ if (_nNULL(text_map))
+ (void)memcpy(&(carrier->text_map), text_map, sizeof(carrier->text_map));
+
+ OT_DBG(OT, "carrier %p: { { %p %p %zu/%zu %hhu } }", carrier, carrier->text_map.key, carrier->text_map.value, carrier->text_map.count, carrier->text_map.size, carrier->text_map.is_dynamic);
+
+ rc = tracer->extract_text_map(tracer, carrier, &retptr);
+ if (rc != otc_propagation_error_code_success) {
+ OT_LOG(" ERROR: extract_text_map() failed: %d", rc);
+
+ OTC_DBG_FREE(retptr);
+ }
+ else if (_nNULL(retptr)) {
+ OT_DBG(OT, "context %p: { %" PRId64 " %p %p }", retptr, retptr->idx, retptr->span, retptr->destroy);
+ }
+
+ return retptr;
+}
+
+
+#ifdef OT_USE_INJECT_CB
+
+/***
+ * NAME
+ * ot_http_headers_writer_set_cb -
+ *
+ * ARGUMENTS
+ * writer -
+ * key -
+ * value -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+static otc_propagation_error_code_t ot_http_headers_writer_set_cb(struct otc_http_headers_writer *writer, const char *key, const char *value)
+{
+ otc_propagation_error_code_t retval = otc_propagation_error_code_success;
+
+ OT_FUNC("%p, \"%s\", \"%s\"", writer, key, value);
+
+ if (otc_text_map_add(&(writer->text_map), key, 0, value, 0, OTC_TEXT_MAP_DUP_KEY | OTC_TEXT_MAP_DUP_VALUE) == -1)
+ retval = otc_propagation_error_code_unknown;
+
+ return retval;
+}
+
+#endif /* OT_USE_INJECT_CB */
+
+
+#ifdef OT_USE_EXTRACT_CB
+
+/***
+ * NAME
+ * ot_http_headers_reader_foreach_key_cb -
+ *
+ * ARGUMENTS
+ * reader -
+ * handler -
+ * arg -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+static otc_propagation_error_code_t ot_http_headers_reader_foreach_key_cb(struct otc_http_headers_reader *reader, otc_propagation_error_code_t (*handler)(void *arg, const char *key, const char *value), void *arg)
+{
+ size_t i;
+ otc_propagation_error_code_t retval = otc_propagation_error_code_success;
+
+ OT_FUNC("%p, %p, %p", reader, handler, arg);
+
+ for (i = 0; (retval == otc_propagation_error_code_success) && (i < reader->text_map.count); i++) {
+ OT_DBG(OT, "\"%s\" -> \"%s\"", reader->text_map.key[i], reader->text_map.value[i]);
+
+ retval = handler(arg, reader->text_map.key[i], reader->text_map.value[i]);
+ }
+
+ return retval;
+}
+
+#endif /* OT_USE_EXTRACT_CB */
+
+
+/***
+ * NAME
+ * ot_inject_http_headers -
+ *
+ * ARGUMENTS
+ * tracer -
+ * span -
+ * carrier -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+struct otc_span_context *ot_inject_http_headers(struct otc_tracer *tracer, const struct otc_span *span, struct otc_http_headers_writer *carrier)
+{
+ struct otc_span_context *retptr = NULL;
+ int rc;
+
+ OT_FUNC("%p, %p, %p", tracer, span, carrier);
+
+ if (_NULL(span))
+ return retptr;
+
+ if (_NULL(retptr = span->span_context((struct otc_span *)span)))
+ return retptr;
+
+ (void)memset(carrier, 0, sizeof(*carrier));
+#ifdef OT_USE_INJECT_CB
+ carrier->set = ot_http_headers_writer_set_cb;
+#endif
+
+ rc = tracer->inject_http_headers(tracer, carrier, retptr);
+ if (rc != otc_propagation_error_code_success) {
+ OT_LOG(" ERROR: inject_http_headers() failed: %d", rc);
+
+ OTC_DBG_FREE(retptr);
+ } else {
+ OT_DBG(OT, "context %p: { %" PRId64 " %p %p }", retptr, retptr->idx, retptr->span, retptr->destroy);
+ }
+
+ OT_DBG(OT, "carrier %p: { { %p %p %zu/%zu %hhu } %p }", carrier, carrier->text_map.key, carrier->text_map.value, carrier->text_map.count, carrier->text_map.size, carrier->text_map.is_dynamic, carrier->set);
+ ot_text_map_show(&(carrier->text_map));
+
+ return retptr;
+}
+
+
+/***
+ * NAME
+ * ot_extract_http_headers -
+ *
+ * ARGUMENTS
+ * tracer -
+ * carrier -
+ * text_map -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+struct otc_span_context *ot_extract_http_headers(struct otc_tracer *tracer, struct otc_http_headers_reader *carrier, const struct otc_text_map *text_map)
+{
+ struct otc_span_context *retptr = NULL;
+ int rc;
+
+ OT_FUNC("%p, %p, %p", tracer, carrier, text_map);
+
+ (void)memset(carrier, 0, sizeof(*carrier));
+#ifdef OT_USE_EXTRACT_CB
+ carrier->foreach_key = ot_http_headers_reader_foreach_key_cb;
+#endif
+
+ if (_nNULL(text_map))
+ (void)memcpy(&(carrier->text_map), text_map, sizeof(carrier->text_map));
+
+ OT_DBG(OT, "carrier %p: { { %p %p %zu/%zu %hhu } }", carrier, carrier->text_map.key, carrier->text_map.value, carrier->text_map.count, carrier->text_map.size, carrier->text_map.is_dynamic);
+
+ rc = tracer->extract_http_headers(tracer, carrier, &retptr);
+ if (rc != otc_propagation_error_code_success) {
+ OT_LOG(" ERROR: extract_http_headers() failed: %d", rc);
+
+ OTC_DBG_FREE(retptr);
+ }
+ else if (_nNULL(retptr)) {
+ OT_DBG(OT, "context %p: { %" PRId64 " %p %p }", retptr, retptr->idx, retptr->span, retptr->destroy);
+ }
+
+ return retptr;
+}
+
+
+/***
+ * NAME
+ * ot_inject_binary -
+ *
+ * ARGUMENTS
+ * tracer -
+ * span -
+ * carrier -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+struct otc_span_context *ot_inject_binary(struct otc_tracer *tracer, const struct otc_span *span, struct otc_custom_carrier_writer *carrier)
+{
+ struct otc_span_context *retptr = NULL;
+ int rc;
+
+ OT_FUNC("%p, %p, %p", tracer, span, carrier);
+
+ if (_NULL(span))
+ return retptr;
+
+ if (_NULL(retptr = span->span_context((struct otc_span *)span)))
+ return retptr;
+
+ (void)memset(carrier, 0, sizeof(*carrier));
+
+ rc = tracer->inject_binary(tracer, carrier, retptr);
+ if (rc != otc_propagation_error_code_success) {
+ OT_LOG(" ERROR: inject_binary() failed: %d", rc);
+
+ OTC_DBG_FREE(retptr);
+ } else {
+ OT_DBG(OT, "context %p: { %" PRId64 " %p %p }", retptr, retptr->idx, retptr->span, retptr->destroy);
+ }
+
+ OT_DBG(OT, "carrier %p: { { %p %zu %hhu } %p }", carrier, carrier->binary_data.data, carrier->binary_data.size, carrier->binary_data.is_dynamic, carrier->inject);
+
+#ifdef DEBUG
+ if (carrier->binary_data.data != NULL) {
+ struct otc_jaeger_trace_context *ctx_jaeger = carrier->binary_data.data;
+ struct otc_dd_trace_context *ctx_dd = carrier->binary_data.data;
+
+ OT_DBG(OT, "Jaeger trace context: %016" PRIx64 "%016" PRIx64 ":%016" PRIx64 ":%016" PRIx64 ":%02hhx <%s> <%s>",
+ ctx_jaeger->trace_id[0], ctx_jaeger->trace_id[1], ctx_jaeger->span_id, ctx_jaeger->parent_span_id, ctx_jaeger->flags,
+ str_hex(ctx_jaeger->baggage, carrier->binary_data.size - sizeof(*ctx_jaeger)),
+ str_ctrl(ctx_jaeger->baggage, carrier->binary_data.size - sizeof(*ctx_jaeger)));
+ OT_DBG(OT, "DataDog trace context: <%s> <%s>",
+ str_hex(ctx_dd->data, carrier->binary_data.size),
+ str_ctrl(ctx_dd->data, carrier->binary_data.size));
+ }
+#endif /* DEBUG */
+
+ return retptr;
+}
+
+
+/***
+ * NAME
+ * ot_extract_binary -
+ *
+ * ARGUMENTS
+ * tracer -
+ * carrier -
+ * binary_data -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+struct otc_span_context *ot_extract_binary(struct otc_tracer *tracer, struct otc_custom_carrier_reader *carrier, const struct otc_binary_data *binary_data)
+{
+ struct otc_span_context *retptr = NULL;
+ int rc;
+
+ OT_FUNC("%p, %p, %p", tracer, carrier, binary_data);
+
+ (void)memset(carrier, 0, sizeof(*carrier));
+
+ if (_nNULL(binary_data) && _nNULL(binary_data->data) && (binary_data->size > 0))
+ (void)memcpy(&(carrier->binary_data), binary_data, sizeof(carrier->binary_data));
+
+ OT_DBG(OT, "carrier %p: { { %p %zu %hhu } %p }", carrier, carrier->binary_data.data, carrier->binary_data.size, carrier->binary_data.is_dynamic, carrier->extract);
+
+ rc = tracer->extract_binary(tracer, carrier, &retptr);
+ if (rc != otc_propagation_error_code_success) {
+ OT_LOG(" ERROR: extract_binary() failed: %d", rc);
+
+ OTC_DBG_FREE(retptr);
+ }
+ else if (_nNULL(retptr)) {
+ OT_DBG(OT, "context %p: { %" PRId64 " %p %p }", retptr, retptr->idx, retptr->span, retptr->destroy);
+ }
+
+ return retptr;
+}
+
+
+/***
+ * NAME
+ * ot_span_finish -
+ *
+ * ARGUMENTS
+ * span -
+ * ts_finish -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * This function does not return a value.
+ */
+void ot_span_finish(struct otc_span **span, const struct timespec *ts_finish)
+{
+ struct otc_finish_span_options options;
+
+ OT_FUNC("%p, %p", span, ts_finish);
+
+ if (_NULL(span) || _NULL(*span))
+ return;
+
+ (void)memset(&options, 0, sizeof(options));
+
+ if (_nNULL(ts_finish))
+ (void)memcpy(&(options.finish_time.value), ts_finish, sizeof(options.finish_time.value));
+
+ OT_DBG(OT, "span %p:%zu finished", *span, (*span)->idx);
+
+ (*span)->finish_with_options(*span, &options);
+
+ *span = NULL;
+}
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/test/opentracing.h b/test/opentracing.h
new file mode 100644
index 0000000..7cb4b99
--- /dev/null
+++ b/test/opentracing.h
@@ -0,0 +1,42 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * 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.
+ */
+#ifndef TEST_OPENTRACING_H
+#define TEST_OPENTRACING_H
+
+struct otc_span *ot_span_init(struct otc_tracer *tracer, const char *operation_name, int ref_type, int ref_ctx_idx, const struct otc_span *ref_span);
+int ot_span_tag(struct otc_span *span, const char *key, int type, ...);
+int ot_span_set_baggage(struct otc_span *span, const char *key, const char *value, ...);
+struct otc_text_map *ot_span_baggage(const struct otc_span *span, const char *key, ...);
+int ot_span_log_kv(struct otc_span *span, const char *key, const char *value, ...);
+int ot_span_log(struct otc_span *span, const char *key, const char *format, ...);
+struct otc_span_context *ot_inject_text_map(struct otc_tracer *tracer, const struct otc_span *span, struct otc_text_map_writer *carrier);
+struct otc_span_context *ot_extract_text_map(struct otc_tracer *tracer, struct otc_text_map_reader *carrier, const struct otc_text_map *text_map);
+struct otc_span_context *ot_inject_http_headers(struct otc_tracer *tracer, const struct otc_span *span, struct otc_http_headers_writer *carrier);
+struct otc_span_context *ot_extract_http_headers(struct otc_tracer *tracer, struct otc_http_headers_reader *carrier, const struct otc_text_map *text_map);
+struct otc_span_context *ot_inject_binary(struct otc_tracer *tracer, const struct otc_span *span, struct otc_custom_carrier_writer *carrier);
+struct otc_span_context *ot_extract_binary(struct otc_tracer *tracer, struct otc_custom_carrier_reader *carrier, const struct otc_binary_data *binary_data);
+void ot_span_finish(struct otc_span **span, const struct timespec *ts_finish);
+
+#endif /* TEST_OPENTRACING_H */
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/test/test.c b/test/test.c
new file mode 100644
index 0000000..287e4e2
--- /dev/null
+++ b/test/test.c
@@ -0,0 +1,485 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * 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.h"
+
+
+#define DEFAULT_DEBUG_LEVEL 0
+#define DEFAULT_THREADS_COUNT 1000
+
+
+typedef unsigned char bool_t;
+
+enum FLAG_OPT_enum {
+ FLAG_OPT_HELP = 0x01,
+ FLAG_OPT_VERSION = 0x02,
+};
+
+static struct {
+ uint8_t debug_level;
+ uint8_t opt_flags;
+ int runcount;
+ int runtime_ms;
+ int threads;
+ const char *ot_config;
+ const char *ot_plugin;
+ struct otc_tracer *ot_tracer;
+} cfg = {
+ .debug_level = DEFAULT_DEBUG_LEVEL,
+ .runtime_ms = -1,
+ .threads = DEFAULT_THREADS_COUNT,
+};
+
+enum OT_SPAN_enum {
+ OT_SPAN_ROOT = 0,
+ OP_SPAN_BAGGAGE,
+ OT_SPAN_PROP_TM,
+ OT_SPAN_PROP_HH,
+ OT_SPAN_PROP_BD,
+ OT_SPAN_MAX,
+};
+
+struct worker {
+ pthread_t thread;
+ int id;
+ struct otc_span *ot_span[OT_SPAN_MAX];
+ int ot_state;
+ uint64_t count;
+};
+
+static struct {
+ const char *name;
+ struct timeval start_time;
+ struct worker worker[8192];
+ volatile bool_t flag_run;
+} prg;
+
+
+uint8_t *cfg_debug_level = &(cfg.debug_level);
+
+
+/***
+ * NAME
+ * thread_id -
+ *
+ * ARGUMENTS
+ * This function takes no arguments.
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+int thread_id(void)
+{
+ pthread_t id;
+ int i;
+
+ id = pthread_self();
+
+ for (i = 0; i < MIN(cfg.threads, TABLESIZE(prg.worker)); i++)
+ if (pthread_equal(prg.worker[i].thread, id))
+ return i + 1;
+
+ return 0;
+}
+
+
+/***
+ * NAME
+ * worker_finish_all_spans -
+ *
+ * ARGUMENTS
+ * worker -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+static void worker_finish_all_spans(struct worker *worker)
+{
+ struct timespec ts;
+ int i;
+
+ OT_FUNC("%p", worker);
+
+ (void)clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ for (i = 0; i < TABLESIZE(worker->ot_span); i++)
+ if (_nNULL(worker->ot_span[i]))
+ ot_span_finish(worker->ot_span + i, &ts);
+}
+
+
+/***
+ * NAME
+ * worker_thread -
+ *
+ * ARGUMENTS
+ * data -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+__attribute__((noreturn)) static void *worker_thread(void *data)
+{
+ struct otc_text_map_writer tm_wr;
+ struct otc_http_headers_writer hh_wr;
+ struct otc_custom_carrier_writer cc_wr;
+ struct otc_span_context *context;
+ struct timeval now;
+ char name[16];
+ struct worker *worker = data;
+#ifdef DEBUG
+ int n = 0;
+#endif
+
+ OT_FUNC("%p", data);
+
+#ifdef __linux__
+ OT_DBG(WORKER, "Worker started, thread id: %" PRI_PTHREADT, syscall(SYS_gettid));
+#else
+ OT_DBG(WORKER, "Worker started, thread id: %" PRI_PTHREADT, worker->thread);
+#endif
+
+ (void)gettimeofday(&now, NULL);
+ (void)srandom(now.tv_usec);
+
+ (void)snprintf(name, sizeof(name), "test/wrk: %d", worker->id);
+ (void)pthread_setname_np(worker->thread, name);
+
+ while (!prg.flag_run) {
+ nsleep(0, 10000000);
+
+#ifdef DEBUG
+ n++;
+#endif
+ }
+
+ OT_DBG(DEBUG, "waiting loop count: %d", n);
+
+ for ( ; 1; worker->ot_state++) {
+ if (worker->ot_state != 0)
+ /* Do nothing. */;
+ else if ((cfg.runtime_ms > 0) && (TIMEVAL_DIFF_MS(&now, &(prg.start_time)) >= (uint)cfg.runtime_ms))
+ break;
+ else if ((cfg.runcount > 0) && ((uint)cfg.runcount <= worker->count))
+ break;
+
+ if (worker->ot_state == 0) {
+ worker->ot_span[OT_SPAN_ROOT] = ot_span_init(cfg.ot_tracer, "root span", -1, -1, NULL);
+ }
+ else if (worker->ot_state == 1) {
+ worker->ot_span[OP_SPAN_BAGGAGE] = ot_span_init(cfg.ot_tracer, "span #1", otc_span_reference_child_of, -1, worker->ot_span[OT_SPAN_ROOT]);
+ }
+ else if (worker->ot_state == 2) {
+ (void)ot_span_set_baggage(worker->ot_span[OP_SPAN_BAGGAGE], "baggage_1", "value_1", "baggage_2", "value_2", NULL);
+ }
+ else if (worker->ot_state == 3) {
+ if (_nNULL(context = ot_inject_text_map(cfg.ot_tracer, worker->ot_span[OP_SPAN_BAGGAGE], &tm_wr)))
+ context->destroy(&context);
+ }
+ else if (worker->ot_state == 4) {
+ struct otc_text_map_reader tm_rd;
+ struct otc_text_map *text_map = &(tm_wr.text_map);
+
+ if (_nNULL(context = ot_extract_text_map(cfg.ot_tracer, &tm_rd, text_map))) {
+ worker->ot_span[OT_SPAN_PROP_TM] = ot_span_init(cfg.ot_tracer, "text map propagation", otc_span_reference_child_of, context->idx, NULL);
+ context->destroy(&context);
+ }
+ otc_text_map_destroy(&text_map, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
+ }
+ else if (worker->ot_state == 5) {
+ struct otc_text_map *baggage = ot_span_baggage(worker->ot_span[OP_SPAN_BAGGAGE], "baggage_1", "baggage_2", NULL);
+
+ otc_text_map_destroy(&baggage, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
+ }
+ else if (worker->ot_state == 6) {
+ if (_nNULL(context = ot_inject_http_headers(cfg.ot_tracer, worker->ot_span[OP_SPAN_BAGGAGE], &hh_wr)))
+ context->destroy(&context);
+ }
+ else if (worker->ot_state == 7) {
+ struct otc_http_headers_reader hh_rd;
+ struct otc_text_map *text_map = &(hh_wr.text_map);
+
+ if (_nNULL(context = ot_extract_http_headers(cfg.ot_tracer, &hh_rd, text_map))) {
+ worker->ot_span[OT_SPAN_PROP_HH] = ot_span_init(cfg.ot_tracer, "http headers propagation", otc_span_reference_child_of, context->idx, NULL);
+ context->destroy(&context);
+ }
+ otc_text_map_destroy(&text_map, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
+ }
+ else if (worker->ot_state == 8) {
+ if (_nNULL(context = ot_inject_binary(cfg.ot_tracer, worker->ot_span[OP_SPAN_BAGGAGE], &cc_wr)))
+ context->destroy(&context);
+ }
+ else if (worker->ot_state == 9) {
+ struct otc_custom_carrier_reader cc_rd;
+ struct otc_binary_data *binary_data = &(cc_wr.binary_data);
+
+ if (_nNULL(context = ot_extract_binary(cfg.ot_tracer, &cc_rd, binary_data))) {
+ worker->ot_span[OT_SPAN_PROP_BD] = ot_span_init(cfg.ot_tracer, "binary data propagation", otc_span_reference_child_of, context->idx, NULL);
+ context->destroy(&context);
+ }
+ otc_binary_data_destroy(&binary_data);
+ }
+ else if (worker->ot_state == 10) {
+ (void)ot_span_tag(worker->ot_span[OT_SPAN_ROOT], "tag_1", OT_VARGS(string, "value_1"), "tag_2", OT_VARGS(string, "value_2"), NULL);
+ }
+ else if (worker->ot_state == 11) {
+ (void)ot_span_log_kv(worker->ot_span[OT_SPAN_PROP_TM], "log_1", "content_1", "log_2", "content_2", NULL);
+ }
+ else {
+ worker_finish_all_spans(worker);
+
+ worker->ot_state = -1;
+ worker->count++;
+ }
+
+ nsleep(0, ((random() % 100) + 1) * 10000);
+ (void)gettimeofday(&now, NULL);
+ }
+
+ pthread_exit(NULL);
+}
+
+
+/***
+ * NAME
+ * worker_run -
+ *
+ * ARGUMENTS
+ * This function takes no arguments.
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+static int worker_run(void)
+{
+ struct timeval now;
+ char ot_infbuf[BUFSIZ];
+ uint64_t total_count = 0;
+ int i, num_threads = 0, retval = EX_OK;
+
+ OT_FUNC("");
+
+ (void)pthread_setname_np(pthread_self(), "test/wrk: main");
+
+ for (i = 0; i < cfg.threads; i++) {
+ prg.worker[i].id = i + 1;
+
+ if (pthread_create(&(prg.worker[i].thread), NULL, worker_thread, prg.worker + i) != 0)
+ (void)fprintf(stderr, "ERROR: Failed to start thread for worker %d: %m\n", prg.worker[i].id);
+ else
+ num_threads++;
+ }
+
+ prg.flag_run = 1;
+
+ (void)gettimeofday(&now, NULL);
+ OT_DBG(WORKER, "%d threads started in %llu ms", num_threads, TIMEVAL_DIFF_MS(&now, &(prg.start_time)));
+
+ for (i = 0; i < cfg.threads; i++) {
+ if (pthread_join(prg.worker[i].thread, NULL) != 0)
+ (void)fprintf(stderr, "ERROR: Failed to join worker thread %d: %m\n", prg.worker[i].id);
+ else
+ OT_LOG("worker %d count: %" PRIu64, i, prg.worker[i].count);
+
+ total_count += prg.worker[i].count;
+ }
+
+ OT_LOG("%d worker(s) total count: %" PRIu64, cfg.threads, total_count);
+
+ cfg.ot_tracer->close(cfg.ot_tracer);
+
+ otc_statistics(ot_infbuf, sizeof(ot_infbuf));
+ OT_LOG("OpenTracing statistics: %s", ot_infbuf);
+
+ return retval;
+}
+
+
+/***
+ * NAME
+ * usage -
+ *
+ * ARGUMENTS
+ * program_name -
+ * flag_verbose -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * This function does not return a value.
+ */
+static void usage(const char *program_name, bool_t flag_verbose)
+{
+ (void)printf("\nUsage: %s { -h --help }\n", program_name);
+ (void)printf(" %s { -V --version }\n", program_name);
+ (void)printf(" %s { [ -R --runcount=VALUE ] | [ -r --runtime=TIME ] } [OPTION]...\n\n", program_name);
+
+ if (flag_verbose) {
+ (void)printf("Options are:\n");
+ (void)printf(" -c, --config=FILE Specify the configuration for the used tracer.\n");
+#ifdef DEBUG
+ (void)printf(" -d, --debug=LEVEL Enable and specify the debug mode level (default: %d).\n", DEFAULT_DEBUG_LEVEL);
+#endif
+ (void)printf(" -h, --help Show this text.\n");
+ (void)printf(" -p, --plugin=FILE Specify the OpenTracing compatible plugin library.\n");
+ (void)printf(" -R, --runcount=VALUE Execute this program a certain number of passes (0 = unlimited).\n");
+ (void)printf(" -r, --runtime=TIME Run this program for a certain amount of time (ms, 0 = unlimited).\n");
+ (void)printf(" -t, --threads=VALUE Specify the number of threads (default: %d).\n", DEFAULT_THREADS_COUNT);
+ (void)printf(" -V, --version Show program version.\n\n");
+ (void)printf("Copyright 2020 HAProxy Technologies\n");
+ (void)printf("SPDX-License-Identifier: Apache-2.0\n\n");
+ } else {
+ (void)printf("For help type: %s -h\n\n", program_name);
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ static const struct option longopts[] = {
+ { "config", required_argument, NULL, 'c' },
+#ifdef DEBUG
+ { "debug", required_argument, NULL, 'd' },
+#endif
+ { "help", no_argument, NULL, 'h' },
+ { "plugin", required_argument, NULL, 'p' },
+ { "runcount", required_argument, NULL, 'R' },
+ { "runtime", required_argument, NULL, 'r' },
+ { "threads", required_argument, NULL, 't' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+#ifdef OTC_DBG_MEM
+ static struct otc_dbg_mem_data dbg_mem_data[1000000];
+ struct otc_dbg_mem dbg_mem;
+#endif
+ const char *shortopts = "c:d:hp:R:r:t:V";
+ struct timeval now;
+ int c, longopts_idx = -1, retval = EX_OK;
+ bool_t flag_error = 0;
+ char ot_errbuf[BUFSIZ];
+
+ (void)gettimeofday(&(prg.start_time), NULL);
+
+ prg.name = basename(argv[0]);
+
+#ifdef OTC_DBG_MEM
+ retval = otc_dbg_mem_init(&dbg_mem, dbg_mem_data, TABLESIZE(dbg_mem_data), 0xff);
+ if (retval == -1) {
+ (void)fprintf(stderr, "ERROR: cannot initialize memory debugger\n");
+
+ return retval;
+ }
+#endif
+
+ while ((c = getopt_long(argc, argv, shortopts, longopts, &longopts_idx)) != EOF) {
+ if (c == 'c')
+ cfg.ot_config = optarg;
+#ifdef DEBUG
+ else if (c == 'd')
+ cfg.debug_level = atoi(optarg) & UINT8_C(0xff);
+#endif
+ else if (c == 'h')
+ cfg.opt_flags |= FLAG_OPT_HELP;
+ else if (c == 'p')
+ cfg.ot_plugin = optarg;
+ else if (c == 'R')
+ cfg.runcount = atoi(optarg);
+ else if (c == 'r')
+ cfg.runtime_ms = atoi(optarg);
+ else if (c == 't')
+ cfg.threads = atoi(optarg);
+ else if (c == 'V')
+ cfg.opt_flags |= FLAG_OPT_VERSION;
+ else
+ retval = EX_USAGE;
+ }
+
+ if (cfg.opt_flags & FLAG_OPT_HELP) {
+ usage(prg.name, 1);
+ }
+ else if (cfg.opt_flags & FLAG_OPT_VERSION) {
+ (void)printf("\n%s v%s [build %d] by %s, %s\n\n", prg.name, PACKAGE_VERSION, PACKAGE_BUILD, PACKAGE_AUTHOR, __DATE__);
+ }
+ else {
+ if ((cfg.runcount < 0) && (cfg.runtime_ms < 0)) {
+ (void)fprintf(stderr, "ERROR: run count/time value not set\n");
+ flag_error = 1;
+ }
+
+ if (!IN_RANGE(cfg.threads, 1, TABLESIZE(prg.worker))) {
+ (void)fprintf(stderr, "ERROR: invalid number of threads '%d'\n", cfg.threads);
+ flag_error = 1;
+ }
+
+ if (_NULL(cfg.ot_plugin) || _NULL(cfg.ot_config)) {
+ (void)fprintf(stderr, "ERROR: the OpenTracing configuration not set\n");
+ flag_error = 1;
+ }
+
+ if (flag_error)
+ usage(prg.name, 0);
+ }
+
+ cfg_debug_level = &(cfg.debug_level);
+
+ OT_FUNC("%d, %p", argc, argv);
+
+ if (flag_error || (cfg.opt_flags & (FLAG_OPT_HELP | FLAG_OPT_VERSION)))
+ return flag_error ? EX_USAGE : EX_OK;
+
+ if (_NULL(cfg.ot_tracer = otc_tracer_load(cfg.ot_plugin, ot_errbuf, sizeof(ot_errbuf)))) {
+ (void)fprintf(stderr, "ERROR: %s\n", (*ot_errbuf == '\0') ? "Unable to load tracing library" : ot_errbuf);
+
+ retval = EX_SOFTWARE;
+ }
+ else if (otc_tracer_start(cfg.ot_config, NULL, ot_errbuf, sizeof(ot_errbuf)) == -1) {
+ (void)fprintf(stderr, "ERROR: %s\n", (*ot_errbuf == '\0') ? "Unable to start tracing" : ot_errbuf);
+
+ retval = EX_SOFTWARE;
+ }
+ else {
+ retval = worker_run();
+ }
+
+ (void)gettimeofday(&now, NULL);
+ OT_DBG(INFO, "Program runtime: %llu ms", TIMEVAL_DIFF_MS(&now, &(prg.start_time)));
+
+ OTC_DBG_MEMINFO();
+
+ return retval;
+}
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/test/util.c b/test/util.c
new file mode 100644
index 0000000..91c1af7
--- /dev/null
+++ b/test/util.c
@@ -0,0 +1,118 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * 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.h"
+
+
+/***
+ * NAME
+ * nsleep -
+ *
+ * ARGUMENTS
+ * sec -
+ * nsec -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * This function does not return a value.
+ */
+void nsleep (uint sec, uint nsec)
+{
+ struct timespec ts1 = { sec, nsec }, ts2, *req = &ts1, *rem = &ts2;
+
+ while ((nanosleep (req, rem) == -1) && (errno == EINTR))
+ SWAP(req, rem);
+}
+
+
+/***
+ * NAME
+ * str_hex -
+ *
+ * ARGUMENTS
+ * data -
+ * size -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+const char *str_hex(const void *data, size_t size)
+{
+ static __thread char retbuf[BUFSIZ];
+ const uint8_t *ptr = data;
+ size_t i;
+
+ if (_NULL(data))
+ return "(null)";
+ else if (size == 0)
+ return "()";
+
+ for (i = 0, size <<= 1; (i < (sizeof(retbuf) - 2)) && (i < size); ptr++) {
+ retbuf[i++] = NIBBLE_TO_HEX(*ptr >> 4);
+ retbuf[i++] = NIBBLE_TO_HEX(*ptr & 0x0f);
+ }
+
+ retbuf[i] = '\0';
+
+ return retbuf;
+}
+
+
+/***
+ * NAME
+ * str_ctrl -
+ *
+ * ARGUMENTS
+ * data -
+ * size -
+ *
+ * DESCRIPTION
+ * -
+ *
+ * RETURN VALUE
+ * -
+ */
+const char *str_ctrl(const void *data, size_t size)
+{
+ static __thread char retbuf[BUFSIZ];
+ const uint8_t *ptr = data;
+ size_t i, n = 0;
+
+ if (_NULL(data))
+ return "(null)";
+ else if (size == 0)
+ return "()";
+
+ for (i = 0; (n < (sizeof(retbuf) - 1)) && (i < size); i++)
+ retbuf[n++] = IN_RANGE(ptr[i], 0x20, 0x7e) ? ptr[i] : '.';
+
+ retbuf[n] = '\0';
+
+ return retbuf;
+}
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/test/util.h b/test/util.h
new file mode 100644
index 0000000..689a042
--- /dev/null
+++ b/test/util.h
@@ -0,0 +1,33 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * 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.
+ */
+#ifndef TEST_UTIL_H
+#define TEST_UTIL_H
+
+int thread_id(void);
+void nsleep (uint sec, uint nsec);
+const char *str_hex(const void *data, size_t size);
+const char *str_ctrl(const void *data, size_t size);
+
+#endif /* TEST_UTIL_H */
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */