diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 52 | ||||
-rw-r--r-- | src/dbg_malloc.cpp | 629 | ||||
-rw-r--r-- | src/export.map | 18 | ||||
-rw-r--r-- | src/export_dbg.map | 29 | ||||
-rw-r--r-- | src/span.cpp | 547 | ||||
-rw-r--r-- | src/tracer.cpp | 874 | ||||
-rw-r--r-- | src/util.cpp | 451 |
7 files changed, 2600 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..bd72329 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,52 @@ +## Process this file with automake to produce Makefile.in +## +## +AM_CPPFLAGS = @OPENTRACING_C_WRAPPER_CPPFLAGS@ + AM_CFLAGS = @OPENTRACING_C_WRAPPER_CFLAGS@ +AM_CXXFLAGS = @OPENTRACING_C_WRAPPER_CXXFLAGS@ + AM_LDFLAGS = @OPENTRACING_C_WRAPPER_LDFLAGS@ + LIBS = @LIBS@ @OPENTRACING_C_WRAPPER_LIBS@ + +if WANT_DEBUG +lib_LTLIBRARIES = libopentracing-c-wrapper_dbg.la +pkgconfig_DATA = ../opentracing-c-wrapper_dbg.pc + +libopentracing_c_wrapper_dbg_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/../include +libopentracing_c_wrapper_dbg_la_CFLAGS = $(AM_CFLAGS) +libopentracing_c_wrapper_dbg_la_CXXFLAGS = $(AM_CXXFLAGS) +libopentracing_c_wrapper_dbg_la_LDFLAGS = $(AM_LDFLAGS) -version-info @LIB_VERSION@ -Wl,--version-script=$(srcdir)/export_dbg.map +libopentracing_c_wrapper_dbg_la_SOURCES = \ + dbg_malloc.cpp \ + span.cpp \ + tracer.cpp \ + util.cpp + +else + +lib_LTLIBRARIES = libopentracing-c-wrapper.la +pkgconfig_DATA = ../opentracing-c-wrapper.pc + +libopentracing_c_wrapper_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/../include +libopentracing_c_wrapper_la_CFLAGS = $(AM_CFLAGS) +libopentracing_c_wrapper_la_CXXFLAGS = $(AM_CXXFLAGS) +libopentracing_c_wrapper_la_LDFLAGS = $(AM_LDFLAGS) -version-info @LIB_VERSION@ -Wl,--version-script=$(srcdir)/export.map +libopentracing_c_wrapper_la_SOURCES = \ + span.cpp \ + tracer.cpp \ + util.cpp +endif + +pkginclude_HEADERS = \ + ../include/opentracing-c-wrapper/common.h \ + ../include/opentracing-c-wrapper/dbg_malloc.h \ + ../include/opentracing-c-wrapper/define.h \ + ../include/opentracing-c-wrapper/include.h \ + ../include/opentracing-c-wrapper/propagation.h \ + ../include/opentracing-c-wrapper/span.h \ + ../include/opentracing-c-wrapper/tracer.h \ + ../include/opentracing-c-wrapper/util.h \ + ../include/opentracing-c-wrapper/value.h + +CLEANFILES = a.out +## +## Makefile.am ends here diff --git a/src/dbg_malloc.cpp b/src/dbg_malloc.cpp new file mode 100644 index 0000000..18214b1 --- /dev/null +++ b/src/dbg_malloc.cpp @@ -0,0 +1,629 @@ +/*** + * 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 struct otc_dbg_mem *dbg_mem = nullptr; + + +/*** + * NAME + * otc_dbg_set_metadata - + * + * ARGUMENTS + * ptr - the real address of the allocated data + * data - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void otc_dbg_set_metadata(void *ptr, struct otc_dbg_mem_data *data) +{ + struct otc_dbg_mem_metadata *metadata; + + if (ptr == nullptr) + return; + + metadata = OT_CAST_TYPEOF(metadata, ptr); + metadata->data = (data == nullptr) ? OT_CAST_TYPEOF(data, metadata) : data; + metadata->magic = DBG_MEM_MAGIC; +} + + +/*** + * NAME + * otc_dbg_mem_add - + * + * ARGUMENTS + * func - + * line - + * ptr - the real address of the allocated data + * size - + * data - + * op_idx - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void otc_dbg_mem_add(const char *func, int line, void *ptr, size_t size, struct otc_dbg_mem_data *data, int op_idx) +{ + (void)snprintf(data->func, sizeof(data->func), "%s:%d", func, line); + + data->ptr = ptr; + data->size = size; + data->used = 1; + + dbg_mem->size += size; + dbg_mem->op_cnt[op_idx]++; + + otc_dbg_set_metadata(ptr, data); +} + + +/*** + * NAME + * otc_dbg_mem_alloc - + * + * ARGUMENTS + * func - + * line - + * old_ptr - the address of the data returned to the program + * ptr - the real address of the allocated data + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void otc_dbg_mem_alloc(const char *func, int line, void *old_ptr, void *ptr, size_t size) +{ + size_t i = 0; + int rc; + + if (dbg_mem == nullptr) { + return; + } + else if (ptr == nullptr) { + DBG_MEM_ERR("invalid memory address: %p", ptr); + + return; + } + + if ((rc = pthread_mutex_lock(&(dbg_mem->mutex))) != 0) { + DBG_MEM_ERR("cannot lock mutex: %s", otc_strerror(rc)); + + return; + } + + if (old_ptr != nullptr) { + /* Reallocating memory. */ + struct otc_dbg_mem_metadata *metadata = OT_CAST_TYPEOF(metadata, ptr); + + if (metadata == nullptr) { + DBG_MEM_ERR("no metadata: MEM_REALLOC %s:%d(%p -> %p %zu)", func, line, old_ptr, DBG_MEM_PTR(ptr), size); + } + else if (metadata->data == nullptr) { + DBG_MEM_ERR("invalid metadata: MEM_REALLOC %s:%d(%p -> %p %zu)", func, line, old_ptr, DBG_MEM_PTR(ptr), size); + } + else if (metadata->data == OT_CAST_TYPEOF(metadata->data, metadata)) { + DBG_MEM_ERR("unset metadata: MEM_REALLOC %s:%d(%p -> %p %zu)", func, line, old_ptr, DBG_MEM_PTR(ptr), size); + } + else if (metadata->magic != DBG_MEM_MAGIC) { + DBG_MEM_ERR("invalid magic: MEM_REALLOC %s:%d(%p -> %p %zu) 0x%016" PRIu64, func, line, old_ptr, DBG_MEM_PTR(ptr), size, metadata->magic); + } + else if (metadata->data->used && (metadata->data->ptr == DBG_MEM_DATA(old_ptr))) { + DBG_MEM_INFO(1, "MEM_REALLOC: %s:%d(%p %zu -> %p %zu)", func, line, old_ptr, metadata->data->size, DBG_MEM_PTR(ptr), size); + + dbg_mem->size -= metadata->data->size; + otc_dbg_mem_add(func, line, ptr, size, metadata->data, 1); + } + } else { + otc_dbg_set_metadata(ptr, nullptr); + + /* + * The first attempt is to find a location that has not been + * used at all so far. If such is not found, an attempt is + * made to find the first available location. + */ + if (dbg_mem->unused < dbg_mem->count) { + i = dbg_mem->unused++; + } else { + do { + if (dbg_mem->reused >= dbg_mem->count) + dbg_mem->reused = 0; + + if (!dbg_mem->data[dbg_mem->reused].used) { + i = dbg_mem->reused++; + + break; + } + + dbg_mem->reused++; + } while (++i <= dbg_mem->count); + } + + if (i < dbg_mem->count) { + DBG_MEM_INFO(1, "MEM_ALLOC: %s:%d(%p %zu %zu)", func, line, DBG_MEM_PTR(ptr), size, i); + + otc_dbg_mem_add(func, line, ptr, size, dbg_mem->data + i, 0); + } + } + + if ((rc = pthread_mutex_unlock(&(dbg_mem->mutex))) != 0) { + DBG_MEM_ERR("cannot unlock mutex: %s", otc_strerror(rc)); + + return; + } + + if (i >= dbg_mem->count) + DBG_MEM_ERR("alloc overflow: %s:%d(%p -> %p %zu)", func, line, old_ptr, DBG_MEM_PTR(ptr), size); +} + + +/*** + * NAME + * otc_dbg_mem_release - + * + * ARGUMENTS + * func - + * line - + * ptr - the address of the data returned to the program + * op_idx - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void otc_dbg_mem_release(const char *func, int line, void *ptr, int op_idx) +{ + struct otc_dbg_mem_metadata *metadata; + bool flag_invalid = 0; + size_t i; + int rc; + + if (dbg_mem == nullptr) { + return; + } + else if (ptr == nullptr) { + DBG_MEM_ERR("invalid memory address: %p", ptr); + + return; + } + + if ((rc = pthread_mutex_lock(&(dbg_mem->mutex))) != 0) { + DBG_MEM_ERR("cannot lock mutex: %s", otc_strerror(rc)); + + return; + } + + metadata = DBG_MEM_DATA(ptr); + if (metadata == nullptr) { + DBG_MEM_ERR("no metadata: MEM_%s %s:%d(%p)", (op_idx == 2) ? "FREE" : "RELEASE", func, line, ptr); + } + else if (metadata->data == nullptr) { + DBG_MEM_ERR("invalid metadata: MEM_%s %s:%d(%p)", (op_idx == 2) ? "FREE" : "RELEASE", func, line, ptr); + } + else if (metadata->data == OT_CAST_TYPEOF(metadata->data, metadata)) { + DBG_MEM_ERR("unset metadata: MEM_%s %s:%d(%p)", (op_idx == 2) ? "FREE" : "RELEASE", func, line, ptr); + } + else if (metadata->magic != DBG_MEM_MAGIC) { + DBG_MEM_ERR("invalid magic: MEM_%s %s:%d(%p) 0x%016" PRIu64, (op_idx == 2) ? "FREE" : "RELEASE", func, line, ptr, metadata->magic); + } + else if (metadata->data->used && (metadata->data->ptr == metadata)) { + DBG_MEM_INFO(1, "MEM_%s: %s:%d(%p %zu)", (op_idx == 2) ? "FREE" : "RELEASE", func, line, ptr, metadata->data->size); + + metadata->data->used = 0; + + dbg_mem->size -= metadata->data->size; + dbg_mem->op_cnt[op_idx]++; + } + else { + flag_invalid = 1; + } + + if ((rc = pthread_mutex_unlock(&(dbg_mem->mutex))) != 0) { + DBG_MEM_ERR("cannot unlock mutex: %s", otc_strerror(rc)); + + return; + } + + if (flag_invalid) { + DBG_MEM_ERR("invalid ptr: %s:%d(%p)", func, line, ptr); + + if (metadata != nullptr) + for (i = 0; i < dbg_mem->count; i++) + if (dbg_mem->data[i].ptr == metadata) + DBG_MEM_ERR("possible previous use: %s %hhu", dbg_mem->data[i].func, dbg_mem->data[i].used); + } +} + + +/*** + * NAME + * otc_dbg_malloc - + * + * ARGUMENTS + * func - + * line - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +void *otc_dbg_malloc(const char *func, int line, size_t size) +{ + void *retptr; + + retptr = malloc(DBG_MEM_SIZE(size)); + + otc_dbg_mem_alloc(func, line, nullptr, retptr, size); + + return DBG_MEM_RETURN(retptr); +} + + +/*** + * NAME + * otc_dbg_calloc - + * + * ARGUMENTS + * func - + * line - + * nelem - + * elsize - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +void *otc_dbg_calloc(const char *func, int line, size_t nelem, size_t elsize) +{ + void *retptr; + + retptr = malloc(DBG_MEM_SIZE(nelem * elsize)); + if (retptr != nullptr) + (void)memset(retptr, 0, DBG_MEM_SIZE(nelem * elsize)); + + otc_dbg_mem_alloc(func, line, nullptr, retptr, nelem * elsize); + + return DBG_MEM_RETURN(retptr); +} + + +/*** + * NAME + * otc_dbg_realloc - + * + * ARGUMENTS + * func - + * line - + * ptr - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +void *otc_dbg_realloc(const char *func, int line, void *ptr, size_t size) +{ + void *retptr; + + if (ptr == nullptr) { + retptr = malloc(DBG_MEM_SIZE(size)); + + otc_dbg_mem_alloc(func, line, nullptr, retptr, size); + } else { + struct otc_dbg_mem_metadata *metadata = DBG_MEM_DATA(ptr); + + /* + * If memory is not allocated via these debug functions, it + * must not be reallocated via them either. + */ + if ((metadata == nullptr) || (metadata->data == nullptr) || (metadata->magic != DBG_MEM_MAGIC)) { + retptr = realloc(ptr, size); + + return retptr; + } else { + retptr = realloc(DBG_MEM_DATA(ptr), DBG_MEM_SIZE(size)); + + otc_dbg_mem_alloc(func, line, ptr, retptr, size); + } + } + + return DBG_MEM_RETURN(retptr); +} + + +/*** + * NAME + * otc_dbg_free - + * + * ARGUMENTS + * func - + * line - + * ptr - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_dbg_free(const char *func, int line, void *ptr) +{ + struct otc_dbg_mem_metadata *metadata; + + otc_dbg_mem_release(func, line, ptr, 2); + + metadata = DBG_MEM_DATA(ptr); + if ((metadata == nullptr) || (metadata->data == nullptr) || (metadata->magic != DBG_MEM_MAGIC)) + free(ptr); + else + free(DBG_MEM_DATA(ptr)); +} + + +/*** + * NAME + * otc_dbg_strdup - + * + * ARGUMENTS + * func - + * line - + * s - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +char *otc_dbg_strdup(const char *func, int line, const char *s) +{ + size_t len = 0; + char *retptr = nullptr; + + if (s != nullptr) { + len = strlen(s) + 1; + retptr = OT_CAST_TYPEOF(retptr, malloc(DBG_MEM_SIZE(len))); + if (retptr != nullptr) + (void)memcpy(DBG_MEM_PTR(retptr), s, len); + } + + otc_dbg_mem_alloc(func, line, nullptr, retptr, len); + + return OT_CAST_TYPEOF(retptr, DBG_MEM_RETURN(retptr)); +} + + +/*** + * NAME + * otc_dbg_strndup - + * + * ARGUMENTS + * func - + * line - + * s - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +char *otc_dbg_strndup(const char *func, int line, const char *s, size_t size) +{ + size_t len = 0; + char *retptr = nullptr; + + if (s != nullptr) { + len = strlen(s); + len = (len < size) ? len : size; + retptr = OT_CAST_TYPEOF(retptr, malloc(DBG_MEM_SIZE(len + 1))); + if (retptr != nullptr) { + (void)memcpy(DBG_MEM_PTR(retptr), s, len); + DBG_MEM_PTR(retptr)[len] = '\0'; + } + } + + otc_dbg_mem_alloc(func, line, nullptr, retptr, len + 1); + + return OT_CAST_TYPEOF(retptr, DBG_MEM_RETURN(retptr)); +} + + +/*** + * NAME + * otc_dbg_mem_init - + * + * ARGUMENTS + * mem - + * data - + * count - + * level - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +int otc_dbg_mem_init(struct otc_dbg_mem *mem, struct otc_dbg_mem_data *data, size_t count, uint8_t level) +{ + pthread_mutexattr_t attr; + int rc, retval = -1; + + if ((mem == nullptr) || (data == nullptr) || (count == 0)) + return retval; + + (void)memset(mem, 0, sizeof(*mem)); + (void)memset(data, 0, sizeof(*data) * count); + + dbg_mem = mem; + dbg_mem->data = data; + dbg_mem->count = count; + dbg_mem->level = level; + + if ((rc = pthread_mutexattr_init(&attr)) == 0) + if ((rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) == 0) + if ((rc = pthread_mutex_init(&(dbg_mem->mutex), &attr)) == 0) + retval = 0; + + return retval; +} + + +/*** + * NAME + * otc_dbg_mem_disable - + * + * ARGUMENTS + * This function takes no arguments. + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_dbg_mem_disable(void) +{ + if (dbg_mem == nullptr) + return; + + (void)pthread_mutex_destroy(&(dbg_mem->mutex)); + + dbg_mem = nullptr; +} + + +/*** + * NAME + * otc_dbg_mem_info - + * + * ARGUMENTS + * This function takes no arguments. + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_dbg_mem_info(void) +{ +#ifdef HAVE_MALLINFO + struct mallinfo mi; +#endif + size_t i, n = 0; + uint64_t size = 0; + + if (dbg_mem == nullptr) + return; + + DBG_MEM_INFO(0, "--- Memory info -------------------------------------"); + DBG_MEM_INFO(0, " alloc/realloc: %" PRIu64 "/%" PRIu64 ", free/release: %" PRIu64 "/%" PRIu64, dbg_mem->op_cnt[0], dbg_mem->op_cnt[1], dbg_mem->op_cnt[2], dbg_mem->op_cnt[3]); + DBG_MEM_INFO(0, " unused: %zu, reused: %zu, count: %zu", dbg_mem->unused, dbg_mem->reused, dbg_mem->count); + for (i = 0; i < dbg_mem->count; i++) + if (dbg_mem->data[i].used) { + DBG_MEM_INFO(0, " %zu %s(%p %zu)", n, dbg_mem->data[i].func, dbg_mem->data[i].ptr, dbg_mem->data[i].size); + + size += dbg_mem->data[i].size; + n++; + } + + if (n > 0) + DBG_MEM_INFO(0, " allocated %zu byte(s) in %zu chunk(s)", size, n); + + if (dbg_mem->size != size) + DBG_MEM_INFO(0, " size does not match: %zu != %zu", dbg_mem->size, size); + +#ifdef HAVE_MALLINFO + mi = mallinfo(); + DBG_MEM_INFO(0, "--- Memory space usage ------------------------------"); + DBG_MEM_INFO(0, " Total non-mmapped bytes: %" PRI_MI, mi.arena); + DBG_MEM_INFO(0, " # of free chunks: %" PRI_MI, mi.ordblks); + DBG_MEM_INFO(0, " # of free fastbin blocks: %" PRI_MI, mi.smblks); + DBG_MEM_INFO(0, " Bytes in mapped regions: %" PRI_MI, mi.hblkhd); + DBG_MEM_INFO(0, " # of mapped regions: %" PRI_MI, mi.hblks); + DBG_MEM_INFO(0, " Max. total allocated space: %" PRI_MI, mi.usmblks); + DBG_MEM_INFO(0, " Free bytes held in fastbins: %" PRI_MI, mi.fsmblks); + DBG_MEM_INFO(0, " Total allocated space: %" PRI_MI, mi.uordblks); + DBG_MEM_INFO(0, " Total free space: %" PRI_MI, mi.fordblks); + DBG_MEM_INFO(0, " Topmost releasable block: %" PRI_MI, mi.keepcost); +#endif +} + + +/*** + * NAME + * otc_dbg_memdup - + * + * ARGUMENTS + * func - + * line - + * s - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +void *otc_dbg_memdup(const char *func, int line, const void *s, size_t size) +{ + void *retptr = nullptr; + + if (s != nullptr) { + retptr = malloc(DBG_MEM_SIZE(size + 1)); + if (retptr != nullptr) { + (void)memcpy(DBG_MEM_PTR(retptr), s, size); + DBG_MEM_PTR(retptr)[size] = '\0'; + } + } + + otc_dbg_mem_alloc(func, line, nullptr, retptr, size); + + return DBG_MEM_RETURN(retptr); +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/src/export.map b/src/export.map new file mode 100644 index 0000000..ab7c3a4 --- /dev/null +++ b/src/export.map @@ -0,0 +1,18 @@ +{ +global: + otc_tracer_init; + otc_tracer_load; + otc_tracer_start; + otc_tracer_global; + otc_tracer_init_global; + otc_text_map_new; + otc_text_map_add; + otc_text_map_destroy; + otc_binary_data_new; + otc_binary_data_destroy; + otc_ext_init; + otc_file_read; + otc_statistics; + +local: *; +}; diff --git a/src/export_dbg.map b/src/export_dbg.map new file mode 100644 index 0000000..4a99a3f --- /dev/null +++ b/src/export_dbg.map @@ -0,0 +1,29 @@ +{ +global: + otc_tracer_init; + otc_tracer_load; + otc_tracer_start; + otc_tracer_global; + otc_tracer_init_global; + otc_text_map_new; + otc_text_map_add; + otc_text_map_destroy; + otc_binary_data_new; + otc_binary_data_destroy; + otc_ext_init; + otc_file_read; + otc_statistics; + + otc_dbg_calloc; + otc_dbg_free; + otc_dbg_malloc; + otc_dbg_mem_disable; + otc_dbg_mem_info; + otc_dbg_mem_init; + otc_dbg_memdup; + otc_dbg_realloc; + otc_dbg_strdup; + otc_dbg_strndup; + +local: *; +}; diff --git a/src/span.cpp b/src/span.cpp new file mode 100644 index 0000000..3e9323d --- /dev/null +++ b/src/span.cpp @@ -0,0 +1,547 @@ +/*** + * 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" + + +#ifdef OT_THREADS_NO_LOCKING +thread_local Handle<opentracing::Span> ot_span_handle; +thread_local Handle<opentracing::SpanContext> ot_span_context_handle; +struct HandleData ot_span; +struct HandleData ot_span_context; +#else +struct Handle<opentracing::Span> ot_span; +struct Handle<opentracing::SpanContext> ot_span_context; +#endif /* OT_THREADS_NO_LOCKING */ + + +/*** + * NAME + * ot_span_finish_with_options - + * + * ARGUMENTS + * span - + * options - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_span_finish_with_options(struct otc_span *span, const struct otc_finish_span_options *options) +{ + OT_LOCK_GUARD(span); + + if (!OT_SPAN_IS_VALID(span)) + return; + + if (options == nullptr) { + ot_span_handle.at(span->idx)->Finish(); + } else { + struct opentracing::FinishSpanOptions span_options; + + if (options->finish_time.value.tv_sec > 0) { + auto dt = timespec_to_duration(&(options->finish_time.value)); + + span_options.finish_steady_timestamp = std::chrono::time_point<std::chrono::steady_clock>(dt); + } + + if (options->log_records != nullptr) { + for (int i = 0; i < options->num_log_records; i++) { + struct opentracing::LogRecord record; + + if (options->log_records[i].timestamp.value.tv_sec > 0) { +#ifdef __clang__ + auto dt = timespec_to_duration_us(&(options->log_records[i].timestamp.value)); +#else + auto dt = timespec_to_duration(&(options->log_records[i].timestamp.value)); +#endif + + record.timestamp = std::chrono::time_point<std::chrono::system_clock>(dt); + } + + for (int j = 0; j < options->log_records[i].num_fields; j++) + if (options->log_records[i].fields[j].value.type == otc_value_bool) { + record.fields.push_back(std::make_pair(options->log_records[i].fields[j].key, options->log_records[i].fields[j].value.value.bool_value)); + } + else if (options->log_records[i].fields[j].value.type == otc_value_double) { + record.fields.push_back(std::make_pair(options->log_records[i].fields[j].key, options->log_records[i].fields[j].value.value.double_value)); + } + else if (options->log_records[i].fields[j].value.type == otc_value_int64) { + record.fields.push_back(std::make_pair(options->log_records[i].fields[j].key, options->log_records[i].fields[j].value.value.int64_value)); + } + else if (options->log_records[i].fields[j].value.type == otc_value_uint64) { + record.fields.push_back(std::make_pair(options->log_records[i].fields[j].key, options->log_records[i].fields[j].value.value.uint64_value)); + } + else if (options->log_records[i].fields[j].value.type == otc_value_string) { + std::string str_value = options->log_records[i].fields[j].value.value.string_value; + + record.fields.push_back(std::make_pair(options->log_records[i].fields[j].key, str_value)); + } + else if (options->log_records[i].fields[j].value.type == otc_value_null) { + record.fields.push_back(std::make_pair(options->log_records[i].fields[j].key, nullptr)); + } + + span_options.log_records.push_back(record); + } + } + + ot_span_handle.at(span->idx)->FinishWithOptions(span_options); + } + + ot_nolock_span_destroy(&span); +} + + +/*** + * NAME + * ot_span_finish - + * + * ARGUMENTS + * span - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_span_finish(struct otc_span *span) +{ + ot_span_finish_with_options(span, nullptr); +} + + +/*** + * NAME + * ot_span_get_context - + * + * ARGUMENTS + * span - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static struct otc_span_context *ot_span_get_context(struct otc_span *span) +{ + OT_LOCK(span, span_context); + + if (!OT_SPAN_IS_VALID(span)) + return nullptr; + + return ot_span_context_new(span); +} + + +/*** + * NAME + * ot_span_set_operation_name - + * + * ARGUMENTS + * span - + * operation_name - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_span_set_operation_name(struct otc_span *span, const char *operation_name) +{ + OT_LOCK_GUARD(span); + + if (!OT_SPAN_IS_VALID(span) || (operation_name == nullptr)) + return; + + ot_span_handle.at(span->idx)->SetOperationName(operation_name); +} + + +/*** + * NAME + * ot_span_set_tag - + * + * ARGUMENTS + * span - + * key - + * value - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_span_set_tag(struct otc_span *span, const char *key, const struct otc_value *value) +{ + OT_LOCK_GUARD(span); + + if (!OT_SPAN_IS_VALID(span) || (key == nullptr) || (value == nullptr)) + return; + + if (value->type == otc_value_bool) { + ot_span_handle.at(span->idx)->SetTag(key, value->value.bool_value); + } + else if (value->type == otc_value_double) { + ot_span_handle.at(span->idx)->SetTag(key, value->value.double_value); + } + else if (value->type == otc_value_int64) { + ot_span_handle.at(span->idx)->SetTag(key, value->value.int64_value); + } + else if (value->type == otc_value_uint64) { + ot_span_handle.at(span->idx)->SetTag(key, value->value.uint64_value); + } + else if (value->type == otc_value_string) { + std::string str_value = value->value.string_value; + + ot_span_handle.at(span->idx)->SetTag(key, str_value); + } + else if (value->type == otc_value_null) { + ot_span_handle.at(span->idx)->SetTag(key, nullptr); + } + else { + /* Do nothing. */ + } +} + + +/*** + * NAME + * ot_span_log_fields - + * + * ARGUMENTS + * span - + * fields - + * num_fields - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_span_log_fields(struct otc_span *span, const struct otc_log_field *fields, int num_fields) +{ + OT_LOCK_GUARD(span); + std::string str_value[OTC_MAXLOGFIELDS]; + + if (!OT_SPAN_IS_VALID(span) || (fields == nullptr) || !OT_IN_RANGE(num_fields, 1, OTC_MAXLOGFIELDS)) + return; + + /* XXX The only data type supported in this function is string. */ + for (int i = 0; (i < num_fields) && (i < OTC_MAXLOGFIELDS); i++) { + if (fields[i].value.type != otc_value_string) + str_value[i] = "invalid data type"; + else if (fields[i].value.value.string_value != nullptr) + str_value[i] = fields[i].value.value.string_value; + else + str_value[i] = ""; + } + + if (num_fields == 1) + ot_span_handle.at(span->idx)->Log({ OT_LF(0) }); + else if (num_fields == 2) + ot_span_handle.at(span->idx)->Log({ OT_LF(0), OT_LF(1) }); + else if (num_fields == 3) + ot_span_handle.at(span->idx)->Log({ OT_LF(0), OT_LF(1), OT_LF(2) }); + else if (num_fields == 4) + ot_span_handle.at(span->idx)->Log({ OT_LF(0), OT_LF(1), OT_LF(2), OT_LF(3) }); + else if (num_fields == 5) + ot_span_handle.at(span->idx)->Log({ OT_LF(0), OT_LF(1), OT_LF(2), OT_LF(3), OT_LF(4) }); + else if (num_fields == 6) + ot_span_handle.at(span->idx)->Log({ OT_LF(0), OT_LF(1), OT_LF(2), OT_LF(3), OT_LF(4), OT_LF(5) }); + else if (num_fields == 7) + ot_span_handle.at(span->idx)->Log({ OT_LF(0), OT_LF(1), OT_LF(2), OT_LF(3), OT_LF(4), OT_LF(5), OT_LF(6) }); + else + ot_span_handle.at(span->idx)->Log({ OT_LF(0), OT_LF(1), OT_LF(2), OT_LF(3), OT_LF(4), OT_LF(5), OT_LF(6), OT_LF(7) }); +} + + +/*** + * NAME + * ot_span_set_baggage_item - + * + * ARGUMENTS + * span - + * key - + * value - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_span_set_baggage_item(struct otc_span *span, const char *key, const char *value) +{ + OT_LOCK_GUARD(span); + + if (!OT_SPAN_IS_VALID(span) || (key == nullptr) || (value == nullptr)) + return; + + ot_span_handle.at(span->idx)->SetBaggageItem(key, value); +} + + +/*** + * NAME + * ot_span_baggage_item - + * + * ARGUMENTS + * span - + * key - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static const char *ot_span_baggage_item(const struct otc_span *span, const char *key) +{ + OT_LOCK_GUARD(span); + const char *retptr = ""; + + if (!OT_SPAN_IS_VALID(span) || (key == nullptr)) + return retptr; + + auto baggage = ot_span_handle.at(span->idx)->BaggageItem(key); + if (!baggage.empty()) + retptr = OTC_DBG_STRDUP(baggage.c_str()); + + return retptr; +} + + +/*** + * NAME + * ot_span_tracer - + * + * ARGUMENTS + * span - NOT USED + * + * DESCRIPTION + * - NOT IMPLEMENTED + * + * RETURN VALUE + * - + */ +static struct otc_tracer *ot_span_tracer(const struct otc_span *span) +{ + struct otc_tracer *retptr = nullptr; + + if (!OT_SPAN_IS_VALID(span)) + return retptr; + + return retptr; +} + + +/*** + * NAME + * ot_nolock_span_destroy - + * + * ARGUMENTS + * span - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void ot_nolock_span_destroy(struct otc_span **span) +{ + if ((span == nullptr) || ((*span) == nullptr)) + return; + + if (OT_SPAN_KEY_IS_VALID(*span)) { + ot_span_handle.erase((*span)->idx); + ot_span.erase_cnt++; + } + + ot_span.destroy_cnt++; + + OT_EXT_FREE_CLEAR(*span); +} + + +/*** + * NAME + * ot_span_destroy - + * + * ARGUMENTS + * span - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_span_destroy(struct otc_span **span) +{ + OT_LOCK_GUARD(span); + + ot_nolock_span_destroy(span); +} + + +/*** + * NAME + * ot_span_new - + * + * ARGUMENTS + * This function takes no arguments. + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_span *ot_span_new(void) +{ + const static struct otc_span span_init = { + .idx = 0, + .finish = ot_span_finish, /* lock span */ + .finish_with_options = ot_span_finish_with_options, /* lock span */ + .span_context = ot_span_get_context, /* lock span and span_context */ + .set_operation_name = ot_span_set_operation_name, /* lock span */ + .set_tag = ot_span_set_tag, /* lock span */ + .log_fields = ot_span_log_fields, /* lock span */ + .set_baggage_item = ot_span_set_baggage_item, /* lock span */ + .baggage_item = ot_span_baggage_item, /* lock span */ + .tracer = ot_span_tracer, /* NOT IMPLEMENTED */ + .destroy = ot_span_destroy /* lock span */ + }; + int64_t idx = ot_span.key++; + struct otc_span *retptr; + + if (idx == 0) { + ot_span_handle.clear(); + ot_span_handle.reserve(8192); + } + + if ((retptr = OT_CAST_TYPEOF(retptr, OT_EXT_MALLOC(sizeof(*retptr)))) != nullptr) { + (void)memcpy(retptr, &span_init, sizeof(*retptr)); + retptr->idx = idx; + } else { + ot_span.alloc_fail_cnt++; + } + + return retptr; +} + + +/*** + * NAME + * ot_nolock_span_context_destroy - + * + * ARGUMENTS + * context - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_nolock_span_context_destroy(struct otc_span_context **context) +{ + if ((context == nullptr) || (*context == nullptr)) + return; + + if (OT_CTX_KEY_IS_VALID(*context)) { + ot_span_context_handle.erase((*context)->idx); + ot_span_context.erase_cnt++; + } + + ot_span_context.destroy_cnt++; + + OT_EXT_FREE_CLEAR(*context); +} + + +/*** + * NAME + * ot_span_context_destroy - + * + * ARGUMENTS + * context - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_span_context_destroy(struct otc_span_context **context) +{ + OT_LOCK_GUARD(span_context); + + ot_nolock_span_context_destroy(context); +} + + +/*** + * NAME + * ot_span_context_new - + * + * ARGUMENTS + * span - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_span_context *ot_span_context_new(const struct otc_span *span) +{ + int64_t idx = ot_span_context.key++; + struct otc_span_context *retptr; + + if (idx == 0) { + ot_span_context_handle.clear(); + ot_span_context_handle.reserve(8192); + } + + if ((retptr = OT_CAST_TYPEOF(retptr, OT_EXT_MALLOC(sizeof(*retptr)))) == nullptr) { + ot_span_context.alloc_fail_cnt++; + + return retptr; + } + + retptr->idx = (span == nullptr) ? idx : -1; + retptr->span = span; + retptr->destroy = ot_span_context_destroy; /* lock span_context */ + + return retptr; +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/src/tracer.cpp b/src/tracer.cpp new file mode 100644 index 0000000..80f56b0 --- /dev/null +++ b/src/tracer.cpp @@ -0,0 +1,874 @@ +/*** + * 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 std::unique_ptr<const opentracing::DynamicTracingLibraryHandle> ot_dynlib = nullptr; +static std::shared_ptr<opentracing::Tracer> ot_tracer = nullptr; + + +/*** + * NAME + * ot_tracer_load - + * + * ARGUMENTS + * library - + * errbuf - + * errbufsiz - + * handle - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static int ot_tracer_load(const char *library, char *errbuf, int errbufsiz, opentracing::DynamicTracingLibraryHandle &handle) +{ + std::string errmsg; + + /* Load the tracer library. */ + auto handle_maybe = opentracing::DynamicallyLoadTracingLibrary(library, errmsg); + if (!handle_maybe) { + (void)snprintf(errbuf, errbufsiz, "Failed to load tracing library: %s", errmsg.empty() ? handle_maybe.error().message().c_str() : errmsg.c_str()); + + return -1; + } + + handle = std::move(*handle_maybe); + + return 0; +} + + +/*** + * NAME + * ot_tracer_start - + * + * ARGUMENTS + * config - + * errbuf - + * errbufsiz - + * tracer - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static int ot_tracer_start(const char *config, char *errbuf, int errbufsiz, std::shared_ptr<opentracing::Tracer> &tracer) +{ + std::string errmsg; + + auto &tracer_factory = ot_dynlib->tracer_factory(); + + /* Create a tracer with the requested configuration. */ + auto tracer_maybe = tracer_factory.MakeTracer(config, errmsg); + if (!tracer_maybe) { + (void)snprintf(errbuf, errbufsiz, "Failed to construct tracer: %s", errmsg.empty() ? tracer_maybe.error().message().c_str() : errmsg.c_str()); + + return -1; + } + + tracer = std::move(*tracer_maybe); + + return 0; +} + + +/*** + * NAME + * ot_tracer_close - + * + * ARGUMENTS + * tracer - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_tracer_close(struct otc_tracer *tracer) +{ + if (tracer == nullptr) + return; + + if (ot_dynlib == nullptr) + return; + + if (ot_tracer != nullptr) + ot_tracer->Close(); + tracer->destroy(&tracer); +} + + +/*** + * NAME + * ot_tracer_start_span_with_options - + * + * ARGUMENTS + * tracer - NOT USED + * operation_name - + * options - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static struct otc_span *ot_tracer_start_span_with_options(struct otc_tracer *tracer, const char *operation_name, const struct otc_start_span_options *options) +{ + OT_LOCK_GUARD(span); + std::unique_ptr<opentracing::Span> span_maybe = nullptr; + struct otc_span *retptr = nullptr; + + if (ot_tracer == nullptr) + return retptr; + else if ((tracer == nullptr) || (operation_name == nullptr)) + return retptr; + + /* Allocating memory for the span. */ + if ((retptr = ot_span_new()) == nullptr) + return retptr; + + if (options == nullptr) { + span_maybe = ot_tracer->StartSpan(operation_name); + } else { + struct opentracing::StartSpanOptions span_options; + + if (options->start_time_steady.value.tv_sec > 0) { + auto dt = timespec_to_duration(&(options->start_time_steady.value)); + + span_options.start_steady_timestamp = std::chrono::time_point<std::chrono::steady_clock>(dt); + } + + if (options->start_time_system.value.tv_sec > 0) { +#ifdef __clang__ + auto dt = timespec_to_duration_us(&(options->start_time_system.value)); +#else + auto dt = timespec_to_duration(&(options->start_time_system.value)); +#endif + + span_options.start_system_timestamp = std::chrono::time_point<std::chrono::system_clock>(dt); + } + + if (options->references != nullptr) { + int lock_cnt = 0; + + for (int i = 0; i < options->num_references; i++) { + const opentracing::SpanContext *context = nullptr; + + if (OT_SPAN_IS_VALID(options->references[i].referenced_context->span)) { + context = &(ot_span_handle.at(options->references[i].referenced_context->span->idx)->context()); + } + else if (OT_CTX_KEY_IS_VALID(options->references[i].referenced_context)) { +#ifdef OT_THREADS_NO_LOCKING + lock_cnt++; +#else + if (lock_cnt++ == 0) + ot_span_context.mutex.lock(); +#endif + + context = ot_span_context_handle.at(options->references[i].referenced_context->idx).get(); + } + + if (options->references[i].type == otc_span_reference_child_of) + span_options.references.push_back(std::make_pair(opentracing::SpanReferenceType::ChildOfRef, context)); + else if (options->references[i].type == otc_span_reference_follows_from) + span_options.references.push_back(std::make_pair(opentracing::SpanReferenceType::FollowsFromRef, context)); + } + +#ifndef OT_THREADS_NO_LOCKING + if (lock_cnt > 0) + ot_span_context.mutex.unlock(); +#endif + } + + if (options->tags != nullptr) { + for (int i = 0; i < options->num_tags; i++) + if (options->tags[i].value.type == otc_value_bool) { + span_options.tags.push_back(std::make_pair(options->tags[i].key, options->tags[i].value.value.bool_value)); + } + else if (options->tags[i].value.type == otc_value_double) { + span_options.tags.push_back(std::make_pair(options->tags[i].key, options->tags[i].value.value.double_value)); + } + else if (options->tags[i].value.type == otc_value_int64) { + span_options.tags.push_back(std::make_pair(options->tags[i].key, options->tags[i].value.value.int64_value)); + } + else if (options->tags[i].value.type == otc_value_uint64) { + span_options.tags.push_back(std::make_pair(options->tags[i].key, options->tags[i].value.value.uint64_value)); + } + else if (options->tags[i].value.type == otc_value_string) { + std::string str_value = options->tags[i].value.value.string_value; + + span_options.tags.push_back(std::make_pair(options->tags[i].key, str_value)); + } + else if (options->tags[i].value.type == otc_value_null) { + span_options.tags.push_back(std::make_pair(options->tags[i].key, nullptr)); + } + } + + span_maybe = ot_tracer->StartSpanWithOptions(operation_name, span_options); + } + + if (span_maybe != nullptr) + ot_span_handle.emplace(retptr->idx, std::move(span_maybe)); + else + ot_nolock_span_destroy(&retptr); + + return retptr; +} + + +/*** + * NAME + * ot_tracer_start_span - + * + * ARGUMENTS + * tracer - NOT USED + * operation_name - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static struct otc_span *ot_tracer_start_span(struct otc_tracer *tracer, const char *operation_name) +{ + return ot_tracer_start_span_with_options(tracer, operation_name, nullptr); +} + + +/*** + * NAME + * ot_tracer_inject_text_map - + * + * ARGUMENTS + * tracer - NOT USED + * carrier - + * span_context - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_inject_text_map(struct otc_tracer *tracer, struct otc_text_map_writer *carrier, const struct otc_span_context *span_context) +{ + TextMap text_map; + TextMapCarrier text_map_carrier(text_map); + opentracing::expected<void> rc; + + if (ot_tracer == nullptr) + return otc_propagation_error_code_invalid_tracer; + else if ((tracer == nullptr) || (carrier == nullptr)) + return otc_propagation_error_code_invalid_carrier; + else if (!OT_CTX_IS_VALID(span_context)) + return otc_propagation_error_code_span_context_corrupted; + + if (OT_SPAN_IS_VALID(span_context->span)) { + OT_LOCK_GUARD(span); + + rc = ot_tracer->Inject(ot_span_handle.at(span_context->span->idx)->context(), text_map_carrier); + } + else if (OT_CTX_KEY_IS_VALID(span_context)) { + OT_LOCK_GUARD(span_context); + + rc = ot_tracer->Inject(*(ot_span_context_handle.at(span_context->idx)), text_map_carrier); + } + + if (!rc) + return otc_propagation_error_code_unknown; + else if (text_map.empty()) + return otc_propagation_error_code_unknown; + else if (otc_text_map_new(&(carrier->text_map), text_map.size()) == nullptr) + return otc_propagation_error_code_unknown; + + for (auto const &it : text_map) + if (carrier->set != nullptr) { + otc_propagation_error_code_t retval = carrier->set(carrier, it.first.c_str(), it.second.c_str()); + if (retval != otc_propagation_error_code_success) + return retval; + } + else if (otc_text_map_add(&(carrier->text_map), it.first.c_str(), 0, it.second.c_str(), 0, OT_CAST_STAT(otc_text_map_flags_t, OTC_TEXT_MAP_DUP_KEY | OTC_TEXT_MAP_DUP_VALUE)) == -1) + return otc_propagation_error_code_unknown; + + return otc_propagation_error_code_success; +} + + +/*** + * NAME + * ot_tracer_inject_http_headers - + * + * ARGUMENTS + * tracer - NOT USED + * carrier - + * span_context - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_inject_http_headers(struct otc_tracer *tracer, struct otc_http_headers_writer *carrier, const struct otc_span_context *span_context) +{ + TextMap text_map; + HTTPHeadersCarrier http_headers_carrier(text_map); + opentracing::expected<void> rc; + + if (ot_tracer == nullptr) + return otc_propagation_error_code_invalid_tracer; + else if ((tracer == nullptr) || (carrier == nullptr)) + return otc_propagation_error_code_invalid_carrier; + else if (!OT_CTX_IS_VALID(span_context)) + return otc_propagation_error_code_span_context_corrupted; + + if (OT_SPAN_IS_VALID(span_context->span)) { + OT_LOCK_GUARD(span); + + rc = ot_tracer->Inject(ot_span_handle.at(span_context->span->idx)->context(), http_headers_carrier); + } + else if (OT_CTX_KEY_IS_VALID(span_context)) { + OT_LOCK_GUARD(span_context); + + rc = ot_tracer->Inject(*(ot_span_context_handle.at(span_context->idx)), http_headers_carrier); + } + + if (!rc) + return otc_propagation_error_code_unknown; + else if (text_map.empty()) + return otc_propagation_error_code_unknown; + else if (otc_text_map_new(&(carrier->text_map), text_map.size()) == nullptr) + return otc_propagation_error_code_unknown; + + for (auto const &it : text_map) + if (carrier->set != nullptr) { + otc_propagation_error_code_t retval = carrier->set(carrier, it.first.c_str(), it.second.c_str()); + if (retval != otc_propagation_error_code_success) + return retval; + } + else if (otc_text_map_add(&(carrier->text_map), it.first.c_str(), 0, it.second.c_str(), 0, OT_CAST_STAT(otc_text_map_flags_t, OTC_TEXT_MAP_DUP_KEY | OTC_TEXT_MAP_DUP_VALUE)) == -1) + return otc_propagation_error_code_unknown; + + return otc_propagation_error_code_success; +} + + +/*** + * NAME + * ot_tracer_inject_binary - + * + * ARGUMENTS + * tracer - NOT USED + * carrier - + * span_context - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_inject_binary(struct otc_tracer *tracer, struct otc_custom_carrier_writer *carrier, const struct otc_span_context *span_context) +{ + std::ostringstream oss(std::ios::binary); + opentracing::expected<void> rc; + + if (ot_tracer == nullptr) + return otc_propagation_error_code_invalid_tracer; + else if ((tracer == nullptr) || (carrier == nullptr)) + return otc_propagation_error_code_invalid_carrier; + else if (!OT_CTX_IS_VALID(span_context)) + return otc_propagation_error_code_span_context_corrupted; + + if (OT_SPAN_IS_VALID(span_context->span)) { + OT_LOCK_GUARD(span); + + rc = ot_tracer->Inject(ot_span_handle.at(span_context->span->idx)->context(), oss); + } + else if (OT_CTX_KEY_IS_VALID(span_context)) { + OT_LOCK_GUARD(span_context); + + rc = ot_tracer->Inject(*(ot_span_context_handle.at(span_context->idx)), oss); + } + + if (rc) + if (otc_binary_data_new(&(carrier->binary_data), oss.str().c_str(), oss.str().size()) != nullptr) + return otc_propagation_error_code_success; + + return otc_propagation_error_code_unknown; +} + + +/*** + * NAME + * ot_tracer_inject_custom - + * + * ARGUMENTS + * tracer - NOT USED + * carrier - NOT USED + * span_context - NOT USED + * + * DESCRIPTION + * - NOT IMPLEMENTED + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_inject_custom(struct otc_tracer *tracer, struct otc_custom_carrier_writer *carrier, const struct otc_span_context *span_context) +{ + if ((tracer == nullptr) || (carrier == nullptr)) + return otc_propagation_error_code_invalid_carrier; + else if (!OT_CTX_IS_VALID(span_context)) + return otc_propagation_error_code_span_context_corrupted; + + return otc_propagation_error_code_success; +} + + +/*** + * NAME + * ot_span_context_add - + * + * ARGUMENTS + * span_context - + * span_context_maybe - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_span_context_add(struct otc_span_context **span_context, std::unique_ptr<opentracing::SpanContext> &span_context_maybe) +{ + OT_LOCK_GUARD(span_context); + + if ((*span_context = ot_span_context_new(nullptr)) == nullptr) { + span_context_maybe.reset(nullptr); + + return otc_propagation_error_code_unknown; + } + + ot_span_context_handle.emplace((*span_context)->idx, std::move(span_context_maybe)); + + return otc_propagation_error_code_success; +} + + +/*** + * NAME + * ot_tracer_text_map_add - + * + * ARGUMENTS + * arg - + * key - + * value - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_text_map_add(void *arg, const char *key, const char *value) +{ + TextMap *text_map = OT_CAST_REINTERPRET(TextMap *, arg); + + if ((arg == nullptr) || (key == nullptr) || (value == nullptr)) + return otc_propagation_error_code_unknown; + + text_map->emplace(key, value); + + return otc_propagation_error_code_success; +} + + +/*** + * NAME + * ot_tracer_extract_text_map - + * + * ARGUMENTS + * tracer - NOT USED + * carrier - + * span_context - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_extract_text_map(struct otc_tracer *tracer, const struct otc_text_map_reader *carrier, struct otc_span_context **span_context) +{ + TextMap text_map; + TextMapCarrier text_map_carrier(text_map); + + if (ot_tracer == nullptr) + return otc_propagation_error_code_invalid_tracer; + else if ((tracer == nullptr) || (carrier == nullptr)) + return otc_propagation_error_code_invalid_carrier; + else if (span_context == nullptr) + return otc_propagation_error_code_invalid_span_context; + + if (carrier->foreach_key != nullptr) { + otc_propagation_error_code_t rc = carrier->foreach_key(OT_CAST_CONST(struct otc_text_map_reader *, carrier), ot_tracer_text_map_add, &text_map); + if (rc != otc_propagation_error_code_success) + return rc; + } else { + for (size_t i = 0; i < carrier->text_map.count; i++) + text_map[carrier->text_map.key[i]] = carrier->text_map.value[i]; + } + + auto span_context_maybe = ot_tracer->Extract(text_map_carrier); + if (!span_context_maybe) + return otc_propagation_error_code_span_context_not_found; + + return ot_span_context_add(span_context, *span_context_maybe); +} + + +/*** + * NAME + * ot_tracer_extract_http_headers - + * + * ARGUMENTS + * tracer - NOT USED + * carrier - + * span_context - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_extract_http_headers(struct otc_tracer *tracer, const struct otc_http_headers_reader *carrier, struct otc_span_context **span_context) +{ + TextMap text_map; + HTTPHeadersCarrier http_headers_carrier(text_map); + + if (ot_tracer == nullptr) + return otc_propagation_error_code_invalid_tracer; + else if ((tracer == nullptr) || (carrier == nullptr)) + return otc_propagation_error_code_invalid_carrier; + else if (span_context == nullptr) + return otc_propagation_error_code_invalid_span_context; + + if (carrier->foreach_key != nullptr) { + otc_propagation_error_code_t rc = carrier->foreach_key(OT_CAST_CONST(struct otc_http_headers_reader *, carrier), ot_tracer_text_map_add, &text_map); + if (rc != otc_propagation_error_code_success) + return rc; + } else { + for (size_t i = 0; i < carrier->text_map.count; i++) + text_map[carrier->text_map.key[i]] = carrier->text_map.value[i]; + } + + auto span_context_maybe = ot_tracer->Extract(http_headers_carrier); + if (!span_context_maybe) + return otc_propagation_error_code_span_context_not_found; + + return ot_span_context_add(span_context, *span_context_maybe); +} + + +/*** + * NAME + * ot_tracer_extract_binary - + * + * ARGUMENTS + * tracer - NOT USED + * carrier - + * span_context - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_extract_binary(struct otc_tracer *tracer, const struct otc_custom_carrier_reader *carrier, struct otc_span_context **span_context) +{ + if (ot_tracer == nullptr) + return otc_propagation_error_code_invalid_tracer; + else if ((tracer == nullptr) || (carrier == nullptr)) + return otc_propagation_error_code_invalid_carrier; + else if (span_context == nullptr) + return otc_propagation_error_code_invalid_span_context; + + if ((carrier->binary_data.data == nullptr) || (carrier->binary_data.size == 0)) + return otc_propagation_error_code_invalid_carrier; + + std::string iss_data(OT_CAST_REINTERPRET(const char *, carrier->binary_data.data), carrier->binary_data.size); + std::istringstream iss(iss_data, std::ios::binary); + + auto span_context_maybe = ot_tracer->Extract(iss); + if (!span_context_maybe) + return otc_propagation_error_code_span_context_not_found; + + return ot_span_context_add(span_context, *span_context_maybe); +} + + +/*** + * NAME + * ot_tracer_extract_custom - + * + * ARGUMENTS + * tracer - NOT USED + * carrier - NOT USED + * span_context - NOT USED + * + * DESCRIPTION + * - NOT IMPLEMENTED + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_extract_custom(struct otc_tracer *tracer, const struct otc_custom_carrier_reader *carrier, struct otc_span_context **span_context) +{ + if ((tracer == nullptr) || (carrier == nullptr) || (span_context == nullptr)) + return otc_propagation_error_code_unknown; + + return otc_propagation_error_code_success; +} + + +/*** + * NAME + * ot_tracer_destroy - + * + * ARGUMENTS + * tracer - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_tracer_destroy(struct otc_tracer **tracer) +{ + if ((tracer == nullptr) || (*tracer == nullptr)) + return; + + OT_FREE_CLEAR(*tracer); +} + + +/*** + * NAME + * ot_tracer_new - + * + * ARGUMENTS + * This function takes no arguments. + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_tracer *ot_tracer_new(void) +{ + const static struct otc_tracer tracer_init = { + .close = ot_tracer_close, /* lock not required */ + .start_span = ot_tracer_start_span, /* lock span */ + .start_span_with_options = ot_tracer_start_span_with_options, /* lock span */ + .inject_text_map = ot_tracer_inject_text_map, /* lock span and/or span_context */ + .inject_http_headers = ot_tracer_inject_http_headers, /* lock span and/or span_context */ + .inject_binary = ot_tracer_inject_binary, /* lock span and/or span_context */ + .inject_custom = ot_tracer_inject_custom, /* NOT IMPLEMENTED */ + .extract_text_map = ot_tracer_extract_text_map, /* lock span_context */ + .extract_http_headers = ot_tracer_extract_http_headers, /* lock span_context */ + .extract_binary = ot_tracer_extract_binary, /* lock span_context */ + .extract_custom = ot_tracer_extract_custom, /* NOT IMPLEMENTED */ + .destroy = ot_tracer_destroy /* lock not required */ + }; + struct otc_tracer *retptr; + + if ((retptr = OT_CAST_TYPEOF(retptr, OTC_DBG_CALLOC(1, sizeof(*retptr)))) != nullptr) + (void)memcpy(retptr, &tracer_init, sizeof(*retptr)); + + return retptr; +} + + +/*** + * NAME + * otc_tracer_load - + * + * ARGUMENTS + * library - + * errbuf - + * errbufsiz - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_tracer *otc_tracer_load(const char *library, char *errbuf, int errbufsiz) +{ + std::unique_ptr<opentracing::DynamicTracingLibraryHandle> handle { + new opentracing::DynamicTracingLibraryHandle {} + }; + std::shared_ptr<opentracing::Tracer> tracer; + struct otc_tracer *retptr = nullptr; + + if ((retptr = ot_tracer_new()) == nullptr) { + /* Do nothing. */; + } + else if (ot_tracer_load(library, errbuf, errbufsiz, *handle) == -1) { + retptr->destroy(&retptr); + } + else { + ot_dynlib = std::move(handle); + } + + return retptr; +} + + +/*** + * NAME + * otc_tracer_start - + * + * ARGUMENTS + * cfgfile - + * cfgbuf - + * errbuf - + * errbufsiz - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +int otc_tracer_start(const char *cfgfile, const char *cfgbuf, char *errbuf, int errbufsiz) +{ + std::shared_ptr<opentracing::Tracer> tracer; + char *config = OT_CAST_CONST(char *, cfgbuf); + int retval = -1; + + if (cfgfile != nullptr) { + config = otc_file_read(cfgfile, "#", errbuf, errbufsiz); + if (config == nullptr) + return retval; + } + + if (ot_tracer_start(config, errbuf, errbufsiz, tracer) == -1) { + /* Do nothing. */; + } else { + ot_tracer = std::move(tracer); + + (void)opentracing::Tracer::InitGlobal(ot_tracer); + + retval = 0; + } + + if (config != cfgbuf) + OT_FREE(config); + + return retval; +} + + +/*** + * NAME + * otc_tracer_init - + * + * ARGUMENTS + * library - + * cfgfile - + * cfgbuf - + * errbuf - + * errbufsiz - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_tracer *otc_tracer_init(const char *library, const char *cfgfile, const char *cfgbuf, char *errbuf, int errbufsiz) +{ + struct otc_tracer *retptr = nullptr; + + if ((retptr = otc_tracer_load(library, errbuf, errbufsiz)) != nullptr) + if (otc_tracer_start(cfgfile, cfgbuf, errbuf, errbufsiz) == -1) + retptr->destroy(&retptr); + + return retptr; +} + + +/*** + * NAME + * otc_tracer_global - + * + * ARGUMENTS + * tracer - NOT USED + * + * DESCRIPTION + * - NOT IMPLEMENTED + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_tracer_global(struct otc_tracer *tracer) +{ + if (tracer == nullptr) + return; +} + + +/*** + * NAME + * otc_tracer_init_global - + * + * ARGUMENTS + * tracer - NOT USED + * + * DESCRIPTION + * - NOT IMPLEMENTED + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_tracer_init_global(struct otc_tracer *tracer) +{ + if (tracer == nullptr) + return; +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 0000000..3236e9d --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,451 @@ +/*** + * 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" + + +otc_ext_malloc_t otc_ext_malloc = OT_IFDEF_DBG(otc_dbg_malloc, malloc); +otc_ext_free_t otc_ext_free = OT_IFDEF_DBG(otc_dbg_free, free); + + +/*** + * NAME + * timespec_to_duration_us - + * + * ARGUMENTS + * ts - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +std::chrono::microseconds timespec_to_duration_us(const struct timespec *ts) +{ + auto duration = std::chrono::seconds{ts->tv_sec} + std::chrono::microseconds{ts->tv_nsec / 1000}; + + return std::chrono::duration_cast<std::chrono::microseconds>(duration); +} + + +/*** + * NAME + * timespec_to_duration - + * + * ARGUMENTS + * ts - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +std::chrono::nanoseconds timespec_to_duration(const struct timespec *ts) +{ + auto duration = std::chrono::seconds{ts->tv_sec} + std::chrono::nanoseconds{ts->tv_nsec}; + + return std::chrono::duration_cast<std::chrono::nanoseconds>(duration); +} + + +/*** + * NAME + * otc_ext_init - + * + * ARGUMENTS + * func_malloc - + * func_free - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_ext_init(otc_ext_malloc_t func_malloc, otc_ext_free_t func_free) +{ + otc_ext_malloc = (func_malloc != nullptr) ? func_malloc : OT_IFDEF_DBG(otc_dbg_malloc, malloc); + otc_ext_free = (func_free != nullptr) ? func_free : OT_IFDEF_DBG(otc_dbg_free, free); +} + + +/*** + * NAME + * otc_text_map_new - + * + * ARGUMENTS + * text_map - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_text_map *otc_text_map_new(struct otc_text_map *text_map, size_t size) +{ + struct otc_text_map *retptr = text_map; + + if (retptr == nullptr) + retptr = OT_CAST_TYPEOF(retptr, OTC_DBG_CALLOC(1, sizeof(*retptr))); + + if (retptr != nullptr) { + retptr->count = 0; + retptr->size = size; + retptr->is_dynamic = text_map == nullptr; + + if (size == 0) + /* Do nothing. */; + else if ((retptr->key = OT_CAST_TYPEOF(retptr->key, OTC_DBG_CALLOC(size, sizeof(*(retptr->key))))) == nullptr) + otc_text_map_destroy(&retptr, OT_CAST_STAT(otc_text_map_flags_t, 0)); + else if ((retptr->value = OT_CAST_TYPEOF(retptr->value, OTC_DBG_CALLOC(size, sizeof(*(retptr->value))))) == nullptr) + otc_text_map_destroy(&retptr, OT_CAST_STAT(otc_text_map_flags_t, 0)); + } + + return retptr; +} + + +/*** + * NAME + * otc_text_map_add - + * + * ARGUMENTS + * text_map - + * key - + * key_len - + * value - + * value_len - + * flags - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +int otc_text_map_add(struct otc_text_map *text_map, const char *key, size_t key_len, const char *value, size_t value_len, otc_text_map_flags_t flags) +{ + int retval = -1; + + if ((text_map == nullptr) || (key == nullptr) || (value == nullptr)) + return retval; + + /* + * Check if it is necessary to increase the number of key/value pairs. + * The number of pairs is increased by half the current number of pairs + * (for example: 8 -> 12 -> 18 -> 27 -> 40 -> 60 ...). + */ + if (text_map->count >= text_map->size) { + typeof(text_map->key) ptr_key; + typeof(text_map->value) ptr_value; + size_t size_add = (text_map->size > 1) ? (text_map->size / 2) : 1; + + if ((ptr_key = OT_CAST_TYPEOF(ptr_key, OTC_DBG_REALLOC(text_map->key, OT_TEXT_MAP_SIZE(key, size_add)))) == nullptr) + return retval; + + text_map->key = ptr_key; + (void)memset(text_map->key + OT_TEXT_MAP_SIZE(key, 0), 0, sizeof(*(text_map->key)) * size_add); + + if ((ptr_value = OT_CAST_TYPEOF(ptr_value, OTC_DBG_REALLOC(text_map->value, OT_TEXT_MAP_SIZE(value, size_add)))) == nullptr) + return retval; + + text_map->value = ptr_value; + (void)memset(text_map->value + OT_TEXT_MAP_SIZE(value, 0), 0, sizeof(*(text_map->value)) * size_add); + + text_map->size += size_add; + } + + text_map->key[text_map->count] = (flags & OTC_TEXT_MAP_DUP_KEY) ? ((key_len > 0) ? OTC_DBG_STRNDUP(key, key_len) : OTC_DBG_STRDUP(key)) : OT_CAST_CONST(char *, key); + text_map->value[text_map->count] = (flags & OTC_TEXT_MAP_DUP_VALUE) ? ((value_len > 0) ? OTC_DBG_STRNDUP(value, value_len) : OTC_DBG_STRDUP(value)) : OT_CAST_CONST(char *, value); + + if ((text_map->key[text_map->count] != nullptr) && (text_map->value[text_map->count] != nullptr)) + retval = text_map->count; + + text_map->count++; + + return retval; +} + + +/*** + * NAME + * otc_text_map_destroy - + * + * ARGUMENTS + * text_map - + * flags - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_text_map_destroy(struct otc_text_map **text_map, otc_text_map_flags_t flags) +{ + if ((text_map == nullptr) || (*text_map == nullptr)) + return; + + if ((*text_map)->key != nullptr) { + if (flags & OTC_TEXT_MAP_FREE_KEY) + for (size_t i = 0; i < (*text_map)->count; i++) + OT_FREE((*text_map)->key[i]); + + OT_FREE_CLEAR((*text_map)->key); + } + + if ((*text_map)->value != nullptr) { + if (flags & OTC_TEXT_MAP_FREE_VALUE) + for (size_t i = 0; i < (*text_map)->count; i++) + OT_FREE((*text_map)->value[i]); + + OT_FREE_CLEAR((*text_map)->value); + } + + if ((*text_map)->is_dynamic) { + OT_FREE_CLEAR(*text_map); + } else { + (*text_map)->count = 0; + (*text_map)->size = 0; + } +} + + +/*** + * NAME + * otc_binary_data_new - + * + * ARGUMENTS + * binary_data - + * data - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_binary_data *otc_binary_data_new(struct otc_binary_data *binary_data, const void *data, size_t size) +{ + struct otc_binary_data *retptr = binary_data; + + if (retptr == nullptr) + retptr = OT_CAST_TYPEOF(retptr, OTC_DBG_CALLOC(1, sizeof(*retptr))); + + if (retptr != nullptr) { + retptr->size = size; + retptr->is_dynamic = binary_data == nullptr; + + if ((data == nullptr) || (size == 0)) + /* Do nothing. */; + else if ((retptr->data = OTC_DBG_MALLOC(size)) != nullptr) + (void)memcpy(retptr->data, data, size); + else + otc_binary_data_destroy(&retptr); + } + + return retptr; +} + + +/*** + * NAME + * otc_binary_data_destroy - + * + * ARGUMENTS + * binary_data - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_binary_data_destroy(struct otc_binary_data **binary_data) +{ + if ((binary_data == nullptr) || (*binary_data == nullptr)) + return; + + OT_FREE_CLEAR((*binary_data)->data); + + if ((*binary_data)->is_dynamic) + OT_FREE_CLEAR(*binary_data); + else + (*binary_data)->size = 0; +} + + +/*** + * NAME + * otc_strerror - + * + * ARGUMENTS + * errnum - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +const char *otc_strerror(int errnum) +{ + static __THR char retbuf[1024]; + const char *retptr = retbuf; + + errno = 0; + (void)strerror_r(errnum, retbuf, sizeof(retbuf)); + if (errno != 0) + retptr = "Unknown error"; + + return retptr; +} + + +/*** + * NAME + * otc_file_read - + * + * ARGUMENTS + * filename - + * comment - + * errbuf - + * errbufsiz - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +char *otc_file_read(const char *filename, const char *comment, char *errbuf, int errbufsiz) +{ + struct stat statbuf; + char *retptr = nullptr; + int fd, rc; + + if (filename == nullptr) + return retptr; + + if ((fd = open(filename, O_RDONLY)) == -1) { + (void)snprintf(errbuf, errbufsiz, "'%s': %s", filename, otc_strerror(errno)); + } + else if ((rc = fstat(fd, &statbuf)) == -1) { + (void)snprintf(errbuf, errbufsiz, "'%s': %s", filename, otc_strerror(errno)); + } + else if ((retptr = OT_CAST_TYPEOF(retptr, OTC_DBG_MALLOC(statbuf.st_size + 1))) == nullptr) { + (void)snprintf(errbuf, errbufsiz, "cannot allocate memory: %s", otc_strerror(errno)); + } + else { + char *buf = retptr; + off_t size = statbuf.st_size; + + while (size > 0) { + ssize_t n; + + if ((n = read(fd, buf, size)) > 0) { + size -= n; + buf += n; + } + else if (n == -1) { + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { + break; + } + else if (errno != EINTR) + { + (void)snprintf(errbuf, errbufsiz, "'%s': %s", filename, otc_strerror(errno)); + OT_FREE_CLEAR(retptr); + + break; + } + } + else { + break; + } + } + + if (comment != nullptr) { + off_t i = 0, c = -1, n = statbuf.st_size; + + for (i = 0; i < n; i++) + if (c < 0) { + /* Remember the starting position of the comment line. */ + if ((strchr(comment, retptr[i]) != nullptr) && ((i == 0) || (retptr[i - 1] == '\n'))) + c = i; + } + else if ((retptr[i] == '\n') && ((i + 1) < n)) { + /* Delete the entire comment line. */ + (void)memmove(retptr + c, retptr + i + 1, n - i - 1); + + n -= i + 1 - c; + i = c - 1; + c = -1; + } + + /* If a comment remains in the last line, delete it. */ + if (c >= 0) { + n -= i - c; + i = c; + } + + retptr[i] = '\0'; + } + else if (size != 0) + OT_FREE_CLEAR(retptr); + } + + (void)close(fd); + + return retptr; +} + + +/*** + * NAME + * otc_statistics - + * + * ARGUMENTS + * buffer - + * bufsiz - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_statistics(char *buffer, size_t bufsiz) +{ + if ((buffer == nullptr) || (bufsiz < 24)) + return; + + (void)snprintf(buffer, bufsiz, "span: %" PRId64 "/%" PRId64 "+%" PRId64 "(%" PRId64 ")/%" PRId64 ", context: %" PRId64 "/%" PRId64 "+%" PRId64 "(%" PRId64 ")/%" PRId64, + ot_span.key, ot_span_handle.size(), ot_span.erase_cnt, ot_span.destroy_cnt, ot_span.alloc_fail_cnt, + ot_span_context.key, ot_span_context_handle.size(), ot_span_context.erase_cnt, ot_span_context.destroy_cnt, ot_span_context.alloc_fail_cnt); +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ |