summaryrefslogtreecommitdiffstats
path: root/test/opentracing.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/opentracing.c')
-rw-r--r--test/opentracing.c771
1 files changed, 771 insertions, 0 deletions
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
+ */