1/* @title: NDJSON */
2#pragma once
3#include <compiler.h>
4#include <linker/symbols.h>
5#include <stdbool.h>
6#include <stddef.h>
7#include <stdint.h>
8
9/* Uses a non-console output and a single
10 * JSON object for each line, with each record type declared once (custom)
11 * to provide custom member fields.
12 *
13 * e.g.
14 *
15 * NDJSON_DECLARE(test_result, "test", "result", 1,
16 * NDJSON_STR(name),
17 * NDJSON_STR(status),
18 * NDJSON_U64(duration_ms));
19 *
20 * ndjson_emit(test_result, .name = t->name, .status = "pass",
21 * .duration_ms = took);
22 *
23 * Omitted fields are left as zero
24 */
25
26#define NDJSON_KEY_SECTION "s"
27#define NDJSON_KEY_KIND "k"
28#define NDJSON_KEY_VERSION "v"
29#define NDJSON_KEY_TIME "t"
30#define NDJSON_KEY_CPU "c"
31#define NDJSON_KEY_TRUNCATED "_trunc"
32
33#define NDJSON_SECTION_NDJSON "ndjson"
34#define NDJSON_SECTION_TEST "test"
35#define NDJSON_SECTION_PANIC "panic"
36#define NDJSON_SECTION_ASAN "asan"
37#define NDJSON_SECTION_SELFTEST "selftest"
38#define NDJSON_SECTION_NIGHTMARE "nightmare"
39#define NDJSON_SECTION_LOCK_CHK "lock_chk"
40#define NDJSON_SECTION_LOG "log"
41
42#define NDJSON_KIND_SCHEMA "schema"
43#define NDJSON_KIND_BYE "bye"
44#define NDJSON_KIND_BEGIN "begin"
45#define NDJSON_KIND_RESULT "result"
46#define NDJSON_KIND_GROUP_START "group_start"
47#define NDJSON_KIND_GROUP_END "group_end"
48#define NDJSON_KIND_TOTALS "totals"
49#define NDJSON_KIND_VERDICT "verdict"
50#define NDJSON_KIND_EXIT "exit"
51#define NDJSON_KIND_AT "at"
52#define NDJSON_KIND_FAULT "fault"
53#define NDJSON_KIND_FRAME "frame"
54#define NDJSON_KIND_PEER "peer"
55#define NDJSON_KIND_OWNER "owner"
56#define NDJSON_KIND_BOOT "boot"
57#define NDJSON_KIND_STAT "stat"
58#define NDJSON_KIND_QUIESCE "quiesce"
59#define NDJSON_KIND_FINDING "finding"
60#define NDJSON_KIND_MESSAGE "msg"
61
62#define NDJSON_TYPE_NAME_U64 "u64"
63#define NDJSON_TYPE_NAME_I64 "i64"
64#define NDJSON_TYPE_NAME_BOOL "bool"
65#define NDJSON_TYPE_NAME_STR "str"
66#define NDJSON_TYPE_NAME_HEX "hex"
67
68enum ndjson_type : uint8_t {
69 NDJSON_TYPE_U64,
70 NDJSON_TYPE_I64,
71 NDJSON_TYPE_BOOL,
72 NDJSON_TYPE_STR,
73 NDJSON_TYPE_HEX, /* emitted as "0x.." */
74};
75
76struct ndjson_field {
77 const char *name;
78 enum ndjson_type type;
79 uint16_t offset; /* into the generated argument struct */
80};
81
82struct ndjson_record {
83 const char *section;
84 const char *kind;
85 uint16_t version;
86 uint16_t nfields;
87 const struct ndjson_field *fields;
88};
89
90LINKER_SECTION_DEFINE(struct ndjson_record, ndjson_records);
91
92#define NDJSON_CTYPE_U64 uint64_t
93#define NDJSON_CTYPE_I64 int64_t
94#define NDJSON_CTYPE_BOOL bool
95#define NDJSON_CTYPE_STR const char *
96#define NDJSON_CTYPE_HEX uint64_t
97
98/* NDJSON_DECLARE uses this */
99#define NDJSON_U64(n) (U64, n)
100#define NDJSON_I64(n) (I64, n)
101#define NDJSON_BOOL(n) (BOOL, n)
102#define NDJSON_STR(n) (STR, n)
103#define NDJSON_HEX(n) (HEX, n)
104
105#define NDJSON_UNTUPLE(...) __VA_ARGS__
106
107#define NDJSON_MEMBER(id, spec) NDJSON_MEMBER_(id, NDJSON_UNTUPLE spec)
108#define NDJSON_MEMBER_(id, ...) NDJSON_MEMBER__(id, __VA_ARGS__)
109#define NDJSON_MEMBER__(id, t, n) NDJSON_CTYPE_##t n;
110
111#define NDJSON_DESC(id, spec) NDJSON_DESC_(id, NDJSON_UNTUPLE spec)
112#define NDJSON_DESC_(id, ...) NDJSON_DESC__(id, __VA_ARGS__)
113#define NDJSON_DESC__(id, t, n) \
114 {.name = #n, \
115 .type = NDJSON_TYPE_##t, \
116 .offset = (uint16_t) __builtin_offsetof(struct __ndjson_args_##id, n)},
117
118/* Apply f(a, x) to each x with constant a */
119#define NDJSON_MAP_1(f, a, x) f(a, x)
120#define NDJSON_MAP_2(f, a, x, ...) f(a, x) NDJSON_MAP_1(f, a, __VA_ARGS__)
121#define NDJSON_MAP_3(f, a, x, ...) f(a, x) NDJSON_MAP_2(f, a, __VA_ARGS__)
122#define NDJSON_MAP_4(f, a, x, ...) f(a, x) NDJSON_MAP_3(f, a, __VA_ARGS__)
123#define NDJSON_MAP_5(f, a, x, ...) f(a, x) NDJSON_MAP_4(f, a, __VA_ARGS__)
124#define NDJSON_MAP_6(f, a, x, ...) f(a, x) NDJSON_MAP_5(f, a, __VA_ARGS__)
125#define NDJSON_MAP_7(f, a, x, ...) f(a, x) NDJSON_MAP_6(f, a, __VA_ARGS__)
126#define NDJSON_MAP_8(f, a, x, ...) f(a, x) NDJSON_MAP_7(f, a, __VA_ARGS__)
127#define NDJSON_MAP_9(f, a, x, ...) f(a, x) NDJSON_MAP_8(f, a, __VA_ARGS__)
128#define NDJSON_MAP_10(f, a, x, ...) f(a, x) NDJSON_MAP_9(f, a, __VA_ARGS__)
129#define NDJSON_MAP_11(f, a, x, ...) f(a, x) NDJSON_MAP_10(f, a, __VA_ARGS__)
130#define NDJSON_MAP_12(f, a, x, ...) f(a, x) NDJSON_MAP_11(f, a, __VA_ARGS__)
131#define NDJSON_MAP_13(f, a, x, ...) f(a, x) NDJSON_MAP_12(f, a, __VA_ARGS__)
132#define NDJSON_MAP_14(f, a, x, ...) f(a, x) NDJSON_MAP_13(f, a, __VA_ARGS__)
133#define NDJSON_MAP_15(f, a, x, ...) f(a, x) NDJSON_MAP_14(f, a, __VA_ARGS__)
134#define NDJSON_MAP_16(f, a, x, ...) f(a, x) NDJSON_MAP_15(f, a, __VA_ARGS__)
135
136#define NDJSON_MAP(f, a, ...) \
137 _DISPATCH(NDJSON_MAP, PP_NARG(__VA_ARGS__))(f, a, __VA_ARGS__)
138
139#define NDJSON_MAX_FIELDS 16
140
141#define NDJSON_DECLARE(id, section_, kind_, version_, ...) \
142 struct __ndjson_args_##id { \
143 NDJSON_MAP(NDJSON_MEMBER, id, __VA_ARGS__) \
144 }; \
145 static const struct ndjson_field __ndjson_fields_##id[] = { \
146 NDJSON_MAP(NDJSON_DESC, id, __VA_ARGS__)}; \
147 LINKER_SECTION_OBJECT(struct ndjson_record, ndjson_records) \
148 __ndjson_rec_##id = {.section = (section_), \
149 .kind = (kind_), \
150 .version = (version_), \
151 .nfields = PP_NARG(__VA_ARGS__), \
152 .fields = __ndjson_fields_##id}; \
153 static_assert(PP_NARG(__VA_ARGS__) <= NDJSON_MAX_FIELDS, \
154 "ndjson record " #id " has too many fields")
155
156#define NDJSON(id) (&__ndjson_rec_##id)
157#define NDJSON_EXTERN(id) extern struct ndjson_record __ndjson_rec_##id
158
159void ndjson_emit_impl(const struct ndjson_record *rec, const void *args);
160
161#define ndjson_emit(id, ...) \
162 ndjson_emit_impl(NDJSON(id), \
163 &(const struct __ndjson_args_##id) {__VA_ARGS__})
164
165#define NDJSON_STR_MAX 512
166
167/* Terminator, and signals that the kernel run ended, used so the logs know
168 * that something failed/hung when it is absent */
169void ndjson_bye(uint64_t code, const char *reason);
170
171void ndjson_early_init(void);
172void ndjson_init(void);
173
174bool ndjson_carrier_online(void);
175void ndjson_carrier_init(uint16_t port);
176void ndjson_carrier_disable(void);
177
178void ndjson_enter_panic(void);
179