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