-all: test
+SUBDIRS := src/
-test: test.c
- gcc -O2 -g -Wall -o test test.c
+all: $(SUBDIRS)
+$(SUBDIRS):
+ $(MAKE) -C $@
-.PHONY: clean
-
-clean:
- rm -f test
+.PHONY: all $(SUBDIRS)
--- /dev/null
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2022 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#ifndef _SIDE_MACROS_H
+#define _SIDE_MACROS_H
+
+/* Helper macros */
+
+#define SIDE_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+/*
+ * Compound literals with static storage are needed by SIDE
+ * instrumentation.
+ * Compound literals are part of the C99 and C11 standards, but not
+ * part of the C++ standards. They are supported by most C++ compilers
+ * though.
+ *
+ * Example use:
+ * static struct mystruct *var = LTTNG_UST_COMPOUND_LITERAL(struct mystruct, { 1, 2, 3 });
+ */
+#define SIDE_COMPOUND_LITERAL(type, ...) (type[]) { __VA_ARGS__ }
+
+#define side_likely(x) __builtin_expect(!!(x), 1)
+#define side_unlikely(x) __builtin_expect(!!(x), 0)
+
+#define SIDE_PARAM(...) __VA_ARGS__
+
+#endif /* _SIDE_MACROS_H */
--- /dev/null
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2022 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#ifndef _SIDE_TRACE_H
+#define _SIDE_TRACE_H
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <side/macros.h>
+
+/* SIDE stands for "Static Instrumentation Dynamically Enabled" */
+
+struct side_arg_vec;
+struct side_type_description;
+struct side_event_field;
+
+enum side_type {
+ SIDE_TYPE_U8,
+ SIDE_TYPE_U16,
+ SIDE_TYPE_U32,
+ SIDE_TYPE_U64,
+ SIDE_TYPE_S8,
+ SIDE_TYPE_S16,
+ SIDE_TYPE_S32,
+ SIDE_TYPE_S64,
+ SIDE_TYPE_STRING,
+ SIDE_TYPE_DYNAMIC,
+ SIDE_TYPE_STRUCT,
+ SIDE_TYPE_ARRAY,
+ SIDE_TYPE_VLA,
+ SIDE_TYPE_VLA_VISITOR,
+ //TODO:
+ //specialized array and vla for fixed-size integers (optimization)
+ //variants (discriminated unions)
+};
+
+enum side_loglevel {
+ SIDE_LOGLEVEL_EMERG = 0,
+ SIDE_LOGLEVEL_ALERT = 1,
+ SIDE_LOGLEVEL_CRIT = 2,
+ SIDE_LOGLEVEL_ERR = 3,
+ SIDE_LOGLEVEL_WARNING = 4,
+ SIDE_LOGLEVEL_NOTICE = 5,
+ SIDE_LOGLEVEL_INFO = 6,
+ SIDE_LOGLEVEL_DEBUG = 7,
+};
+
+enum side_visitor_status {
+ SIDE_VISITOR_STATUS_ERROR = -1,
+ SIDE_VISITOR_STATUS_OK = 0,
+ SIDE_VISITOR_STATUS_END = 1,
+};
+
+typedef enum side_visitor_status (*side_visitor_begin)(void *ctx);
+typedef enum side_visitor_status (*side_visitor_end)(void *ctx);
+typedef enum side_visitor_status (*side_visitor_get_next)(void *ctx, struct side_arg_vec *sav_elem);
+
+struct side_type_description {
+ enum side_type type;
+ union {
+ struct {
+ uint32_t nr_fields;
+ const struct side_event_field *fields;
+ } side_struct;
+ struct {
+ uint32_t length;
+ const struct side_type_description *elem_type;
+ } side_array;
+ struct {
+ const struct side_type_description *elem_type;
+ } side_vla;
+ struct {
+ const struct side_type_description *elem_type;
+ side_visitor_begin begin;
+ side_visitor_end end;
+ side_visitor_get_next get_next;
+ } side_vla_visitor;
+ } u;
+};
+
+struct side_event_field {
+ const char *field_name;
+ struct side_type_description side_type;
+};
+
+struct side_event_description {
+ uint32_t version;
+ uint32_t enabled;
+ uint32_t loglevel; /* enum side_loglevel */
+ uint32_t nr_fields;
+ const char *provider_name;
+ const char *event_name;
+ const struct side_event_field *fields;
+};
+
+struct side_arg_vec {
+ uint32_t type; /* enum side_type */
+ union {
+ uint8_t side_u8;
+ uint16_t side_u16;
+ uint32_t side_u32;
+ uint64_t side_u64;
+ int8_t side_s8;
+ int16_t side_s16;
+ int32_t side_s32;
+ int64_t side_s64;
+ const char *string;
+ const struct side_arg_vec_description *side_struct;
+ const struct side_arg_vec_description *side_array;
+ const struct side_arg_vec_description *side_vla;
+ void *side_vla_visitor_ctx;
+ } u;
+};
+
+struct side_arg_vec_description {
+ const struct side_arg_vec *sav;
+ uint32_t len;
+};
+
+#define side_type_decl(_type) { .type = _type }
+#define side_field(_type, _name) { .field_name = _name, .side_type = side_type_decl(_type) }
+
+#define side_type_struct_decl(_fields) \
+ { \
+ .type = SIDE_TYPE_STRUCT, \
+ .u = { \
+ .side_struct = { \
+ .nr_fields = SIDE_ARRAY_SIZE(SIDE_PARAM(_fields)), \
+ .fields = _fields, \
+ }, \
+ }, \
+ }
+#define side_field_struct(_name, _fields) \
+ { \
+ .field_name = _name, \
+ .side_type = side_type_struct_decl(SIDE_PARAM(_fields)), \
+ }
+
+#define side_type_array_decl(_elem_type, _length) \
+ { \
+ .type = SIDE_TYPE_ARRAY, \
+ .u = { \
+ .side_array = { \
+ .length = _length, \
+ .elem_type = _elem_type, \
+ }, \
+ }, \
+ }
+#define side_field_array(_name, _elem_type, _length) \
+ { \
+ .field_name = _name, \
+ .side_type = side_type_array_decl(_elem_type, _length), \
+ }
+
+#define side_type_vla_decl(_elem_type) \
+ { \
+ .type = SIDE_TYPE_VLA, \
+ .u = { \
+ .side_vla = { \
+ .elem_type = _elem_type, \
+ }, \
+ }, \
+ }
+#define side_field_vla(_name, _elem_type) \
+ { \
+ .field_name = _name, \
+ .side_type = side_type_vla_decl(_elem_type), \
+ }
+
+#define side_type_vla_visitor_decl(_elem_type, _begin, _end, _get_next) \
+ { \
+ .type = SIDE_TYPE_VLA_VISITOR, \
+ .u = { \
+ .side_vla_visitor = { \
+ .elem_type = _elem_type, \
+ .begin = _begin, \
+ .end = _end, \
+ .get_next = _get_next, \
+ }, \
+ }, \
+ }
+#define side_field_vla_visitor(_name, _elem_type, _begin, _end, _get_next) \
+ { \
+ .field_name = _name, \
+ .side_type = side_type_vla_visitor_decl(_elem_type, _begin, _end, _get_next), \
+ }
+
+#define side_vla_elem(...) \
+ SIDE_COMPOUND_LITERAL(const struct side_type_description, side_type_decl(__VA_ARGS__))
+
+#define side_vla_visitor_elem(...) \
+ SIDE_COMPOUND_LITERAL(const struct side_type_description, side_type_decl(__VA_ARGS__))
+
+#define side_array_elem(...) \
+ SIDE_COMPOUND_LITERAL(const struct side_type_description, side_type_decl(__VA_ARGS__))
+
+#define side_field_list(...) \
+ SIDE_COMPOUND_LITERAL(const struct side_event_field, __VA_ARGS__)
+
+#define side_arg_u8(val) { .type = SIDE_TYPE_U8, .u = { .side_u8 = (val) } }
+#define side_arg_u16(val) { .type = SIDE_TYPE_U16, .u = { .side_u16 = (val) } }
+#define side_arg_u32(val) { .type = SIDE_TYPE_U32, .u = { .side_u32 = (val) } }
+#define side_arg_u64(val) { .type = SIDE_TYPE_U64, .u = { .side_u64 = (val) } }
+#define side_arg_s8(val) { .type = SIDE_TYPE_S8, .u = { .side_s8 = (val) } }
+#define side_arg_s16(val) { .type = SIDE_TYPE_S16, .u = { .side_s16 = (val) } }
+#define side_arg_s32(val) { .type = SIDE_TYPE_S32, .u = { .side_s32 = (val) } }
+#define side_arg_s64(val) { .type = SIDE_TYPE_S64, .u = { .side_s64 = (val) } }
+#define side_arg_string(val) { .type = SIDE_TYPE_STRING, .u = { .string = (val) } }
+#define side_arg_struct(_side_type) { .type = SIDE_TYPE_STRUCT, .u = { .side_struct = (_side_type) } }
+#define side_arg_array(_side_type) { .type = SIDE_TYPE_ARRAY, .u = { .side_array = (_side_type) } }
+#define side_arg_vla(_side_type) { .type = SIDE_TYPE_VLA, .u = { .side_vla = (_side_type) } }
+#define side_arg_vla_visitor(_ctx) { .type = SIDE_TYPE_VLA_VISITOR, .u = { .side_vla_visitor_ctx = (_ctx) } }
+
+#define side_arg_define_vec(_identifier, _sav) \
+ const struct side_arg_vec _identifier##_vec[] = { _sav }; \
+ const struct side_arg_vec_description _identifier = { \
+ .sav = _identifier##_vec, \
+ .len = SIDE_ARRAY_SIZE(_identifier##_vec), \
+ }
+
+#define side_arg_list(...) __VA_ARGS__
+
+#define side_event_cond(desc) if (side_unlikely((desc)->enabled))
+#define side_event_call(desc, _sav) \
+ { \
+ const struct side_arg_vec side_sav[] = { _sav }; \
+ const struct side_arg_vec_description sav_desc = { \
+ .sav = side_sav, \
+ .len = SIDE_ARRAY_SIZE(side_sav), \
+ }; \
+ tracer_call(desc, &sav_desc); \
+ }
+
+#define side_event(desc, sav) \
+ side_event_cond(desc) \
+ side_event_call(desc, SIDE_PARAM(sav)); \
+
+#define side_define_event(_identifier, _provider, _event, _loglevel, _fields) \
+ struct side_event_description _identifier = { \
+ .version = 0, \
+ .enabled = 0, \
+ .loglevel = _loglevel, \
+ .nr_fields = SIDE_ARRAY_SIZE(SIDE_PARAM(_fields)), \
+ .provider_name = _provider, \
+ .event_name = _event, \
+ .fields = _fields, \
+ }
+
+#define side_declare_event(_identifier) \
+ struct side_event_description _identifier
+
+#endif /* _SIDE_TRACE_H */
--- /dev/null
+all: test
+
+tracer.o: tracer.c
+ gcc -O2 -g -Wall -I../include/ -c -o tracer.o tracer.c
+
+test.o: test.c
+ gcc -O2 -g -Wall -I../include/ -c -o test.o test.c
+
+test: tracer.o test.o
+ gcc -O2 -g -Wall -o test tracer.o test.o
+
+.PHONY: clean
+
+clean:
+ rm -f test
--- /dev/null
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2022 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <side/trace.h>
+#include "tracer.h"
+
+/* User code example */
+
+static side_define_event(my_provider_event, "myprovider", "myevent", SIDE_LOGLEVEL_DEBUG,
+ side_field_list(
+ side_field(SIDE_TYPE_U32, "abc"),
+ side_field(SIDE_TYPE_S64, "def"),
+ side_field(SIDE_TYPE_DYNAMIC, "dynamic"),
+ )
+);
+
+static
+void test_fields(void)
+{
+ uint32_t uw = 42;
+ int64_t sdw = -500;
+
+ my_provider_event.enabled = 1;
+ side_event(&my_provider_event, side_arg_list(side_arg_u32(uw), side_arg_s64(sdw), side_arg_string("zzz")));
+}
+
+static side_define_event(my_provider_event2, "myprovider", "myevent2", SIDE_LOGLEVEL_DEBUG,
+ side_field_list(
+ side_field_struct("structfield",
+ side_field_list(
+ side_field(SIDE_TYPE_U32, "x"),
+ side_field(SIDE_TYPE_S64, "y"),
+ )
+ ),
+ side_field(SIDE_TYPE_U8, "z"),
+ )
+);
+
+static
+void test_struct(void)
+{
+ my_provider_event2.enabled = 1;
+ side_event_cond(&my_provider_event2) {
+ side_arg_define_vec(mystruct, side_arg_list(side_arg_u32(21), side_arg_s64(22)));
+ side_event_call(&my_provider_event2, side_arg_list(side_arg_struct(&mystruct), side_arg_u8(55)));
+ }
+}
+
+static side_define_event(my_provider_event_array, "myprovider", "myarray", SIDE_LOGLEVEL_DEBUG,
+ side_field_list(
+ side_field_array("arr", side_array_elem(SIDE_TYPE_U32), 3),
+ side_field(SIDE_TYPE_S64, "v"),
+ )
+);
+
+static
+void test_array(void)
+{
+ my_provider_event_array.enabled = 1;
+ side_event_cond(&my_provider_event_array) {
+ side_arg_define_vec(myarray, side_arg_list(side_arg_u32(1), side_arg_u32(2), side_arg_u32(3)));
+ side_event_call(&my_provider_event_array, side_arg_list(side_arg_array(&myarray), side_arg_s64(42)));
+ }
+}
+
+static side_define_event(my_provider_event_vla, "myprovider", "myvla", SIDE_LOGLEVEL_DEBUG,
+ side_field_list(
+ side_field_vla("vla", side_vla_elem(SIDE_TYPE_U32)),
+ side_field(SIDE_TYPE_S64, "v"),
+ )
+);
+
+static
+void test_vla(void)
+{
+ my_provider_event_vla.enabled = 1;
+ side_event_cond(&my_provider_event_vla) {
+ side_arg_define_vec(myvla, side_arg_list(side_arg_u32(1), side_arg_u32(2), side_arg_u32(3)));
+ side_event_call(&my_provider_event_vla, side_arg_list(side_arg_vla(&myvla), side_arg_s64(42)));
+ }
+}
+
+struct app_visitor_ctx {
+ const uint32_t *ptr;
+ int init_pos;
+ int current_pos;
+ int end_pos;
+};
+
+enum side_visitor_status test_visitor_begin(void *_ctx)
+{
+ struct app_visitor_ctx *ctx = (struct app_visitor_ctx *) _ctx;
+ ctx->current_pos = ctx->init_pos;
+ return SIDE_VISITOR_STATUS_OK;
+}
+
+enum side_visitor_status test_visitor_end(void *_ctx)
+{
+ return SIDE_VISITOR_STATUS_OK;
+}
+
+enum side_visitor_status test_visitor_get_next(void *_ctx, struct side_arg_vec *sav_elem)
+{
+ struct app_visitor_ctx *ctx = (struct app_visitor_ctx *) _ctx;
+
+ if (ctx->current_pos >= ctx->end_pos)
+ return SIDE_VISITOR_STATUS_END;
+ sav_elem->type = SIDE_TYPE_U32;
+ sav_elem->u.side_u32 = ctx->ptr[ctx->current_pos++];
+ return SIDE_VISITOR_STATUS_OK;
+}
+
+static uint32_t testarray[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+
+static side_define_event(my_provider_event_vla_visitor, "myprovider", "myvlavisit", SIDE_LOGLEVEL_DEBUG,
+ side_field_list(
+ side_field_vla_visitor("vlavisit", side_vla_visitor_elem(SIDE_TYPE_U32),
+ test_visitor_begin, test_visitor_end, test_visitor_get_next),
+ side_field(SIDE_TYPE_S64, "v"),
+ )
+);
+
+static
+void test_vla_visitor(void)
+{
+ my_provider_event_vla_visitor.enabled = 1;
+ side_event_cond(&my_provider_event_vla_visitor) {
+ struct app_visitor_ctx ctx = {
+ .ptr = testarray,
+ .init_pos = 0,
+ .current_pos = 0,
+ .end_pos = SIDE_ARRAY_SIZE(testarray),
+ };
+ side_event_call(&my_provider_event_vla_visitor, side_arg_list(side_arg_vla_visitor(&ctx), side_arg_s64(42)));
+ }
+}
+
+int main()
+{
+ test_fields();
+ test_struct();
+ test_array();
+ test_vla();
+ test_vla_visitor();
+ return 0;
+}
--- /dev/null
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2022 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <side/trace.h>
+
+static
+void tracer_print_struct(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc);
+static
+void tracer_print_array(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc);
+static
+void tracer_print_vla(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc);
+static
+void tracer_print_vla_visitor(const struct side_type_description *type_desc, void *ctx);
+
+static
+void tracer_print_type(const struct side_type_description *type_desc, const struct side_arg_vec *item)
+{
+ if (type_desc->type != SIDE_TYPE_DYNAMIC && type_desc->type != item->type) {
+ printf("ERROR: type mismatch between description and arguments\n");
+ abort();
+ }
+
+ switch (item->type) {
+ case SIDE_TYPE_U8:
+ printf("%" PRIu8, item->u.side_u8);
+ break;
+ case SIDE_TYPE_U16:
+ printf("%" PRIu16, item->u.side_u16);
+ break;
+ case SIDE_TYPE_U32:
+ printf("%" PRIu32, item->u.side_u32);
+ break;
+ case SIDE_TYPE_U64:
+ printf("%" PRIu64, item->u.side_u64);
+ break;
+ case SIDE_TYPE_S8:
+ printf("%" PRId8, item->u.side_s8);
+ break;
+ case SIDE_TYPE_S16:
+ printf("%" PRId16, item->u.side_s16);
+ break;
+ case SIDE_TYPE_S32:
+ printf("%" PRId32, item->u.side_s32);
+ break;
+ case SIDE_TYPE_S64:
+ printf("%" PRId64, item->u.side_s64);
+ break;
+ case SIDE_TYPE_STRING:
+ printf("%s", item->u.string);
+ break;
+ case SIDE_TYPE_STRUCT:
+ tracer_print_struct(type_desc, item->u.side_struct);
+ break;
+ case SIDE_TYPE_ARRAY:
+ tracer_print_array(type_desc, item->u.side_array);
+ break;
+ case SIDE_TYPE_VLA:
+ tracer_print_vla(type_desc, item->u.side_vla);
+ break;
+ case SIDE_TYPE_VLA_VISITOR:
+ tracer_print_vla_visitor(type_desc, item->u.side_vla_visitor_ctx);
+ break;
+ default:
+ printf("<UNKNOWN TYPE>");
+ abort();
+ }
+}
+
+static
+void tracer_print_field(const struct side_event_field *item_desc, const struct side_arg_vec *item)
+{
+ printf("(\"%s\", ", item_desc->field_name);
+ tracer_print_type(&item_desc->side_type, item);
+ printf(")");
+}
+
+static
+void tracer_print_struct(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc)
+{
+ const struct side_arg_vec *sav = sav_desc->sav;
+ uint32_t side_sav_len = sav_desc->len;
+ int i;
+
+ if (type_desc->u.side_struct.nr_fields != side_sav_len) {
+ printf("ERROR: number of fields mismatch between description and arguments of structure\n");
+ abort();
+ }
+ printf("{ ");
+ for (i = 0; i < side_sav_len; i++) {
+ printf("%s", i ? ", " : "");
+ tracer_print_field(&type_desc->u.side_struct.fields[i], &sav[i]);
+ }
+ printf(" }");
+}
+
+static
+void tracer_print_array(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc)
+{
+ const struct side_arg_vec *sav = sav_desc->sav;
+ uint32_t side_sav_len = sav_desc->len;
+ int i;
+
+ if (type_desc->u.side_array.length != side_sav_len) {
+ printf("ERROR: length mismatch between description and arguments of array\n");
+ abort();
+ }
+ printf("[ ");
+ for (i = 0; i < side_sav_len; i++) {
+ printf("%s", i ? ", " : "");
+ tracer_print_type(type_desc->u.side_array.elem_type, &sav[i]);
+ }
+ printf(" ]");
+}
+
+static
+void tracer_print_vla(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc)
+{
+ const struct side_arg_vec *sav = sav_desc->sav;
+ uint32_t side_sav_len = sav_desc->len;
+ int i;
+
+ printf("[ ");
+ for (i = 0; i < side_sav_len; i++) {
+ printf("%s", i ? ", " : "");
+ tracer_print_type(type_desc->u.side_vla.elem_type, &sav[i]);
+ }
+ printf(" ]");
+}
+
+static
+void tracer_print_vla_visitor(const struct side_type_description *type_desc, void *ctx)
+{
+ enum side_visitor_status status;
+ int i;
+
+ status = type_desc->u.side_vla_visitor.begin(ctx);
+ if (status != SIDE_VISITOR_STATUS_OK) {
+ printf("ERROR: Visitor error\n");
+ abort();
+ }
+
+ printf("[ ");
+ status = SIDE_VISITOR_STATUS_OK;
+ for (i = 0; status == SIDE_VISITOR_STATUS_OK; i++) {
+ struct side_arg_vec sav_elem;
+
+ status = type_desc->u.side_vla_visitor.get_next(ctx, &sav_elem);
+ switch (status) {
+ case SIDE_VISITOR_STATUS_OK:
+ break;
+ case SIDE_VISITOR_STATUS_ERROR:
+ printf("ERROR: Visitor error\n");
+ abort();
+ case SIDE_VISITOR_STATUS_END:
+ continue;
+ }
+ printf("%s", i ? ", " : "");
+ tracer_print_type(type_desc->u.side_vla_visitor.elem_type, &sav_elem);
+ }
+ printf(" ]");
+ if (type_desc->u.side_vla_visitor.end) {
+ status = type_desc->u.side_vla_visitor.end(ctx);
+ if (status != SIDE_VISITOR_STATUS_OK) {
+ printf("ERROR: Visitor error\n");
+ abort();
+ }
+ }
+}
+
+void tracer_call(const struct side_event_description *desc, const struct side_arg_vec_description *sav_desc)
+{
+ const struct side_arg_vec *sav = sav_desc->sav;
+ uint32_t side_sav_len = sav_desc->len;
+ int i;
+
+ printf("provider: %s, event: %s, ", desc->provider_name, desc->event_name);
+ if (desc->nr_fields != side_sav_len) {
+ printf("ERROR: number of fields mismatch between description and arguments\n");
+ abort();
+ }
+ for (i = 0; i < side_sav_len; i++) {
+ printf("%s", i ? ", " : "");
+ tracer_print_field(&desc->fields[i], &sav[i]);
+ }
+ printf("\n");
+}
--- /dev/null
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2022 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#ifndef _TRACER_H
+#define _TRACER_H
+
+#include <side/trace.h>
+
+void tracer_call(const struct side_event_description *desc, const struct side_arg_vec_description *sav_desc);
+
+#endif
+++ /dev/null
-// SPDX-License-Identifier: MIT
-/*
- * Copyright 2022 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#include <stdint.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-/* SIDE stands for "Static Instrumentation Dynamically Enabled" */
-
-/* Helper macros */
-
-#define SIDE_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
-
-/*
- * Compound literals with static storage are needed by SIDE
- * instrumentation.
- * Compound literals are part of the C99 and C11 standards, but not
- * part of the C++ standards. They are supported by most C++ compilers
- * though.
- *
- * Example use:
- * static struct mystruct *var = LTTNG_UST_COMPOUND_LITERAL(struct mystruct, { 1, 2, 3 });
- */
-#define SIDE_COMPOUND_LITERAL(type, ...) (type[]) { __VA_ARGS__ }
-
-#define side_likely(x) __builtin_expect(!!(x), 1)
-#define side_unlikely(x) __builtin_expect(!!(x), 0)
-
-struct side_arg_vec;
-struct side_type_description;
-struct side_event_field;
-
-enum side_type {
- SIDE_TYPE_U8,
- SIDE_TYPE_U16,
- SIDE_TYPE_U32,
- SIDE_TYPE_U64,
- SIDE_TYPE_S8,
- SIDE_TYPE_S16,
- SIDE_TYPE_S32,
- SIDE_TYPE_S64,
- SIDE_TYPE_STRING,
- SIDE_TYPE_DYNAMIC,
- SIDE_TYPE_STRUCT,
- SIDE_TYPE_ARRAY,
- SIDE_TYPE_VLA,
- SIDE_TYPE_VLA_VISITOR,
- //TODO:
- //specialized array and vla for fixed-size integers (optimization)
- //variants (discriminated unions)
-};
-
-enum side_loglevel {
- SIDE_LOGLEVEL_EMERG = 0,
- SIDE_LOGLEVEL_ALERT = 1,
- SIDE_LOGLEVEL_CRIT = 2,
- SIDE_LOGLEVEL_ERR = 3,
- SIDE_LOGLEVEL_WARNING = 4,
- SIDE_LOGLEVEL_NOTICE = 5,
- SIDE_LOGLEVEL_INFO = 6,
- SIDE_LOGLEVEL_DEBUG = 7,
-};
-
-enum side_visitor_status {
- SIDE_VISITOR_STATUS_ERROR = -1,
- SIDE_VISITOR_STATUS_OK = 0,
- SIDE_VISITOR_STATUS_END = 1,
-};
-
-typedef enum side_visitor_status (*side_visitor_begin)(void *ctx);
-typedef enum side_visitor_status (*side_visitor_end)(void *ctx);
-typedef enum side_visitor_status (*side_visitor_get_next)(void *ctx, struct side_arg_vec *sav_elem);
-
-struct side_type_description {
- enum side_type type;
- union {
- struct {
- uint32_t nr_fields;
- const struct side_event_field *fields;
- } side_struct;
- struct {
- uint32_t length;
- const struct side_type_description *elem_type;
- } side_array;
- struct {
- const struct side_type_description *elem_type;
- } side_vla;
- struct {
- const struct side_type_description *elem_type;
- side_visitor_begin begin;
- side_visitor_end end;
- side_visitor_get_next get_next;
- } side_vla_visitor;
- } u;
-};
-
-struct side_event_field {
- const char *field_name;
- struct side_type_description side_type;
-};
-
-struct side_event_description {
- uint32_t version;
- uint32_t enabled;
- uint32_t loglevel; /* enum side_loglevel */
- uint32_t nr_fields;
- const char *provider_name;
- const char *event_name;
- const struct side_event_field *fields;
-};
-
-struct side_arg_vec {
- uint32_t type; /* enum side_type */
- union {
- uint8_t side_u8;
- uint16_t side_u16;
- uint32_t side_u32;
- uint64_t side_u64;
- int8_t side_s8;
- int16_t side_s16;
- int32_t side_s32;
- int64_t side_s64;
- const char *string;
- const struct side_arg_vec_description *side_struct;
- const struct side_arg_vec_description *side_array;
- const struct side_arg_vec_description *side_vla;
- void *side_vla_visitor_ctx;
- } u;
-};
-
-struct side_arg_vec_description {
- const struct side_arg_vec *sav;
- uint32_t len;
-};
-
-/* Tracer example */
-
-static
-void tracer_print_struct(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc);
-static
-void tracer_print_array(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc);
-static
-void tracer_print_vla(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc);
-static
-void tracer_print_vla_visitor(const struct side_type_description *type_desc, void *ctx);
-
-static
-void tracer_print_type(const struct side_type_description *type_desc, const struct side_arg_vec *item)
-{
- if (type_desc->type != SIDE_TYPE_DYNAMIC && type_desc->type != item->type) {
- printf("ERROR: type mismatch between description and arguments\n");
- abort();
- }
-
- switch (item->type) {
- case SIDE_TYPE_U8:
- printf("%" PRIu8, item->u.side_u8);
- break;
- case SIDE_TYPE_U16:
- printf("%" PRIu16, item->u.side_u16);
- break;
- case SIDE_TYPE_U32:
- printf("%" PRIu32, item->u.side_u32);
- break;
- case SIDE_TYPE_U64:
- printf("%" PRIu64, item->u.side_u64);
- break;
- case SIDE_TYPE_S8:
- printf("%" PRId8, item->u.side_s8);
- break;
- case SIDE_TYPE_S16:
- printf("%" PRId16, item->u.side_s16);
- break;
- case SIDE_TYPE_S32:
- printf("%" PRId32, item->u.side_s32);
- break;
- case SIDE_TYPE_S64:
- printf("%" PRId64, item->u.side_s64);
- break;
- case SIDE_TYPE_STRING:
- printf("%s", item->u.string);
- break;
- case SIDE_TYPE_STRUCT:
- tracer_print_struct(type_desc, item->u.side_struct);
- break;
- case SIDE_TYPE_ARRAY:
- tracer_print_array(type_desc, item->u.side_array);
- break;
- case SIDE_TYPE_VLA:
- tracer_print_vla(type_desc, item->u.side_vla);
- break;
- case SIDE_TYPE_VLA_VISITOR:
- tracer_print_vla_visitor(type_desc, item->u.side_vla_visitor_ctx);
- break;
- default:
- printf("<UNKNOWN TYPE>");
- abort();
- }
-}
-
-static
-void tracer_print_field(const struct side_event_field *item_desc, const struct side_arg_vec *item)
-{
- printf("(\"%s\", ", item_desc->field_name);
- tracer_print_type(&item_desc->side_type, item);
- printf(")");
-}
-
-static
-void tracer_print_struct(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc)
-{
- const struct side_arg_vec *sav = sav_desc->sav;
- uint32_t side_sav_len = sav_desc->len;
- int i;
-
- if (type_desc->u.side_struct.nr_fields != side_sav_len) {
- printf("ERROR: number of fields mismatch between description and arguments of structure\n");
- abort();
- }
- printf("{ ");
- for (i = 0; i < side_sav_len; i++) {
- printf("%s", i ? ", " : "");
- tracer_print_field(&type_desc->u.side_struct.fields[i], &sav[i]);
- }
- printf(" }");
-}
-
-static
-void tracer_print_array(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc)
-{
- const struct side_arg_vec *sav = sav_desc->sav;
- uint32_t side_sav_len = sav_desc->len;
- int i;
-
- if (type_desc->u.side_array.length != side_sav_len) {
- printf("ERROR: length mismatch between description and arguments of array\n");
- abort();
- }
- printf("[ ");
- for (i = 0; i < side_sav_len; i++) {
- printf("%s", i ? ", " : "");
- tracer_print_type(type_desc->u.side_array.elem_type, &sav[i]);
- }
- printf(" ]");
-}
-
-static
-void tracer_print_vla(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc)
-{
- const struct side_arg_vec *sav = sav_desc->sav;
- uint32_t side_sav_len = sav_desc->len;
- int i;
-
- printf("[ ");
- for (i = 0; i < side_sav_len; i++) {
- printf("%s", i ? ", " : "");
- tracer_print_type(type_desc->u.side_vla.elem_type, &sav[i]);
- }
- printf(" ]");
-}
-
-static
-void tracer_print_vla_visitor(const struct side_type_description *type_desc, void *ctx)
-{
- enum side_visitor_status status;
- int i;
-
- status = type_desc->u.side_vla_visitor.begin(ctx);
- if (status != SIDE_VISITOR_STATUS_OK) {
- printf("ERROR: Visitor error\n");
- abort();
- }
-
- printf("[ ");
- status = SIDE_VISITOR_STATUS_OK;
- for (i = 0; status == SIDE_VISITOR_STATUS_OK; i++) {
- struct side_arg_vec sav_elem;
-
- status = type_desc->u.side_vla_visitor.get_next(ctx, &sav_elem);
- switch (status) {
- case SIDE_VISITOR_STATUS_OK:
- break;
- case SIDE_VISITOR_STATUS_ERROR:
- printf("ERROR: Visitor error\n");
- abort();
- case SIDE_VISITOR_STATUS_END:
- continue;
- }
- printf("%s", i ? ", " : "");
- tracer_print_type(type_desc->u.side_vla_visitor.elem_type, &sav_elem);
- }
- printf(" ]");
- if (type_desc->u.side_vla_visitor.end) {
- status = type_desc->u.side_vla_visitor.end(ctx);
- if (status != SIDE_VISITOR_STATUS_OK) {
- printf("ERROR: Visitor error\n");
- abort();
- }
- }
-}
-
-__attribute__((noinline))
-void tracer_call(const struct side_event_description *desc, const struct side_arg_vec_description *sav_desc)
-{
- const struct side_arg_vec *sav = sav_desc->sav;
- uint32_t side_sav_len = sav_desc->len;
- int i;
-
- printf("provider: %s, event: %s, ", desc->provider_name, desc->event_name);
- if (desc->nr_fields != side_sav_len) {
- printf("ERROR: number of fields mismatch between description and arguments\n");
- abort();
- }
- for (i = 0; i < side_sav_len; i++) {
- printf("%s", i ? ", " : "");
- tracer_print_field(&desc->fields[i], &sav[i]);
- }
- printf("\n");
-}
-
-#define SIDE_PARAM(...) __VA_ARGS__
-
-#define side_type_decl(_type) { .type = _type }
-#define side_field(_type, _name) { .field_name = _name, .side_type = side_type_decl(_type) }
-
-#define side_type_struct_decl(_fields) \
- { \
- .type = SIDE_TYPE_STRUCT, \
- .u = { \
- .side_struct = { \
- .nr_fields = SIDE_ARRAY_SIZE(SIDE_PARAM(_fields)), \
- .fields = _fields, \
- }, \
- }, \
- }
-#define side_field_struct(_name, _fields) \
- { \
- .field_name = _name, \
- .side_type = side_type_struct_decl(SIDE_PARAM(_fields)), \
- }
-
-#define side_type_array_decl(_elem_type, _length) \
- { \
- .type = SIDE_TYPE_ARRAY, \
- .u = { \
- .side_array = { \
- .length = _length, \
- .elem_type = _elem_type, \
- }, \
- }, \
- }
-#define side_field_array(_name, _elem_type, _length) \
- { \
- .field_name = _name, \
- .side_type = side_type_array_decl(_elem_type, _length), \
- }
-
-#define side_type_vla_decl(_elem_type) \
- { \
- .type = SIDE_TYPE_VLA, \
- .u = { \
- .side_vla = { \
- .elem_type = _elem_type, \
- }, \
- }, \
- }
-#define side_field_vla(_name, _elem_type) \
- { \
- .field_name = _name, \
- .side_type = side_type_vla_decl(_elem_type), \
- }
-
-#define side_type_vla_visitor_decl(_elem_type, _begin, _end, _get_next) \
- { \
- .type = SIDE_TYPE_VLA_VISITOR, \
- .u = { \
- .side_vla_visitor = { \
- .elem_type = _elem_type, \
- .begin = _begin, \
- .end = _end, \
- .get_next = _get_next, \
- }, \
- }, \
- }
-#define side_field_vla_visitor(_name, _elem_type, _begin, _end, _get_next) \
- { \
- .field_name = _name, \
- .side_type = side_type_vla_visitor_decl(_elem_type, _begin, _end, _get_next), \
- }
-
-#define side_vla_elem(...) \
- SIDE_COMPOUND_LITERAL(const struct side_type_description, side_type_decl(__VA_ARGS__))
-
-#define side_vla_visitor_elem(...) \
- SIDE_COMPOUND_LITERAL(const struct side_type_description, side_type_decl(__VA_ARGS__))
-
-#define side_array_elem(...) \
- SIDE_COMPOUND_LITERAL(const struct side_type_description, side_type_decl(__VA_ARGS__))
-
-#define side_field_list(...) \
- SIDE_COMPOUND_LITERAL(const struct side_event_field, __VA_ARGS__)
-
-#define side_arg_u8(val) { .type = SIDE_TYPE_U8, .u = { .side_u8 = (val) } }
-#define side_arg_u16(val) { .type = SIDE_TYPE_U16, .u = { .side_u16 = (val) } }
-#define side_arg_u32(val) { .type = SIDE_TYPE_U32, .u = { .side_u32 = (val) } }
-#define side_arg_u64(val) { .type = SIDE_TYPE_U64, .u = { .side_u64 = (val) } }
-#define side_arg_s8(val) { .type = SIDE_TYPE_S8, .u = { .side_s8 = (val) } }
-#define side_arg_s16(val) { .type = SIDE_TYPE_S16, .u = { .side_s16 = (val) } }
-#define side_arg_s32(val) { .type = SIDE_TYPE_S32, .u = { .side_s32 = (val) } }
-#define side_arg_s64(val) { .type = SIDE_TYPE_S64, .u = { .side_s64 = (val) } }
-#define side_arg_string(val) { .type = SIDE_TYPE_STRING, .u = { .string = (val) } }
-#define side_arg_struct(_side_type) { .type = SIDE_TYPE_STRUCT, .u = { .side_struct = (_side_type) } }
-#define side_arg_array(_side_type) { .type = SIDE_TYPE_ARRAY, .u = { .side_array = (_side_type) } }
-#define side_arg_vla(_side_type) { .type = SIDE_TYPE_VLA, .u = { .side_vla = (_side_type) } }
-#define side_arg_vla_visitor(_ctx) { .type = SIDE_TYPE_VLA_VISITOR, .u = { .side_vla_visitor_ctx = (_ctx) } }
-
-#define side_arg_define_vec(_identifier, _sav) \
- const struct side_arg_vec _identifier##_vec[] = { _sav }; \
- const struct side_arg_vec_description _identifier = { \
- .sav = _identifier##_vec, \
- .len = SIDE_ARRAY_SIZE(_identifier##_vec), \
- }
-
-#define side_arg_list(...) __VA_ARGS__
-
-#define side_event_cond(desc) if (side_unlikely((desc)->enabled))
-#define side_event_call(desc, _sav) \
- { \
- const struct side_arg_vec side_sav[] = { _sav }; \
- const struct side_arg_vec_description sav_desc = { \
- .sav = side_sav, \
- .len = SIDE_ARRAY_SIZE(side_sav), \
- }; \
- tracer_call(desc, &sav_desc); \
- }
-
-#define side_event(desc, sav) \
- side_event_cond(desc) \
- side_event_call(desc, SIDE_PARAM(sav)); \
-
-#define side_define_event(_identifier, _provider, _event, _loglevel, _fields) \
- struct side_event_description _identifier = { \
- .version = 0, \
- .enabled = 0, \
- .loglevel = _loglevel, \
- .nr_fields = SIDE_ARRAY_SIZE(SIDE_PARAM(_fields)), \
- .provider_name = _provider, \
- .event_name = _event, \
- .fields = _fields, \
- }
-
-#define side_declare_event(_identifier) \
- struct side_event_description _identifier
-
-/* User code example */
-
-static side_define_event(my_provider_event, "myprovider", "myevent", SIDE_LOGLEVEL_DEBUG,
- side_field_list(
- side_field(SIDE_TYPE_U32, "abc"),
- side_field(SIDE_TYPE_S64, "def"),
- side_field(SIDE_TYPE_DYNAMIC, "dynamic"),
- )
-);
-
-static
-void test_fields(void)
-{
- uint32_t uw = 42;
- int64_t sdw = -500;
-
- my_provider_event.enabled = 1;
- side_event(&my_provider_event, side_arg_list(side_arg_u32(uw), side_arg_s64(sdw), side_arg_string("zzz")));
-}
-
-static side_define_event(my_provider_event2, "myprovider", "myevent2", SIDE_LOGLEVEL_DEBUG,
- side_field_list(
- side_field_struct("structfield",
- side_field_list(
- side_field(SIDE_TYPE_U32, "x"),
- side_field(SIDE_TYPE_S64, "y"),
- )
- ),
- side_field(SIDE_TYPE_U8, "z"),
- )
-);
-
-static
-void test_struct(void)
-{
- my_provider_event2.enabled = 1;
- side_event_cond(&my_provider_event2) {
- side_arg_define_vec(mystruct, side_arg_list(side_arg_u32(21), side_arg_s64(22)));
- side_event_call(&my_provider_event2, side_arg_list(side_arg_struct(&mystruct), side_arg_u8(55)));
- }
-}
-
-static side_define_event(my_provider_event_array, "myprovider", "myarray", SIDE_LOGLEVEL_DEBUG,
- side_field_list(
- side_field_array("arr", side_array_elem(SIDE_TYPE_U32), 3),
- side_field(SIDE_TYPE_S64, "v"),
- )
-);
-
-static
-void test_array(void)
-{
- my_provider_event_array.enabled = 1;
- side_event_cond(&my_provider_event_array) {
- side_arg_define_vec(myarray, side_arg_list(side_arg_u32(1), side_arg_u32(2), side_arg_u32(3)));
- side_event_call(&my_provider_event_array, side_arg_list(side_arg_array(&myarray), side_arg_s64(42)));
- }
-}
-
-static side_define_event(my_provider_event_vla, "myprovider", "myvla", SIDE_LOGLEVEL_DEBUG,
- side_field_list(
- side_field_vla("vla", side_vla_elem(SIDE_TYPE_U32)),
- side_field(SIDE_TYPE_S64, "v"),
- )
-);
-
-static
-void test_vla(void)
-{
- my_provider_event_vla.enabled = 1;
- side_event_cond(&my_provider_event_vla) {
- side_arg_define_vec(myvla, side_arg_list(side_arg_u32(1), side_arg_u32(2), side_arg_u32(3)));
- side_event_call(&my_provider_event_vla, side_arg_list(side_arg_vla(&myvla), side_arg_s64(42)));
- }
-}
-
-struct app_visitor_ctx {
- const uint32_t *ptr;
- int init_pos;
- int current_pos;
- int end_pos;
-};
-
-enum side_visitor_status test_visitor_begin(void *_ctx)
-{
- struct app_visitor_ctx *ctx = (struct app_visitor_ctx *) _ctx;
- ctx->current_pos = ctx->init_pos;
- return SIDE_VISITOR_STATUS_OK;
-}
-
-enum side_visitor_status test_visitor_end(void *_ctx)
-{
- return SIDE_VISITOR_STATUS_OK;
-}
-
-enum side_visitor_status test_visitor_get_next(void *_ctx, struct side_arg_vec *sav_elem)
-{
- struct app_visitor_ctx *ctx = (struct app_visitor_ctx *) _ctx;
-
- if (ctx->current_pos >= ctx->end_pos)
- return SIDE_VISITOR_STATUS_END;
- sav_elem->type = SIDE_TYPE_U32;
- sav_elem->u.side_u32 = ctx->ptr[ctx->current_pos++];
- return SIDE_VISITOR_STATUS_OK;
-}
-
-static uint32_t testarray[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
-
-static side_define_event(my_provider_event_vla_visitor, "myprovider", "myvlavisit", SIDE_LOGLEVEL_DEBUG,
- side_field_list(
- side_field_vla_visitor("vlavisit", side_vla_visitor_elem(SIDE_TYPE_U32),
- test_visitor_begin, test_visitor_end, test_visitor_get_next),
- side_field(SIDE_TYPE_S64, "v"),
- )
-);
-
-static
-void test_vla_visitor(void)
-{
- my_provider_event_vla_visitor.enabled = 1;
- side_event_cond(&my_provider_event_vla_visitor) {
- struct app_visitor_ctx ctx = {
- .ptr = testarray,
- .init_pos = 0,
- .current_pos = 0,
- .end_pos = SIDE_ARRAY_SIZE(testarray),
- };
- side_event_call(&my_provider_event_vla_visitor, side_arg_list(side_arg_vla_visitor(&ctx), side_arg_s64(42)));
- }
-}
-
-int main()
-{
- test_fields();
- test_struct();
- test_array();
- test_vla();
- test_vla_visitor();
- return 0;
-}