Initial code
[libside.git] / test.c
CommitLineData
e5540b2a
MD
1// SPDX-License-Identifier: MIT
2/*
3 * Copyright 2022 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 */
5
6#include <stdint.h>
7#include <inttypes.h>
8#include <stdlib.h>
9#include <stdio.h>
10
11/* SIDE stands for "Static Instrumentation Dynamically Enabled" */
12
13/* Helper macros */
14
15#define SIDE_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
16
17/*
18 * Compound literals with static storage are needed by SIDE
19 * instrumentation.
20 * Compound literals are part of the C99 and C11 standards, but not
21 * part of the C++ standards. They are supported by most C++ compilers
22 * though.
23 *
24 * Example use:
25 * static struct mystruct *var = LTTNG_UST_COMPOUND_LITERAL(struct mystruct, { 1, 2, 3 });
26 */
27#define SIDE_COMPOUND_LITERAL(type, ...) (type[]) { __VA_ARGS__ }
28
29#define side_likely(x) __builtin_expect(!!(x), 1)
30#define side_unlikely(x) __builtin_expect(!!(x), 0)
31
32struct side_arg_vec;
33struct side_type_description;
34struct side_event_field;
35
36enum side_type {
37 SIDE_TYPE_U8,
38 SIDE_TYPE_U16,
39 SIDE_TYPE_U32,
40 SIDE_TYPE_U64,
41 SIDE_TYPE_S8,
42 SIDE_TYPE_S16,
43 SIDE_TYPE_S32,
44 SIDE_TYPE_S64,
45 SIDE_TYPE_STRING,
46 SIDE_TYPE_DYNAMIC,
47 SIDE_TYPE_STRUCT,
48 SIDE_TYPE_ARRAY,
49 SIDE_TYPE_VLA,
50 SIDE_TYPE_VLA_VISITOR,
51 //TODO:
52 //specialized array and vla for fixed-size integers (optimization)
53 //variants (discriminated unions)
54};
55
56enum side_loglevel {
57 SIDE_LOGLEVEL_EMERG = 0,
58 SIDE_LOGLEVEL_ALERT = 1,
59 SIDE_LOGLEVEL_CRIT = 2,
60 SIDE_LOGLEVEL_ERR = 3,
61 SIDE_LOGLEVEL_WARNING = 4,
62 SIDE_LOGLEVEL_NOTICE = 5,
63 SIDE_LOGLEVEL_INFO = 6,
64 SIDE_LOGLEVEL_DEBUG = 7,
65};
66
67enum side_visitor_status {
68 SIDE_VISITOR_STATUS_ERROR = -1,
69 SIDE_VISITOR_STATUS_OK = 0,
70 SIDE_VISITOR_STATUS_END = 1,
71};
72
73typedef enum side_visitor_status (*side_visitor_begin)(void *ctx);
74typedef enum side_visitor_status (*side_visitor_end)(void *ctx);
75typedef enum side_visitor_status (*side_visitor_get_next)(void *ctx, struct side_arg_vec *sav_elem);
76
77struct side_type_description {
78 enum side_type type;
79 union {
80 struct {
81 uint32_t nr_fields;
82 const struct side_event_field *fields;
83 } side_struct;
84 struct {
85 uint32_t length;
86 const struct side_type_description *elem_type;
87 } side_array;
88 struct {
89 const struct side_type_description *elem_type;
90 } side_vla;
91 struct {
92 const struct side_type_description *elem_type;
93 side_visitor_begin begin;
94 side_visitor_end end;
95 side_visitor_get_next get_next;
96 } side_vla_visitor;
97 } u;
98};
99
100struct side_event_field {
101 const char *field_name;
102 struct side_type_description side_type;
103};
104
105struct side_event_description {
106 uint32_t version;
107 uint32_t enabled;
108 uint32_t loglevel; /* enum side_loglevel */
109 uint32_t nr_fields;
110 const char *provider_name;
111 const char *event_name;
112 const struct side_event_field *fields;
113};
114
115struct side_arg_vec {
116 uint32_t type; /* enum side_type */
117 union {
118 uint8_t side_u8;
119 uint16_t side_u16;
120 uint32_t side_u32;
121 uint64_t side_u64;
122 int8_t side_s8;
123 int16_t side_s16;
124 int32_t side_s32;
125 int64_t side_s64;
126 const char *string;
127 const struct side_arg_vec_description *side_struct;
128 const struct side_arg_vec_description *side_array;
129 const struct side_arg_vec_description *side_vla;
130 void *side_vla_visitor_ctx;
131 } u;
132};
133
134struct side_arg_vec_description {
135 const struct side_arg_vec *sav;
136 uint32_t len;
137};
138
139/* Tracer example */
140
141static
142void tracer_print_struct(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc);
143static
144void tracer_print_array(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc);
145static
146void tracer_print_vla(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc);
147static
148void tracer_print_vla_visitor(const struct side_type_description *type_desc, void *ctx);
149
150static
151void tracer_print_type(const struct side_type_description *type_desc, const struct side_arg_vec *item)
152{
153 if (type_desc->type != SIDE_TYPE_DYNAMIC && type_desc->type != item->type) {
154 printf("ERROR: type mismatch between description and arguments\n");
155 abort();
156 }
157
158 switch (item->type) {
159 case SIDE_TYPE_U8:
160 printf("%" PRIu8, item->u.side_u8);
161 break;
162 case SIDE_TYPE_U16:
163 printf("%" PRIu16, item->u.side_u16);
164 break;
165 case SIDE_TYPE_U32:
166 printf("%" PRIu32, item->u.side_u32);
167 break;
168 case SIDE_TYPE_U64:
169 printf("%" PRIu64, item->u.side_u64);
170 break;
171 case SIDE_TYPE_S8:
172 printf("%" PRId8, item->u.side_s8);
173 break;
174 case SIDE_TYPE_S16:
175 printf("%" PRId16, item->u.side_s16);
176 break;
177 case SIDE_TYPE_S32:
178 printf("%" PRId32, item->u.side_s32);
179 break;
180 case SIDE_TYPE_S64:
181 printf("%" PRId64, item->u.side_s64);
182 break;
183 case SIDE_TYPE_STRING:
184 printf("%s", item->u.string);
185 break;
186 case SIDE_TYPE_STRUCT:
187 tracer_print_struct(type_desc, item->u.side_struct);
188 break;
189 case SIDE_TYPE_ARRAY:
190 tracer_print_array(type_desc, item->u.side_array);
191 break;
192 case SIDE_TYPE_VLA:
193 tracer_print_vla(type_desc, item->u.side_vla);
194 break;
195 case SIDE_TYPE_VLA_VISITOR:
196 tracer_print_vla_visitor(type_desc, item->u.side_vla_visitor_ctx);
197 break;
198 default:
199 printf("<UNKNOWN TYPE>");
200 abort();
201 }
202}
203
204static
205void tracer_print_field(const struct side_event_field *item_desc, const struct side_arg_vec *item)
206{
207 printf("(\"%s\", ", item_desc->field_name);
208 tracer_print_type(&item_desc->side_type, item);
209 printf(")");
210}
211
212static
213void tracer_print_struct(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc)
214{
215 const struct side_arg_vec *sav = sav_desc->sav;
216 uint32_t side_sav_len = sav_desc->len;
217 int i;
218
219 if (type_desc->u.side_struct.nr_fields != side_sav_len) {
220 printf("ERROR: number of fields mismatch between description and arguments of structure\n");
221 abort();
222 }
223 printf("{ ");
224 for (i = 0; i < side_sav_len; i++) {
225 printf("%s", i ? ", " : "");
226 tracer_print_field(&type_desc->u.side_struct.fields[i], &sav[i]);
227 }
228 printf(" }");
229}
230
231static
232void tracer_print_array(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc)
233{
234 const struct side_arg_vec *sav = sav_desc->sav;
235 uint32_t side_sav_len = sav_desc->len;
236 int i;
237
238 if (type_desc->u.side_array.length != side_sav_len) {
239 printf("ERROR: length mismatch between description and arguments of array\n");
240 abort();
241 }
242 printf("[ ");
243 for (i = 0; i < side_sav_len; i++) {
244 printf("%s", i ? ", " : "");
245 tracer_print_type(type_desc->u.side_array.elem_type, &sav[i]);
246 }
247 printf(" ]");
248}
249
250static
251void tracer_print_vla(const struct side_type_description *type_desc, const struct side_arg_vec_description *sav_desc)
252{
253 const struct side_arg_vec *sav = sav_desc->sav;
254 uint32_t side_sav_len = sav_desc->len;
255 int i;
256
257 printf("[ ");
258 for (i = 0; i < side_sav_len; i++) {
259 printf("%s", i ? ", " : "");
260 tracer_print_type(type_desc->u.side_vla.elem_type, &sav[i]);
261 }
262 printf(" ]");
263}
264
265static
266void tracer_print_vla_visitor(const struct side_type_description *type_desc, void *ctx)
267{
268 enum side_visitor_status status;
269 int i;
270
271 status = type_desc->u.side_vla_visitor.begin(ctx);
272 if (status != SIDE_VISITOR_STATUS_OK) {
273 printf("ERROR: Visitor error\n");
274 abort();
275 }
276
277 printf("[ ");
278 status = SIDE_VISITOR_STATUS_OK;
279 for (i = 0; status == SIDE_VISITOR_STATUS_OK; i++) {
280 struct side_arg_vec sav_elem;
281
282 status = type_desc->u.side_vla_visitor.get_next(ctx, &sav_elem);
283 switch (status) {
284 case SIDE_VISITOR_STATUS_OK:
285 break;
286 case SIDE_VISITOR_STATUS_ERROR:
287 printf("ERROR: Visitor error\n");
288 abort();
289 case SIDE_VISITOR_STATUS_END:
290 continue;
291 }
292 printf("%s", i ? ", " : "");
293 tracer_print_type(type_desc->u.side_vla_visitor.elem_type, &sav_elem);
294 }
295 printf(" ]");
296 if (type_desc->u.side_vla_visitor.end) {
297 status = type_desc->u.side_vla_visitor.end(ctx);
298 if (status != SIDE_VISITOR_STATUS_OK) {
299 printf("ERROR: Visitor error\n");
300 abort();
301 }
302 }
303}
304
305__attribute__((noinline))
306void tracer_call(const struct side_event_description *desc, const struct side_arg_vec_description *sav_desc)
307{
308 const struct side_arg_vec *sav = sav_desc->sav;
309 uint32_t side_sav_len = sav_desc->len;
310 int i;
311
312 printf("provider: %s, event: %s, ", desc->provider_name, desc->event_name);
313 if (desc->nr_fields != side_sav_len) {
314 printf("ERROR: number of fields mismatch between description and arguments\n");
315 abort();
316 }
317 for (i = 0; i < side_sav_len; i++) {
318 printf("%s", i ? ", " : "");
319 tracer_print_field(&desc->fields[i], &sav[i]);
320 }
321 printf("\n");
322}
323
324#define SIDE_PARAM(...) __VA_ARGS__
325
326#define side_type_decl(_type) { .type = _type }
327#define side_field(_type, _name) { .field_name = _name, .side_type = side_type_decl(_type) }
328
329#define side_type_struct_decl(_fields) \
330 { \
331 .type = SIDE_TYPE_STRUCT, \
332 .u = { \
333 .side_struct = { \
334 .nr_fields = SIDE_ARRAY_SIZE(SIDE_PARAM(_fields)), \
335 .fields = _fields, \
336 }, \
337 }, \
338 }
339#define side_field_struct(_name, _fields) \
340 { \
341 .field_name = _name, \
342 .side_type = side_type_struct_decl(SIDE_PARAM(_fields)), \
343 }
344
345#define side_type_array_decl(_elem_type, _length) \
346 { \
347 .type = SIDE_TYPE_ARRAY, \
348 .u = { \
349 .side_array = { \
350 .length = _length, \
351 .elem_type = _elem_type, \
352 }, \
353 }, \
354 }
355#define side_field_array(_name, _elem_type, _length) \
356 { \
357 .field_name = _name, \
358 .side_type = side_type_array_decl(_elem_type, _length), \
359 }
360
361#define side_type_vla_decl(_elem_type) \
362 { \
363 .type = SIDE_TYPE_VLA, \
364 .u = { \
365 .side_vla = { \
366 .elem_type = _elem_type, \
367 }, \
368 }, \
369 }
370#define side_field_vla(_name, _elem_type) \
371 { \
372 .field_name = _name, \
373 .side_type = side_type_vla_decl(_elem_type), \
374 }
375
376#define side_type_vla_visitor_decl(_elem_type, _begin, _end, _get_next) \
377 { \
378 .type = SIDE_TYPE_VLA_VISITOR, \
379 .u = { \
380 .side_vla_visitor = { \
381 .elem_type = _elem_type, \
382 .begin = _begin, \
383 .end = _end, \
384 .get_next = _get_next, \
385 }, \
386 }, \
387 }
388#define side_field_vla_visitor(_name, _elem_type, _begin, _end, _get_next) \
389 { \
390 .field_name = _name, \
391 .side_type = side_type_vla_visitor_decl(_elem_type, _begin, _end, _get_next), \
392 }
393
394#define side_vla_elem(...) \
395 SIDE_COMPOUND_LITERAL(const struct side_type_description, side_type_decl(__VA_ARGS__))
396
397#define side_vla_visitor_elem(...) \
398 SIDE_COMPOUND_LITERAL(const struct side_type_description, side_type_decl(__VA_ARGS__))
399
400#define side_array_elem(...) \
401 SIDE_COMPOUND_LITERAL(const struct side_type_description, side_type_decl(__VA_ARGS__))
402
403#define side_field_list(...) \
404 SIDE_COMPOUND_LITERAL(const struct side_event_field, __VA_ARGS__)
405
406#define side_arg_u8(val) { .type = SIDE_TYPE_U8, .u = { .side_u8 = (val) } }
407#define side_arg_u16(val) { .type = SIDE_TYPE_U16, .u = { .side_u16 = (val) } }
408#define side_arg_u32(val) { .type = SIDE_TYPE_U32, .u = { .side_u32 = (val) } }
409#define side_arg_u64(val) { .type = SIDE_TYPE_U64, .u = { .side_u64 = (val) } }
410#define side_arg_s8(val) { .type = SIDE_TYPE_S8, .u = { .side_s8 = (val) } }
411#define side_arg_s16(val) { .type = SIDE_TYPE_S16, .u = { .side_s16 = (val) } }
412#define side_arg_s32(val) { .type = SIDE_TYPE_S32, .u = { .side_s32 = (val) } }
413#define side_arg_s64(val) { .type = SIDE_TYPE_S64, .u = { .side_s64 = (val) } }
414#define side_arg_string(val) { .type = SIDE_TYPE_STRING, .u = { .string = (val) } }
415#define side_arg_struct(_side_type) { .type = SIDE_TYPE_STRUCT, .u = { .side_struct = (_side_type) } }
416#define side_arg_array(_side_type) { .type = SIDE_TYPE_ARRAY, .u = { .side_array = (_side_type) } }
417#define side_arg_vla(_side_type) { .type = SIDE_TYPE_VLA, .u = { .side_vla = (_side_type) } }
418#define side_arg_vla_visitor(_ctx) { .type = SIDE_TYPE_VLA_VISITOR, .u = { .side_vla_visitor_ctx = (_ctx) } }
419
420#define side_arg_define_vec(_identifier, _sav) \
421 const struct side_arg_vec _identifier##_vec[] = { _sav }; \
422 const struct side_arg_vec_description _identifier = { \
423 .sav = _identifier##_vec, \
424 .len = SIDE_ARRAY_SIZE(_identifier##_vec), \
425 }
426
427#define side_arg_list(...) __VA_ARGS__
428
429#define side_event_cond(desc) if (side_unlikely((desc)->enabled))
430#define side_event_call(desc, _sav) \
431 { \
432 const struct side_arg_vec side_sav[] = { _sav }; \
433 const struct side_arg_vec_description sav_desc = { \
434 .sav = side_sav, \
435 .len = SIDE_ARRAY_SIZE(side_sav), \
436 }; \
437 tracer_call(desc, &sav_desc); \
438 }
439
440#define side_event(desc, sav) \
441 side_event_cond(desc) \
442 side_event_call(desc, SIDE_PARAM(sav)); \
443
444#define side_define_event(_identifier, _provider, _event, _loglevel, _fields) \
445 struct side_event_description _identifier = { \
446 .version = 0, \
447 .enabled = 0, \
448 .loglevel = _loglevel, \
449 .nr_fields = SIDE_ARRAY_SIZE(SIDE_PARAM(_fields)), \
450 .provider_name = _provider, \
451 .event_name = _event, \
452 .fields = _fields, \
453 }
454
455#define side_declare_event(_identifier) \
456 struct side_event_description _identifier
457
458/* User code example */
459
460static side_define_event(my_provider_event, "myprovider", "myevent", SIDE_LOGLEVEL_DEBUG,
461 side_field_list(
462 side_field(SIDE_TYPE_U32, "abc"),
463 side_field(SIDE_TYPE_S64, "def"),
464 side_field(SIDE_TYPE_DYNAMIC, "dynamic"),
465 )
466);
467
468static
469void test_fields(void)
470{
471 uint32_t uw = 42;
472 int64_t sdw = -500;
473
474 my_provider_event.enabled = 1;
475 side_event(&my_provider_event, side_arg_list(side_arg_u32(uw), side_arg_s64(sdw), side_arg_string("zzz")));
476}
477
478static side_define_event(my_provider_event2, "myprovider", "myevent2", SIDE_LOGLEVEL_DEBUG,
479 side_field_list(
480 side_field_struct("structfield",
481 side_field_list(
482 side_field(SIDE_TYPE_U32, "x"),
483 side_field(SIDE_TYPE_S64, "y"),
484 )
485 ),
486 side_field(SIDE_TYPE_U8, "z"),
487 )
488);
489
490static
491void test_struct(void)
492{
493 my_provider_event2.enabled = 1;
494 side_event_cond(&my_provider_event2) {
495 side_arg_define_vec(mystruct, side_arg_list(side_arg_u32(21), side_arg_s64(22)));
496 side_event_call(&my_provider_event2, side_arg_list(side_arg_struct(&mystruct), side_arg_u8(55)));
497 }
498}
499
500static side_define_event(my_provider_event_array, "myprovider", "myarray", SIDE_LOGLEVEL_DEBUG,
501 side_field_list(
502 side_field_array("arr", side_array_elem(SIDE_TYPE_U32), 3),
503 side_field(SIDE_TYPE_S64, "v"),
504 )
505);
506
507static
508void test_array(void)
509{
510 my_provider_event_array.enabled = 1;
511 side_event_cond(&my_provider_event_array) {
512 side_arg_define_vec(myarray, side_arg_list(side_arg_u32(1), side_arg_u32(2), side_arg_u32(3)));
513 side_event_call(&my_provider_event_array, side_arg_list(side_arg_array(&myarray), side_arg_s64(42)));
514 }
515}
516
517static side_define_event(my_provider_event_vla, "myprovider", "myvla", SIDE_LOGLEVEL_DEBUG,
518 side_field_list(
519 side_field_vla("vla", side_vla_elem(SIDE_TYPE_U32)),
520 side_field(SIDE_TYPE_S64, "v"),
521 )
522);
523
524static
525void test_vla(void)
526{
527 my_provider_event_vla.enabled = 1;
528 side_event_cond(&my_provider_event_vla) {
529 side_arg_define_vec(myvla, side_arg_list(side_arg_u32(1), side_arg_u32(2), side_arg_u32(3)));
530 side_event_call(&my_provider_event_vla, side_arg_list(side_arg_vla(&myvla), side_arg_s64(42)));
531 }
532}
533
534struct app_visitor_ctx {
535 const uint32_t *ptr;
536 int init_pos;
537 int current_pos;
538 int end_pos;
539};
540
541enum side_visitor_status test_visitor_begin(void *_ctx)
542{
543 struct app_visitor_ctx *ctx = (struct app_visitor_ctx *) _ctx;
544 ctx->current_pos = ctx->init_pos;
545 return SIDE_VISITOR_STATUS_OK;
546}
547
548enum side_visitor_status test_visitor_end(void *_ctx)
549{
550 return SIDE_VISITOR_STATUS_OK;
551}
552
553enum side_visitor_status test_visitor_get_next(void *_ctx, struct side_arg_vec *sav_elem)
554{
555 struct app_visitor_ctx *ctx = (struct app_visitor_ctx *) _ctx;
556
557 if (ctx->current_pos >= ctx->end_pos)
558 return SIDE_VISITOR_STATUS_END;
559 sav_elem->type = SIDE_TYPE_U32;
560 sav_elem->u.side_u32 = ctx->ptr[ctx->current_pos++];
561 return SIDE_VISITOR_STATUS_OK;
562}
563
564static uint32_t testarray[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
565
566static side_define_event(my_provider_event_vla_visitor, "myprovider", "myvlavisit", SIDE_LOGLEVEL_DEBUG,
567 side_field_list(
568 side_field_vla_visitor("vlavisit", side_vla_visitor_elem(SIDE_TYPE_U32),
569 test_visitor_begin, test_visitor_end, test_visitor_get_next),
570 side_field(SIDE_TYPE_S64, "v"),
571 )
572);
573
574static
575void test_vla_visitor(void)
576{
577 my_provider_event_vla_visitor.enabled = 1;
578 side_event_cond(&my_provider_event_vla_visitor) {
579 struct app_visitor_ctx ctx = {
580 .ptr = testarray,
581 .init_pos = 0,
582 .current_pos = 0,
583 .end_pos = SIDE_ARRAY_SIZE(testarray),
584 };
585 side_event_call(&my_provider_event_vla_visitor, side_arg_list(side_arg_vla_visitor(&ctx), side_arg_s64(42)));
586 }
587}
588
589int main()
590{
591 test_fields();
592 test_struct();
593 test_array();
594 test_vla();
595 test_vla_visitor();
596 return 0;
597}
This page took 0.047959 seconds and 4 git commands to generate.