| 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 | |
| 68 | enum 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 | |
| 76 | struct ndjson_field { |
| 77 | const char *name; |
| 78 | enum ndjson_type type; |
| 79 | uint16_t offset; /* into the generated argument struct */ |
| 80 | }; |
| 81 | |
| 82 | struct 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 | |
| 90 | LINKER_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 | |
| 159 | void 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 */ |
| 169 | void ndjson_bye(uint64_t code, const char *reason); |
| 170 | |
| 171 | void ndjson_early_init(void); |
| 172 | void ndjson_init(void); |
| 173 | |
| 174 | bool ndjson_carrier_online(void); |
| 175 | void ndjson_carrier_init(uint16_t port); |
| 176 | void ndjson_carrier_disable(void); |
| 177 | |
| 178 | void ndjson_enter_panic(void); |
| 179 | |