addons/otel/src/event.o \
addons/otel/src/filter.o \
addons/otel/src/parser.o \
+ addons/otel/src/pool.o \
+ addons/otel/src/scope.o \
addons/otel/src/util.o
OTEL_CFLAGS := $(OTEL_CFLAGS) -Iaddons/otel/include $(OTEL_DEFINE)
#ifndef _OTEL_CONFIG_H_
#define _OTEL_CONFIG_H_
+/* Memory pool selection flags. */
+#define USE_POOL_BUFFER
+#define USE_POOL_OTEL_SPAN_CONTEXT
+#define USE_POOL_OTEL_SCOPE_SPAN
+#define USE_POOL_OTEL_SCOPE_CONTEXT
+#define USE_POOL_OTEL_RUNTIME_CONTEXT
+#define USE_TRASH_CHUNK
+
#define FLT_OTEL_ID_MAXLEN 64 /* Maximum identifier length. */
#define FLT_OTEL_DEBUG_LEVEL 0b11101111111 /* Default debug bitmask. */
#include "conf_funcs.h"
#include "filter.h"
#include "parser.h"
+#include "pool.h"
+#include "scope.h"
#include "util.h"
#endif /* _OTEL_INCLUDE_H_ */
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#ifndef _OTEL_POOL_H_
+#define _OTEL_POOL_H_
+
+#define FLT_OTEL_POOL_INIT(p,n,s,r) \
+ do { \
+ if (((r) == FLT_OTEL_RET_OK) && ((p) == NULL)) { \
+ (p) = create_pool(n, (s), MEM_F_SHARED); \
+ if ((p) == NULL) \
+ (r) = FLT_OTEL_RET_ERROR; \
+ \
+ OTELC_DBG(DEBUG, #p " %p %u", (p), FLT_OTEL_DEREF((p), size, 0)); \
+ } \
+ } while (0)
+
+#define FLT_OTEL_POOL_DESTROY(p) \
+ do { \
+ if ((p) != NULL) { \
+ OTELC_DBG(DEBUG, #p " %p %u", (p), (p)->size); \
+ \
+ pool_destroy(p); \
+ (p) = NULL; \
+ } \
+ } while (0)
+
+
+extern struct pool_head *pool_head_otel_scope_span __read_mostly;
+extern struct pool_head *pool_head_otel_scope_context __read_mostly;
+extern struct pool_head *pool_head_otel_runtime_context __read_mostly;
+extern struct pool_head *pool_head_otel_span_context __read_mostly;
+
+
+/* Allocate memory from a pool with optional zeroing. */
+void *flt_otel_pool_alloc(struct pool_head *pool, size_t size, bool flag_clear, char **err);
+
+/* Duplicate a string into pool-allocated memory. */
+void *flt_otel_pool_strndup(struct pool_head *pool, const char *s, size_t size, char **err);
+
+/* Release pool-allocated memory and clear the pointer. */
+void flt_otel_pool_free(struct pool_head *pool, void **ptr);
+
+/* Initialize OTel filter memory pools. */
+int flt_otel_pool_init(void);
+
+/* Destroy OTel filter memory pools. */
+void flt_otel_pool_destroy(void);
+
+/* Log debug information about OTel filter memory pools. */
+#ifndef DEBUG_OTEL
+# define flt_otel_pool_info() while (0)
+#else
+void flt_otel_pool_info(void);
+#endif
+
+/* Allocate a trash buffer with optional zeroing. */
+struct buffer *flt_otel_trash_alloc(bool flag_clear, char **err);
+
+/* Release a trash buffer and clear the pointer. */
+void flt_otel_trash_free(struct buffer **ptr);
+
+#endif /* _OTEL_POOL_H_ */
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#ifndef _OTEL_SCOPE_H_
+#define _OTEL_SCOPE_H_
+
+#define FLT_OTEL_SCOPE_SPAN_FINISH_REQ "*req*"
+#define FLT_OTEL_SCOPE_SPAN_FINISH_RES "*res*"
+#define FLT_OTEL_SCOPE_SPAN_FINISH_ALL "*"
+
+#define FLT_OTEL_RT_CTX(p) ((struct flt_otel_runtime_context *)(p))
+
+#define FLT_OTEL_DBG_SCOPE_SPAN(h,p) \
+ OTELC_DBG(DEBUG, h "%p:{ '%s' %zu %u %hhu %p %p %p }", (p), \
+ FLT_OTEL_STR_HDR_ARGS(p, id), (p)->smp_opt_dir, \
+ (p)->flag_finish, (p)->span, (p)->ref_span, (p)->ref_ctx)
+
+#define FLT_OTEL_DBG_SCOPE_CONTEXT(h,p) \
+ OTELC_DBG(DEBUG, h "%p:{ '%s' %zu %u %hhu %p }", (p), \
+ FLT_OTEL_STR_HDR_ARGS(p, id), (p)->smp_opt_dir, \
+ (p)->flag_finish, (p)->context)
+
+#define FLT_OTEL_DBG_SCOPE_DATA_EVENT(h,p) \
+ OTELC_DBG(DEBUG, h "%p:{ '%s' %p %zu %zu %s }", &(p), \
+ (p).name, (p).attr, (p).cnt, (p).size, \
+ flt_otel_list_dump(&((p).list)))
+
+#define FLT_OTEL_DBG_SCOPE_DATA_STATUS(h,p) \
+ OTELC_DBG(DEBUG, h "%p:{ %d '%s' }", (p), (p)->code, OTELC_STR_ARG((p)->description))
+
+#define FLT_OTEL_DBG_SCOPE_DATA_KV_FMT "%p:{ %p %zu %zu }"
+#define FLT_OTEL_DBG_SCOPE_DATA_KV_ARGS(p) &(p), (p).attr, (p).cnt, (p).size
+#define FLT_OTEL_DBG_SCOPE_DATA(h,p) \
+ OTELC_DBG(DEBUG, h "%p:{ " FLT_OTEL_DBG_SCOPE_DATA_KV_FMT " " FLT_OTEL_DBG_SCOPE_DATA_KV_FMT " %s }", (p), \
+ FLT_OTEL_DBG_SCOPE_DATA_KV_ARGS((p)->baggage), FLT_OTEL_DBG_SCOPE_DATA_KV_ARGS((p)->attributes), \
+ flt_otel_list_dump(&((p)->events)))
+
+#define FLT_OTEL_DBG_RUNTIME_CONTEXT(h,p) \
+ OTELC_DBG(DEBUG, h "%p:{ %p %p '%s' %hhu %hhu 0x%02hhx 0x%08x %u %d %s %s }", (p), \
+ (p)->stream, (p)->filter, (p)->uuid, (p)->flag_harderr, (p)->flag_disabled, \
+ (p)->logging, (p)->analyzers, (p)->idle_timeout, (p)->idle_exp, \
+ flt_otel_list_dump(&((p)->spans)), flt_otel_list_dump(&((p)->contexts)))
+
+/* Anonymous struct containing a const string pointer and its length. */
+#define FLT_OTEL_CONST_STR_HDR(p) \
+ struct { \
+ const char *p; \
+ size_t p##_len; \
+ }
+
+
+/* Growable key-value array for span attributes or baggage. */
+struct flt_otel_scope_data_kv {
+ struct otelc_kv *attr; /* Key-value array for storing attributes. */
+ size_t cnt; /* Number of currently used array elements. */
+ size_t size; /* Total number of array elements. */
+};
+
+/* Named event with its own key-value attribute array. */
+struct flt_otel_scope_data_event {
+ char *name; /* Event name, not used for other data types. */
+ struct otelc_kv *attr; /* Key-value array for storing attributes. */
+ size_t cnt; /* Number of currently used array elements. */
+ size_t size; /* Total number of array elements. */
+ struct list list; /* Used to chain this structure. */
+};
+
+struct flt_otel_scope_data_status {
+ int code; /* OTELC_SPAN_STATUS_* value. */
+ char *description; /* Span status description string. */
+};
+
+struct flt_otel_scope_data {
+ struct flt_otel_scope_data_kv baggage; /* Defined scope baggage. */
+ struct flt_otel_scope_data_kv attributes; /* Defined scope attributes. */
+ struct list events; /* Defined scope events. */
+ struct flt_otel_scope_data_status status; /* Defined scope status. */
+};
+
+/* flt_otel_runtime_context->spans */
+struct flt_otel_scope_span {
+ FLT_OTEL_CONST_STR_HDR(id); /* The span operation name/len. */
+ uint smp_opt_dir; /* SMP_OPT_DIR_RE(Q|S) */
+ bool flag_finish; /* Whether the span is marked for completion. */
+ struct otelc_span *span; /* The current span. */
+ struct otelc_span *ref_span; /* Span to which the current span refers. */
+ struct otelc_span_context *ref_ctx; /* Span context to which the current span refers. */
+ struct list list; /* Used to chain this structure. */
+};
+
+/* flt_otel_runtime_context->contexts */
+struct flt_otel_scope_context {
+ FLT_OTEL_CONST_STR_HDR(id); /* The span context name/len. */
+ uint smp_opt_dir; /* SMP_OPT_DIR_RE(Q|S) */
+ bool flag_finish; /* Whether the span context is marked for completion. */
+ struct otelc_span_context *context; /* The current span context. */
+ struct list list; /* Used to chain this structure. */
+};
+
+/* The runtime filter context attached to a stream. */
+struct flt_otel_runtime_context {
+ struct stream *stream; /* The stream to which the filter is attached. */
+ struct filter *filter; /* The OpenTelemetry filter. */
+ char uuid[40]; /* Randomly generated UUID. */
+ bool flag_harderr; /* [0 1] */
+ bool flag_disabled; /* [0 1] */
+ uint8_t logging; /* [0 1 3] */
+ uint analyzers; /* Executed channel analyzers. */
+ uint idle_timeout; /* Idle timeout interval in milliseconds (0 = off). */
+ int idle_exp; /* Tick at which the next idle timeout fires. */
+ struct list spans; /* The scope spans. */
+ struct list contexts; /* The scope contexts. */
+};
+
+
+#ifndef DEBUG_OTEL
+# define flt_otel_scope_data_dump(...) while (0)
+#else
+/* Dump scope data contents for debugging. */
+void flt_otel_scope_data_dump(const struct flt_otel_scope_data *data);
+#endif
+
+/* Allocate and initialize a runtime context for a stream. */
+struct flt_otel_runtime_context *flt_otel_runtime_context_init(struct stream *s, struct filter *f, char **err);
+
+/* Free the runtime context attached to a filter. */
+void flt_otel_runtime_context_free(struct filter *f);
+
+/* Allocate and initialize a scope span in the runtime context. */
+struct flt_otel_scope_span *flt_otel_scope_span_init(struct flt_otel_runtime_context *rt_ctx, const char *id, size_t id_len, const char *ref_id, size_t ref_id_len, uint dir, char **err);
+
+/* Free a scope span and remove it from the runtime context. */
+void flt_otel_scope_span_free(struct flt_otel_scope_span **ptr);
+
+/* Allocate and initialize a scope context in the runtime context. */
+struct flt_otel_scope_context *flt_otel_scope_context_init(struct flt_otel_runtime_context *rt_ctx, struct otelc_tracer *tracer, const char *id, size_t id_len, const struct otelc_text_map *text_map, uint dir, char **err);
+
+/* Free a scope context and remove it from the runtime context. */
+void flt_otel_scope_context_free(struct flt_otel_scope_context **ptr);
+
+/* Initialize scope data arrays and lists. */
+void flt_otel_scope_data_init(struct flt_otel_scope_data *ptr);
+
+/* Free all scope data contents. */
+void flt_otel_scope_data_free(struct flt_otel_scope_data *ptr);
+
+/* Free scope spans and contexts no longer needed by a channel. */
+void flt_otel_scope_free_unused(struct flt_otel_runtime_context *rt_ctx, struct channel *chn);
+
+#endif /* _OTEL_SCOPE_H_ */
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "../include/include.h"
+
+
+struct pool_head *pool_head_otel_scope_span __read_mostly = NULL;
+struct pool_head *pool_head_otel_scope_context __read_mostly = NULL;
+struct pool_head *pool_head_otel_runtime_context __read_mostly = NULL;
+struct pool_head *pool_head_otel_span_context __read_mostly = NULL;
+
+#ifdef USE_POOL_OTEL_SCOPE_SPAN
+REGISTER_POOL(&pool_head_otel_scope_span, "otel_scope_span", sizeof(struct flt_otel_scope_span));
+#endif
+#ifdef USE_POOL_OTEL_SCOPE_CONTEXT
+REGISTER_POOL(&pool_head_otel_scope_context, "otel_scope_context", sizeof(struct flt_otel_scope_context));
+#endif
+#ifdef USE_POOL_OTEL_RUNTIME_CONTEXT
+REGISTER_POOL(&pool_head_otel_runtime_context, "otel_runtime_context", sizeof(struct flt_otel_runtime_context));
+#endif
+#ifdef USE_POOL_OTEL_SPAN_CONTEXT
+REGISTER_POOL(&pool_head_otel_span_context, "otel_span_context", MAX(sizeof(struct otelc_span), sizeof(struct otelc_span_context)));
+#endif
+
+
+/***
+ * NAME
+ * flt_otel_pool_alloc - pool-aware memory allocation
+ *
+ * SYNOPSIS
+ * void *flt_otel_pool_alloc(struct pool_head *pool, size_t size, bool flag_clear, char **err)
+ *
+ * ARGUMENTS
+ * pool - HAProxy memory pool to allocate from (or NULL for heap)
+ * size - number of bytes to allocate
+ * flag_clear - whether to zero-fill the allocated memory
+ * err - indirect pointer to error message string
+ *
+ * DESCRIPTION
+ * Allocates <size> bytes of memory from the HAProxy memory <pool>. If <pool>
+ * is NULL, the allocation falls back to the heap via OTELC_MALLOC(). When
+ * <flag_clear> is set, the allocated memory is zero-filled. On allocation
+ * failure, an error message is stored via <err>.
+ *
+ * RETURN VALUE
+ * Returns a pointer to the allocated memory, or NULL on failure.
+ */
+void *flt_otel_pool_alloc(struct pool_head *pool, size_t size, bool flag_clear, char **err)
+{
+ void *retptr;
+
+ OTELC_FUNC("%p, %zu, %hhu, %p:%p", pool, size, flag_clear, OTELC_DPTR_ARGS(err));
+
+ if (pool != NULL) {
+ retptr = pool_alloc(pool);
+ if (retptr != NULL)
+ OTELC_DBG(NOTICE, "POOL_ALLOC: %s:%d(%p %zu)", __func__, __LINE__, retptr, FLT_OTEL_DEREF(pool, size, size));
+ } else {
+ retptr = OTELC_MALLOC(size);
+ }
+
+ if (retptr == NULL)
+ FLT_OTEL_ERR("out of memory");
+ else if (flag_clear)
+ (void)memset(retptr, 0, size);
+
+ OTELC_RETURN_PTR(retptr);
+}
+
+
+/***
+ * NAME
+ * flt_otel_pool_strndup - pool-aware string duplication
+ *
+ * SYNOPSIS
+ * void *flt_otel_pool_strndup(struct pool_head *pool, const char *s, size_t size, char **err)
+ *
+ * ARGUMENTS
+ * pool - HAProxy memory pool to allocate from (or NULL for heap)
+ * s - source string to duplicate
+ * size - maximum number of characters to copy
+ * err - indirect pointer to error message string
+ *
+ * DESCRIPTION
+ * Duplicates up to <size> characters from the string <s> using the HAProxy
+ * memory <pool>. If <pool> is NULL, the duplication falls back to
+ * OTELC_STRNDUP(). When using a pool, the copy is truncated to <pool>->size-1
+ * bytes and null-terminated.
+ *
+ * RETURN VALUE
+ * Returns a pointer to the duplicated string, or NULL on failure.
+ */
+void *flt_otel_pool_strndup(struct pool_head *pool, const char *s, size_t size, char **err)
+{
+ void *retptr;
+
+ OTELC_FUNC("%p, \"%.*s\", %zu, %p:%p", pool, (int)size, s, size, OTELC_DPTR_ARGS(err));
+
+ if (pool != NULL) {
+ retptr = pool_alloc(pool);
+ if (retptr != NULL) {
+ (void)memcpy(retptr, s, MIN(pool->size - 1, size));
+
+ ((uint8_t *)retptr)[MIN(pool->size - 1, size)] = '\0';
+ }
+ } else {
+ retptr = OTELC_STRNDUP(s, size);
+ }
+
+ if (retptr != NULL)
+ OTELC_DBG(NOTICE, "POOL_STRNDUP: %s:%d(%p %zu)", __func__, __LINE__, retptr, FLT_OTEL_DEREF(pool, size, size));
+ else
+ FLT_OTEL_ERR("out of memory");
+
+ OTELC_RETURN_PTR(retptr);
+}
+
+
+/***
+ * NAME
+ * flt_otel_pool_free - pool-aware memory deallocation
+ *
+ * SYNOPSIS
+ * void flt_otel_pool_free(struct pool_head *pool, void **ptr)
+ *
+ * ARGUMENTS
+ * pool - HAProxy memory pool to return memory to (or NULL for heap)
+ * ptr - indirect pointer to the memory to free
+ *
+ * DESCRIPTION
+ * Returns memory referenced by <*ptr> to the HAProxy memory <pool>. If
+ * <pool> is NULL, the memory is freed via OTELC_SFREE(). The pointer <*ptr>
+ * is set to NULL after freeing. If <ptr> is NULL or <*ptr> is already NULL,
+ * the function returns immediately.
+ *
+ * RETURN VALUE
+ * This function does not return a value.
+ */
+void flt_otel_pool_free(struct pool_head *pool, void **ptr)
+{
+ OTELC_FUNC("%p, %p:%p", pool, OTELC_DPTR_ARGS(ptr));
+
+ if ((ptr == NULL) || (*ptr == NULL))
+ OTELC_RETURN();
+
+ OTELC_DBG(NOTICE, "POOL_FREE: %s:%d(%p %u)", __func__, __LINE__, *ptr, FLT_OTEL_DEREF(pool, size, 0));
+
+ if (pool != NULL)
+ pool_free(pool, *ptr);
+ else
+ OTELC_SFREE(*ptr);
+
+ *ptr = NULL;
+
+ OTELC_RETURN();
+}
+
+
+/***
+ * NAME
+ * flt_otel_pool_init - OTel filter memory pool initialization
+ *
+ * SYNOPSIS
+ * int flt_otel_pool_init(void)
+ *
+ * ARGUMENTS
+ * This function takes no arguments.
+ *
+ * DESCRIPTION
+ * Initializes all memory pools used by the OTel filter. Each pool is
+ * created only when the corresponding USE_POOL_OTEL_* macro is defined.
+ *
+ * RETURN VALUE
+ * Returns FLT_OTEL_RET_OK on success, FLT_OTEL_RET_ERROR on failure.
+ */
+int flt_otel_pool_init(void)
+{
+ int retval = FLT_OTEL_RET_OK;
+
+ OTELC_FUNC("");
+
+#ifdef USE_POOL_OTEL_SCOPE_SPAN
+ FLT_OTEL_POOL_INIT(pool_head_otel_scope_span, "otel_scope_span", sizeof(struct flt_otel_scope_span), retval);
+#endif
+#ifdef USE_POOL_OTEL_SCOPE_CONTEXT
+ FLT_OTEL_POOL_INIT(pool_head_otel_scope_context, "otel_scope_context", sizeof(struct flt_otel_scope_context), retval);
+#endif
+#ifdef USE_POOL_OTEL_RUNTIME_CONTEXT
+ FLT_OTEL_POOL_INIT(pool_head_otel_runtime_context, "otel_runtime_context", sizeof(struct flt_otel_runtime_context), retval);
+#endif
+#ifdef USE_POOL_OTEL_SPAN_CONTEXT
+ FLT_OTEL_POOL_INIT(pool_head_otel_span_context, "otel_span_context", OTELC_MAX(sizeof(struct otelc_span), sizeof(struct otelc_span_context)), retval);
+#endif
+
+ OTELC_RETURN_INT(retval);
+}
+
+
+/***
+ * NAME
+ * flt_otel_pool_destroy - OTel filter memory pool destruction
+ *
+ * SYNOPSIS
+ * void flt_otel_pool_destroy(void)
+ *
+ * ARGUMENTS
+ * This function takes no arguments.
+ *
+ * DESCRIPTION
+ * Destroys all memory pools used by the OTel filter. Each pool is
+ * destroyed only when the corresponding USE_POOL_OTEL_* macro is defined.
+ *
+ * RETURN VALUE
+ * This function does not return a value.
+ */
+void flt_otel_pool_destroy(void)
+{
+ OTELC_FUNC("");
+
+#ifdef USE_POOL_OTEL_SCOPE_SPAN
+ FLT_OTEL_POOL_DESTROY(pool_head_otel_scope_span);
+#endif
+#ifdef USE_POOL_OTEL_SCOPE_CONTEXT
+ FLT_OTEL_POOL_DESTROY(pool_head_otel_scope_context);
+#endif
+#ifdef USE_POOL_OTEL_RUNTIME_CONTEXT
+ FLT_OTEL_POOL_DESTROY(pool_head_otel_runtime_context);
+#endif
+#ifdef USE_POOL_OTEL_SPAN_CONTEXT
+ FLT_OTEL_POOL_DESTROY(pool_head_otel_span_context);
+#endif
+
+ OTELC_RETURN();
+}
+
+
+#ifdef DEBUG_OTEL
+
+/***
+ * NAME
+ * flt_otel_pool_info - debug pool sizes logging
+ *
+ * SYNOPSIS
+ * void flt_otel_pool_info(void)
+ *
+ * ARGUMENTS
+ * This function takes no arguments.
+ *
+ * DESCRIPTION
+ * Logs the sizes of all registered HAProxy memory pools used by the OTel
+ * filter (buffer, trash, scope_span, scope_context, runtime_context).
+ *
+ * RETURN VALUE
+ * This function does not return a value.
+ */
+void flt_otel_pool_info(void)
+{
+ OTELC_DBG(NOTICE, "--- pool info ----------");
+
+ /*
+ * In case we have some error in the configuration file,
+ * it is possible that this pool was not initialized.
+ */
+#ifdef USE_POOL_BUFFER
+ OTELC_DBG(NOTICE, " buffer: %p %u", pool_head_buffer, FLT_OTEL_DEREF(pool_head_buffer, size, 0));
+#endif
+#ifdef USE_TRASH_CHUNK
+ OTELC_DBG(NOTICE, " trash: %p %u", pool_head_trash, FLT_OTEL_DEREF(pool_head_trash, size, 0));
+#endif
+
+#ifdef USE_POOL_OTEL_SCOPE_SPAN
+ OTELC_DBG(NOTICE, " otel_scope_span: %p %u", pool_head_otel_scope_span, FLT_OTEL_DEREF(pool_head_otel_scope_span, size, 0));
+#endif
+#ifdef USE_POOL_OTEL_SCOPE_CONTEXT
+ OTELC_DBG(NOTICE, " otel_scope_context: %p %u", pool_head_otel_scope_context, FLT_OTEL_DEREF(pool_head_otel_scope_context, size, 0));
+#endif
+#ifdef USE_POOL_OTEL_RUNTIME_CONTEXT
+ OTELC_DBG(NOTICE, " otel_runtime_context: %p %u", pool_head_otel_runtime_context, FLT_OTEL_DEREF(pool_head_otel_runtime_context, size, 0));
+#endif
+#ifdef USE_POOL_OTEL_SPAN_CONTEXT
+ OTELC_DBG(NOTICE, " otel_span_context: %p %u", pool_head_otel_span_context, FLT_OTEL_DEREF(pool_head_otel_span_context, size, 0));
+#endif
+}
+
+#endif /* DEBUG_OTEL */
+
+
+/***
+ * NAME
+ * flt_otel_trash_alloc - trash buffer allocation
+ *
+ * SYNOPSIS
+ * struct buffer *flt_otel_trash_alloc(bool flag_clear, char **err)
+ *
+ * ARGUMENTS
+ * flag_clear - whether to zero-fill the buffer area
+ * err - indirect pointer to error message string
+ *
+ * DESCRIPTION
+ * Allocates a temporary buffer chunk for use as scratch space. When
+ * USE_TRASH_CHUNK is defined, the buffer is obtained via alloc_trash_chunk();
+ * otherwise, a buffer structure and its data area are allocated from the heap
+ * using global.tune.bufsize as the buffer size. When <flag_clear> is set,
+ * the buffer's data area is zero-filled.
+ *
+ * RETURN VALUE
+ * Returns a pointer to the allocated buffer, or NULL on failure.
+ */
+struct buffer *flt_otel_trash_alloc(bool flag_clear, char **err)
+{
+ struct buffer *retptr;
+
+ OTELC_FUNC("%hhu, %p:%p", flag_clear, OTELC_DPTR_ARGS(err));
+
+#ifdef USE_TRASH_CHUNK
+ retptr = alloc_trash_chunk();
+ if (retptr != NULL)
+ OTELC_DBG(NOTICE, "TRASH_ALLOC: %s:%d(%p %zu)", __func__, __LINE__, retptr, retptr->size);
+#else
+ retptr = OTELC_MALLOC(sizeof(*retptr));
+ if (retptr != NULL) {
+ chunk_init(retptr, OTELC_MALLOC(global.tune.bufsize), global.tune.bufsize);
+ if (retptr->area == NULL)
+ OTELC_SFREE_CLEAR(retptr);
+ else
+ *(retptr->area) = '\0';
+ }
+#endif
+
+ if (retptr == NULL)
+ FLT_OTEL_ERR("out of memory");
+ else if (flag_clear)
+ (void)memset(retptr->area, 0, retptr->size);
+
+ OTELC_RETURN_PTR(retptr);
+}
+
+
+/***
+ * NAME
+ * flt_otel_trash_free - trash buffer deallocation
+ *
+ * SYNOPSIS
+ * void flt_otel_trash_free(struct buffer **ptr)
+ *
+ * ARGUMENTS
+ * ptr - indirect pointer to the buffer to free
+ *
+ * DESCRIPTION
+ * Frees a trash buffer chunk previously allocated by flt_otel_trash_alloc().
+ * When USE_TRASH_CHUNK is defined, the buffer is freed via
+ * free_trash_chunk(); otherwise, both the data area and the buffer structure
+ * are freed individually. The pointer <*ptr> is set to NULL after freeing.
+ *
+ * RETURN VALUE
+ * This function does not return a value.
+ */
+void flt_otel_trash_free(struct buffer **ptr)
+{
+ OTELC_FUNC("%p:%p", OTELC_DPTR_ARGS(ptr));
+
+ if ((ptr == NULL) || (*ptr == NULL))
+ OTELC_RETURN();
+
+ OTELC_DBG(NOTICE, "TRASH_FREE: %s:%d(%p %zu)", __func__, __LINE__, *ptr, (*ptr)->size);
+
+#ifdef USE_TRASH_CHUNK
+ free_trash_chunk(*ptr);
+#else
+ OTELC_SFREE((*ptr)->area);
+ OTELC_SFREE(*ptr);
+#endif
+
+ *ptr = NULL;
+
+ OTELC_RETURN();
+}
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "../include/include.h"
+
+
+/***
+ * NAME
+ * flt_otel_runtime_context_init - per-stream runtime context allocation
+ *
+ * SYNOPSIS
+ * struct flt_otel_runtime_context *flt_otel_runtime_context_init(struct stream *s, struct filter *f, char **err)
+ *
+ * ARGUMENTS
+ * s - the stream to which the context belongs
+ * f - the filter instance
+ * err - indirect pointer to error message string
+ *
+ * DESCRIPTION
+ * Allocates and initializes a per-stream runtime context from pool memory.
+ * It copies the hard-error, disabled and logging flags from the filter
+ * configuration, generates a UUID and stores it in the sess.otel.uuid
+ * HAProxy variable.
+ *
+ * RETURN VALUE
+ * Returns a pointer to the new runtime context, or NULL on failure.
+ */
+struct flt_otel_runtime_context *flt_otel_runtime_context_init(struct stream *s, struct filter *f, char **err)
+{
+ const struct flt_otel_conf *conf = FLT_OTEL_CONF(f);
+ struct buffer uuid;
+ struct flt_otel_runtime_context *retptr = NULL;
+
+ OTELC_FUNC("%p, %p, %p:%p", s, f, OTELC_DPTR_ARGS(err));
+
+ retptr = flt_otel_pool_alloc(pool_head_otel_runtime_context, sizeof(*retptr), 1, err);
+ if (retptr == NULL)
+ OTELC_RETURN_PTR(retptr);
+
+ /* Initialize runtime context fields and generate a session UUID. */
+ retptr->stream = s;
+ retptr->filter = f;
+ retptr->flag_harderr = _HA_ATOMIC_LOAD(&(conf->instr->flag_harderr));
+ retptr->flag_disabled = _HA_ATOMIC_LOAD(&(conf->instr->flag_disabled));
+ retptr->logging = _HA_ATOMIC_LOAD(&(conf->instr->logging));
+ retptr->idle_timeout = 0;
+ retptr->idle_exp = TICK_ETERNITY;
+ LIST_INIT(&(retptr->spans));
+ LIST_INIT(&(retptr->contexts));
+
+ uuid = b_make(retptr->uuid, sizeof(retptr->uuid), 0, 0);
+ ha_generate_uuid_v4(&uuid);
+
+ FLT_OTEL_DBG_RUNTIME_CONTEXT("session context: ", retptr);
+
+ OTELC_RETURN_PTR(retptr);
+}
+
+
+/***
+ * NAME
+ * flt_otel_runtime_context_free - per-stream runtime context cleanup
+ *
+ * SYNOPSIS
+ * void flt_otel_runtime_context_free(struct filter *f)
+ *
+ * ARGUMENTS
+ * f - the filter instance whose runtime context is to be freed
+ *
+ * DESCRIPTION
+ * Frees the per-stream runtime context attached to <f>. It ends all active
+ * spans with the current monotonic timestamp, destroys all extracted
+ * contexts, and returns the pool memory.
+ *
+ * RETURN VALUE
+ * This function does not return a value.
+ */
+void flt_otel_runtime_context_free(struct filter *f)
+{
+ struct flt_otel_runtime_context *rt_ctx = f->ctx;
+
+ OTELC_FUNC("%p", f);
+
+ if (rt_ctx == NULL)
+ OTELC_RETURN();
+
+ FLT_OTEL_DBG_RUNTIME_CONTEXT("session context: ", rt_ctx);
+
+ /* End all active spans with a common timestamp. */
+ if (!LIST_ISEMPTY(&(rt_ctx->spans))) {
+ struct flt_otel_scope_span *span, *span_back;
+
+ list_for_each_entry_safe(span, span_back, &(rt_ctx->spans), list)
+ flt_otel_scope_span_free(&span);
+ }
+
+ /* Destroy all extracted span contexts. */
+ if (!LIST_ISEMPTY(&(rt_ctx->contexts))) {
+ struct flt_otel_scope_context *ctx, *ctx_back;
+
+ list_for_each_entry_safe(ctx, ctx_back, &(rt_ctx->contexts), list)
+ flt_otel_scope_context_free(&ctx);
+ }
+
+ flt_otel_pool_free(pool_head_otel_runtime_context, &(f->ctx));
+
+ OTELC_RETURN();
+}
+
+
+/***
+ * NAME
+ * flt_otel_scope_span_init - scope span lookup or creation
+ *
+ * SYNOPSIS
+ * struct flt_otel_scope_span *flt_otel_scope_span_init(struct flt_otel_runtime_context *rt_ctx, const char *id, size_t id_len, const char *ref_id, size_t ref_id_len, uint dir, char **err)
+ *
+ * ARGUMENTS
+ * rt_ctx - the runtime context owning the span list
+ * id - the span operation name
+ * id_len - length of the <id> string
+ * ref_id - the parent span or context name, or NULL
+ * ref_id_len - length of the <ref_id> string
+ * dir - the sample fetch direction (SMP_OPT_DIR_REQ/RES)
+ * err - indirect pointer to error message string
+ *
+ * DESCRIPTION
+ * Finds an existing scope span by <id> in the runtime context or creates a
+ * new one. If <ref_id> is set, it resolves the parent reference by searching
+ * the span list first, then the extracted context list.
+ *
+ * RETURN VALUE
+ * Returns the existing or new scope span, or NULL on failure.
+ */
+struct flt_otel_scope_span *flt_otel_scope_span_init(struct flt_otel_runtime_context *rt_ctx, const char *id, size_t id_len, const char *ref_id, size_t ref_id_len, uint dir, char **err)
+{
+ struct otelc_span *ref_span = NULL;
+ struct otelc_span_context *ref_ctx = NULL;
+ struct flt_otel_scope_span *span, *retptr = NULL;
+ struct flt_otel_scope_context *ctx;
+
+ OTELC_FUNC("%p, \"%s\", %zu, \"%s\", %zu, %u, %p:%p", rt_ctx, OTELC_STR_ARG(id), id_len, OTELC_STR_ARG(ref_id), ref_id_len, dir, OTELC_DPTR_ARGS(err));
+
+ if ((rt_ctx == NULL) || (id == NULL))
+ OTELC_RETURN_PTR(retptr);
+
+ /* Return the existing span if one matches this ID. */
+ list_for_each_entry(span, &(rt_ctx->spans), list)
+ if (FLT_OTEL_CONF_STR_CMP(span->id, id)) {
+ OTELC_DBG(NOTICE, "found span %p", span);
+
+ OTELC_RETURN_PTR(span);
+ }
+
+ /* Resolve the parent reference from spans or contexts. */
+ if (ref_id != NULL) {
+ list_for_each_entry(span, &(rt_ctx->spans), list)
+ if (FLT_OTEL_CONF_STR_CMP(span->id, ref_id)) {
+ ref_span = span->span;
+
+ break;
+ }
+
+ if (ref_span != NULL) {
+ OTELC_DBG(NOTICE, "found referenced span %p", span);
+ } else {
+ list_for_each_entry(ctx, &(rt_ctx->contexts), list)
+ if (FLT_OTEL_CONF_STR_CMP(ctx->id, ref_id)) {
+ ref_ctx = ctx->context;
+
+ break;
+ }
+
+ if (ref_ctx != NULL) {
+ OTELC_DBG(NOTICE, "found referenced context %p", ctx);
+ } else {
+ FLT_OTEL_ERR("cannot find referenced span/context '%s'", ref_id);
+
+ OTELC_RETURN_PTR(retptr);
+ }
+ }
+ }
+
+ retptr = flt_otel_pool_alloc(pool_head_otel_scope_span, sizeof(*retptr), 1, err);
+ if (retptr == NULL)
+ OTELC_RETURN_PTR(retptr);
+
+ /* Populate the new scope span and insert it into the list. */
+ retptr->id = id;
+ retptr->id_len = id_len;
+ retptr->smp_opt_dir = dir;
+ retptr->ref_span = ref_span;
+ retptr->ref_ctx = ref_ctx;
+ LIST_INSERT(&(rt_ctx->spans), &(retptr->list));
+
+ FLT_OTEL_DBG_SCOPE_SPAN("new span ", retptr);
+
+ OTELC_RETURN_PTR(retptr);
+}
+
+
+/***
+ * NAME
+ * flt_otel_scope_span_free - scope span cleanup
+ *
+ * SYNOPSIS
+ * void flt_otel_scope_span_free(struct flt_otel_scope_span **ptr)
+ *
+ * ARGUMENTS
+ * ptr - pointer to the scope span pointer to free
+ *
+ * DESCRIPTION
+ * Frees a scope span entry pointed to by <ptr> and removes it from its list.
+ * If the OTel span is still active (non-NULL), the function refuses to free
+ * it and returns immediately.
+ *
+ * RETURN VALUE
+ * This function does not return a value.
+ */
+void flt_otel_scope_span_free(struct flt_otel_scope_span **ptr)
+{
+ OTELC_FUNC("%p:%p", OTELC_DPTR_ARGS(ptr));
+
+ if ((ptr == NULL) || (*ptr == NULL))
+ OTELC_RETURN();
+
+ FLT_OTEL_DBG_SCOPE_SPAN("", *ptr);
+
+ /* If the span is still active, do nothing. */
+ if ((*ptr)->span != NULL) {
+ OTELC_DBG(NOTICE, "cannot finish active span");
+
+ OTELC_RETURN();
+ }
+
+ FLT_OTEL_LIST_DEL(&((*ptr)->list));
+ flt_otel_pool_free(pool_head_otel_scope_span, (void **)ptr);
+
+ OTELC_RETURN();
+}
+
+
+/***
+ * NAME
+ * flt_otel_scope_context_init - scope context extraction
+ *
+ * SYNOPSIS
+ * struct flt_otel_scope_context *flt_otel_scope_context_init(struct flt_otel_runtime_context *rt_ctx, struct otelc_tracer *tracer, const char *id, size_t id_len, const struct otelc_text_map *text_map, uint dir, char **err)
+ *
+ * ARGUMENTS
+ * rt_ctx - the runtime context owning the context list
+ * tracer - the OTel tracer used for context extraction
+ * id - the context name
+ * id_len - length of the <id> string
+ * text_map - the carrier text map to extract from
+ * dir - the sample fetch direction (SMP_OPT_DIR_REQ/RES)
+ * err - indirect pointer to error message string
+ *
+ * DESCRIPTION
+ * Finds an existing scope context by <id> in the runtime context or creates
+ * a new one by extracting the span context from the <text_map> carrier via
+ * the <tracer>.
+ *
+ * RETURN VALUE
+ * Returns the existing or new scope context, or NULL on failure.
+ */
+struct flt_otel_scope_context *flt_otel_scope_context_init(struct flt_otel_runtime_context *rt_ctx, struct otelc_tracer *tracer, const char *id, size_t id_len, const struct otelc_text_map *text_map, uint dir, char **err)
+{
+ struct flt_otel_scope_context *retptr = NULL;
+
+ OTELC_FUNC("%p, %p, \"%s\", %zu, %p, %u, %p:%p", rt_ctx, tracer, OTELC_STR_ARG(id), id_len, text_map, dir, OTELC_DPTR_ARGS(err));
+
+ if ((rt_ctx == NULL) || (tracer == NULL) || (id == NULL) || (text_map == NULL))
+ OTELC_RETURN_PTR(retptr);
+
+ /* Return the existing context if one matches this ID. */
+ list_for_each_entry(retptr, &(rt_ctx->contexts), list)
+ if (FLT_OTEL_CONF_STR_CMP(retptr->id, id)) {
+ OTELC_DBG(NOTICE, "found context %p", retptr);
+
+ OTELC_RETURN_PTR(retptr);
+ }
+
+ retptr = flt_otel_pool_alloc(pool_head_otel_scope_context, sizeof(*retptr), 1, err);
+ if (retptr == NULL)
+ OTELC_RETURN_PTR(retptr);
+
+ /* Populate the new scope context and insert it into the list. */
+ retptr->id = id;
+ retptr->id_len = id_len;
+ retptr->smp_opt_dir = dir;
+ LIST_INSERT(&(rt_ctx->contexts), &(retptr->list));
+
+ FLT_OTEL_DBG_SCOPE_CONTEXT("new context ", retptr);
+
+ OTELC_RETURN_PTR(retptr);
+}
+
+
+/***
+ * NAME
+ * flt_otel_scope_context_free - scope context cleanup
+ *
+ * SYNOPSIS
+ * void flt_otel_scope_context_free(struct flt_otel_scope_context **ptr)
+ *
+ * ARGUMENTS
+ * ptr - pointer to the scope context pointer to free
+ *
+ * DESCRIPTION
+ * Frees a scope context entry pointed to by <ptr>. It destroys the
+ * underlying OTel span context, removes the entry from its list, and
+ * returns the pool memory.
+ *
+ * RETURN VALUE
+ * This function does not return a value.
+ */
+void flt_otel_scope_context_free(struct flt_otel_scope_context **ptr)
+{
+ OTELC_FUNC("%p:%p", OTELC_DPTR_ARGS(ptr));
+
+ if ((ptr == NULL) || (*ptr == NULL))
+ OTELC_RETURN();
+
+ FLT_OTEL_DBG_SCOPE_CONTEXT("", *ptr);
+
+ FLT_OTEL_LIST_DEL(&((*ptr)->list));
+ flt_otel_pool_free(pool_head_otel_scope_context, (void **)ptr);
+
+ OTELC_RETURN();
+}
+
+
+#ifdef DEBUG_OTEL
+
+/***
+ * NAME
+ * flt_otel_scope_data_dump - debug scope data dump
+ *
+ * SYNOPSIS
+ * void flt_otel_scope_data_dump(const struct flt_otel_scope_data *data)
+ *
+ * ARGUMENTS
+ * data - the scope data structure to dump
+ *
+ * DESCRIPTION
+ * Dumps the contents of a scope <data> structure for debugging: baggage
+ * key-value pairs, attributes, events with their attributes, span links,
+ * and the status code/description.
+ *
+ * RETURN VALUE
+ * This function does not return a value.
+ */
+void flt_otel_scope_data_dump(const struct flt_otel_scope_data *data)
+{
+ size_t i;
+
+ if (data == NULL)
+ return;
+
+ if (data->baggage.attr == NULL) {
+ OTELC_DBG(WORKER, "baggage %p:{ }", &(data->baggage));
+ } else {
+ OTELC_DBG(WORKER, "baggage %p:{", &(data->baggage));
+ for (i = 0; i < data->baggage.cnt; i++)
+ OTELC_DBG_KV(WORKER, " ", data->baggage.attr + i);
+ OTELC_DBG(WORKER, "}");
+ }
+
+ if (data->attributes.attr == NULL) {
+ OTELC_DBG(WORKER, "attributes %p:{ }", &(data->attributes));
+ } else {
+ OTELC_DBG(WORKER, "attributes %p:{", &(data->attributes));
+ for (i = 0; i < data->attributes.cnt; i++)
+ OTELC_DBG_KV(WORKER, " ", data->attributes.attr + i);
+ OTELC_DBG(WORKER, "}");
+ }
+
+ if (LIST_ISEMPTY(&(data->events))) {
+ OTELC_DBG(WORKER, "events %p:{ }", &(data->events));
+ } else {
+ struct flt_otel_scope_data_event *event;
+
+ OTELC_DBG(WORKER, "events %p:{", &(data->events));
+ list_for_each_entry_rev(event, &(data->events), list) {
+ OTELC_DBG(WORKER, " '%s' %zu/%zu", event->name, event->cnt, event->size);
+ if (event->attr != NULL)
+ for (i = 0; i < event->cnt; i++)
+ OTELC_DBG_KV(WORKER, " ", event->attr + i);
+ }
+ OTELC_DBG(WORKER, "}");
+ }
+
+ if ((data->status.code == 0) && (data->status.description == NULL))
+ OTELC_DBG(WORKER, "status %p:{ }", &(data->status));
+ else
+ FLT_OTEL_DBG_SCOPE_DATA_STATUS("status ", &(data->status));
+}
+
+#endif /* DEBUG_OTEL */
+
+
+/***
+ * NAME
+ * flt_otel_scope_data_init - scope data zero-initialization
+ *
+ * SYNOPSIS
+ * void flt_otel_scope_data_init(struct flt_otel_scope_data *ptr)
+ *
+ * ARGUMENTS
+ * ptr - the scope data structure to initialize
+ *
+ * DESCRIPTION
+ * Zero-initializes the scope data structure pointed to by <ptr> and sets up
+ * the event and link list heads.
+ *
+ * RETURN VALUE
+ * This function does not return a value.
+ */
+void flt_otel_scope_data_init(struct flt_otel_scope_data *ptr)
+{
+ OTELC_FUNC("%p", ptr);
+
+ if (ptr == NULL)
+ OTELC_RETURN();
+
+ (void)memset(ptr, 0, sizeof(*ptr));
+ LIST_INIT(&(ptr->events));
+
+ OTELC_RETURN();
+}
+
+
+/***
+ * NAME
+ * flt_otel_scope_data_free - scope data cleanup
+ *
+ * SYNOPSIS
+ * void flt_otel_scope_data_free(struct flt_otel_scope_data *ptr)
+ *
+ * ARGUMENTS
+ * ptr - the scope data structure to free
+ *
+ * DESCRIPTION
+ * Frees all contents of the scope data structure pointed to by <ptr>: baggage
+ * and attribute key-value arrays, event entries with their attributes, link
+ * entries, and the status description string.
+ *
+ * RETURN VALUE
+ * This function does not return a value.
+ */
+void flt_otel_scope_data_free(struct flt_otel_scope_data *ptr)
+{
+ struct flt_otel_scope_data_event *event, *event_back;
+
+ OTELC_FUNC("%p", ptr);
+
+ if (ptr == NULL)
+ OTELC_RETURN();
+
+ FLT_OTEL_DBG_SCOPE_DATA("", ptr);
+
+ /* Destroy all dynamic scope data contents. */
+ otelc_kv_destroy(&(ptr->baggage.attr), ptr->baggage.cnt);
+ otelc_kv_destroy(&(ptr->attributes.attr), ptr->attributes.cnt);
+ list_for_each_entry_safe(event, event_back, &(ptr->events), list) {
+ otelc_kv_destroy(&(event->attr), event->cnt);
+ OTELC_SFREE(event->name);
+ OTELC_SFREE(event);
+ }
+ OTELC_SFREE(ptr->status.description);
+
+ (void)memset(ptr, 0, sizeof(*ptr));
+
+ OTELC_RETURN();
+}
+
+
+/***
+ * NAME
+ * flt_otel_scope_free_unused - remove unused spans and contexts
+ *
+ * SYNOPSIS
+ * void flt_otel_scope_free_unused(struct flt_otel_runtime_context *rt_ctx, struct channel *chn)
+ *
+ * ARGUMENTS
+ * rt_ctx - the runtime context to clean up
+ * chn - the channel for HTTP header cleanup
+ *
+ * DESCRIPTION
+ * Removes scope spans with a NULL OTel span and scope contexts with a NULL
+ * OTel context from the runtime context. For each removed context, the
+ * associated HTTP headers are also cleaned up via <chn>.
+ *
+ * RETURN VALUE
+ * This function does not return a value.
+ */
+void flt_otel_scope_free_unused(struct flt_otel_runtime_context *rt_ctx, struct channel *chn)
+{
+ OTELC_FUNC("%p, %p", rt_ctx, chn);
+
+ if (rt_ctx == NULL)
+ OTELC_RETURN();
+
+ /* Remove spans that were never successfully created. */
+ if (!LIST_ISEMPTY(&(rt_ctx->spans))) {
+ struct flt_otel_scope_span *span, *span_back;
+
+ list_for_each_entry_safe(span, span_back, &(rt_ctx->spans), list)
+ if (span->span == NULL)
+ flt_otel_scope_span_free(&span);
+ }
+
+ FLT_OTEL_DBG_RUNTIME_CONTEXT("session context: ", rt_ctx);
+
+ OTELC_RETURN();
+}
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */