1#include <bootstage.h>
2#include <global.h>
3#include <ndjson.h>
4#include <smp/core.h>
5#include <string.h>
6#include <time/time.h>
7
8#include "internal.h"
9
10/* first point where GS:base and clock can be read without fault */
11#define NDJSON_CONTEXT_STAGE BOOTSTAGE_EARLY_DEVICES
12
13static void ndjson_put(const char *s) {
14 ndjson_carrier_write(s, len: strlen(str: s));
15}
16
17static void ndjson_put_u64(uint64_t v) {
18 char buf[20];
19 size_t n = 0;
20
21 do {
22 buf[n++] = (char) ('0' + (v % 10));
23 v /= 10;
24 } while (v);
25
26 while (n)
27 ndjson_carrier_putc(c: buf[--n]);
28}
29
30static void ndjson_put_i64(int64_t v) {
31 if (v < 0) {
32 ndjson_carrier_putc(c: '-');
33 ndjson_put_u64(v: (uint64_t) -(v + 1) + 1); /* INT64_MIN survives this */
34 return;
35 }
36
37 ndjson_put_u64(v: (uint64_t) v);
38}
39
40static void ndjson_put_hex(uint64_t v) {
41 static const char digits[] = "0123456789abcdef";
42 char buf[16];
43 size_t n = 0;
44
45 do {
46 buf[n++] = digits[v & 0xf];
47 v >>= 4;
48 } while (v);
49
50 ndjson_put(s: "\"0x");
51 while (n)
52 ndjson_carrier_putc(c: buf[--n]);
53 ndjson_carrier_putc(c: '"');
54}
55
56static void ndjson_put_escaped_byte(uint8_t c) {
57 static const char digits[] = "0123456789abcdef";
58
59 ndjson_put(s: "\\u00");
60 ndjson_carrier_putc(c: digits[c >> 4]);
61 ndjson_carrier_putc(c: digits[c & 0xf]);
62}
63
64/* Returns true when a string was cut short, and escapes bytes */
65static bool ndjson_put_string(const char *s) {
66 if (!s) {
67 ndjson_put(s: "null");
68 return false;
69 }
70
71 ndjson_carrier_putc(c: '"');
72
73 size_t i = 0;
74 for (; s[i]; i++) {
75 uint8_t c = (uint8_t) s[i];
76
77 if (i == NDJSON_STR_MAX)
78 break;
79
80 switch (c) {
81 case '"': ndjson_put(s: "\\\""); continue;
82 case '\\': ndjson_put(s: "\\\\"); continue;
83 case '\n': ndjson_put(s: "\\n"); continue;
84 case '\r': ndjson_put(s: "\\r"); continue;
85 case '\t': ndjson_put(s: "\\t"); continue;
86 case '\b': ndjson_put(s: "\\b"); continue;
87 case '\f': ndjson_put(s: "\\f"); continue;
88 default: break;
89 }
90
91 if (c < 0x20 || c > 0x7e) {
92 ndjson_put_escaped_byte(c);
93 continue;
94 }
95
96 ndjson_carrier_putc(c: (char) c);
97 }
98
99 ndjson_carrier_putc(c: '"');
100 return s[i] != '\0';
101}
102
103/* Separator goes with the key */
104static bool ndjson_put_first_key(const char *name) {
105 bool trunc = ndjson_put_string(s: name);
106 ndjson_carrier_putc(c: ':');
107 return trunc;
108}
109
110static bool ndjson_put_key(const char *name) {
111 ndjson_carrier_putc(c: ',');
112 return ndjson_put_first_key(name);
113}
114
115static bool ndjson_put_field(const struct ndjson_field *f, const void *args) {
116 const void *p = (const char *) args + f->offset;
117 bool trunc = ndjson_put_key(name: f->name);
118
119 switch (f->type) {
120 case NDJSON_TYPE_U64: ndjson_put_u64(v: *(const uint64_t *) p); break;
121 case NDJSON_TYPE_I64: ndjson_put_i64(v: *(const int64_t *) p); break;
122 case NDJSON_TYPE_HEX: ndjson_put_hex(v: *(const uint64_t *) p); break;
123 case NDJSON_TYPE_BOOL:
124 ndjson_put(s: *(const bool *) p ? "true" : "false");
125 break;
126 case NDJSON_TYPE_STR:
127 trunc |= ndjson_put_string(s: *(const char *const *) p);
128 break;
129 }
130
131 return trunc;
132}
133
134static void ndjson_put_context(void) {
135 bool ready = global.current_bootstage >= NDJSON_CONTEXT_STAGE;
136
137 ndjson_put_key(NDJSON_KEY_TIME);
138 if (ready)
139 ndjson_put_u64(v: time_get_ms());
140 else
141 ndjson_put(s: "null");
142
143 ndjson_put_key(NDJSON_KEY_CPU);
144 if (ready)
145 ndjson_put_u64(v: smp_id_raw()); /* Just a snapshot */
146 else
147 ndjson_put(s: "null");
148}
149
150void ndjson_emit_impl(const struct ndjson_record *rec, const void *args) {
151 bool irqs_were_on;
152
153 if (!ndjson_carrier_begin(irqs_were_on: &irqs_were_on))
154 return;
155
156 bool trunc = false;
157
158 ndjson_carrier_putc(c: '{');
159 trunc |= ndjson_put_first_key(NDJSON_KEY_SECTION);
160 trunc |= ndjson_put_string(s: rec->section);
161
162 trunc |= ndjson_put_key(NDJSON_KEY_KIND);
163 trunc |= ndjson_put_string(s: rec->kind);
164
165 trunc |= ndjson_put_key(NDJSON_KEY_VERSION);
166 ndjson_put_u64(v: rec->version);
167
168 ndjson_put_context();
169
170 for (uint16_t i = 0; i < rec->nfields; i++)
171 trunc |= ndjson_put_field(f: &rec->fields[i], args);
172
173 if (trunc) {
174 ndjson_put_key(NDJSON_KEY_TRUNCATED);
175 ndjson_put(s: "true");
176 }
177
178 ndjson_put(s: "}\n");
179
180 ndjson_carrier_end(irqs_were_on);
181}
182