1 #ifndef JEMALLOC_INTERNAL_EMITTER_H
2 #define JEMALLOC_INTERNAL_EMITTER_H
3
4 #include "jemalloc/internal/ql.h"
5
6 typedef enum emitter_output_e emitter_output_t;
7 enum emitter_output_e {
8 emitter_output_json,
9 emitter_output_table
10 };
11
12 typedef enum emitter_justify_e emitter_justify_t;
13 enum emitter_justify_e {
14 emitter_justify_left,
15 emitter_justify_right,
16 /* Not for users; just to pass to internal functions. */
17 emitter_justify_none
18 };
19
20 typedef enum emitter_type_e emitter_type_t;
21 enum emitter_type_e {
22 emitter_type_bool,
23 emitter_type_int,
24 emitter_type_unsigned,
25 emitter_type_uint32,
26 emitter_type_uint64,
27 emitter_type_size,
28 emitter_type_ssize,
29 emitter_type_string,
30 /*
31 * A title is a column title in a table; it's just a string, but it's
32 * not quoted.
33 */
34 emitter_type_title,
35 };
36
37 typedef struct emitter_col_s emitter_col_t;
38 struct emitter_col_s {
39 /* Filled in by the user. */
40 emitter_justify_t justify;
41 int width;
42 emitter_type_t type;
43 union {
44 bool bool_val;
45 int int_val;
46 unsigned unsigned_val;
47 uint32_t uint32_val;
48 uint64_t uint64_val;
49 size_t size_val;
50 ssize_t ssize_val;
51 const char *str_val;
52 };
53
54 /* Filled in by initialization. */
55 ql_elm(emitter_col_t) link;
56 };
57
58 typedef struct emitter_row_s emitter_row_t;
59 struct emitter_row_s {
60 ql_head(emitter_col_t) cols;
61 };
62
63 static inline void
emitter_row_init(emitter_row_t * row)64 emitter_row_init(emitter_row_t *row) {
65 ql_new(&row->cols);
66 }
67
68 static inline void
emitter_col_init(emitter_col_t * col,emitter_row_t * row)69 emitter_col_init(emitter_col_t *col, emitter_row_t *row) {
70 ql_elm_new(col, link);
71 ql_tail_insert(&row->cols, col, link);
72 }
73
74 typedef struct emitter_s emitter_t;
75 struct emitter_s {
76 emitter_output_t output;
77 /* The output information. */
78 void (*write_cb)(void *, const char *);
79 void *cbopaque;
80 int nesting_depth;
81 /* True if we've already emitted a value at the given depth. */
82 bool item_at_depth;
83 };
84
85 static inline void
emitter_init(emitter_t * emitter,emitter_output_t emitter_output,void (* write_cb)(void *,const char *),void * cbopaque)86 emitter_init(emitter_t *emitter, emitter_output_t emitter_output,
87 void (*write_cb)(void *, const char *), void *cbopaque) {
88 emitter->output = emitter_output;
89 emitter->write_cb = write_cb;
90 emitter->cbopaque = cbopaque;
91 emitter->item_at_depth = false;
92 emitter->nesting_depth = 0;
93 }
94
95 /* Internal convenience function. Write to the emitter the given string. */
96 JEMALLOC_FORMAT_PRINTF(2, 3)
97 static inline void
emitter_printf(emitter_t * emitter,const char * format,...)98 emitter_printf(emitter_t *emitter, const char *format, ...) {
99 va_list ap;
100
101 va_start(ap, format);
102 malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
103 va_end(ap);
104 }
105
106 /* Write to the emitter the given string, but only in table mode. */
107 JEMALLOC_FORMAT_PRINTF(2, 3)
108 static inline void
emitter_table_printf(emitter_t * emitter,const char * format,...)109 emitter_table_printf(emitter_t *emitter, const char *format, ...) {
110 if (emitter->output == emitter_output_table) {
111 va_list ap;
112 va_start(ap, format);
113 malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
114 va_end(ap);
115 }
116 }
117
118 static inline void
emitter_gen_fmt(char * out_fmt,size_t out_size,const char * fmt_specifier,emitter_justify_t justify,int width)119 emitter_gen_fmt(char *out_fmt, size_t out_size, const char *fmt_specifier,
120 emitter_justify_t justify, int width) {
121 size_t written;
122 if (justify == emitter_justify_none) {
123 written = malloc_snprintf(out_fmt, out_size,
124 "%%%s", fmt_specifier);
125 } else if (justify == emitter_justify_left) {
126 written = malloc_snprintf(out_fmt, out_size,
127 "%%-%d%s", width, fmt_specifier);
128 } else {
129 written = malloc_snprintf(out_fmt, out_size,
130 "%%%d%s", width, fmt_specifier);
131 }
132 /* Only happens in case of bad format string, which *we* choose. */
133 assert(written < out_size);
134 }
135
136 /*
137 * Internal. Emit the given value type in the relevant encoding (so that the
138 * bool true gets mapped to json "true", but the string "true" gets mapped to
139 * json "\"true\"", for instance.
140 *
141 * Width is ignored if justify is emitter_justify_none.
142 */
143 static inline void
emitter_print_value(emitter_t * emitter,emitter_justify_t justify,int width,emitter_type_t value_type,const void * value)144 emitter_print_value(emitter_t *emitter, emitter_justify_t justify, int width,
145 emitter_type_t value_type, const void *value) {
146 size_t str_written;
147 #define BUF_SIZE 256
148 #define FMT_SIZE 10
149 /*
150 * We dynamically generate a format string to emit, to let us use the
151 * snprintf machinery. This is kinda hacky, but gets the job done
152 * quickly without having to think about the various snprintf edge
153 * cases.
154 */
155 char fmt[FMT_SIZE];
156 char buf[BUF_SIZE];
157
158 #define EMIT_SIMPLE(type, format) \
159 emitter_gen_fmt(fmt, FMT_SIZE, format, justify, width); \
160 emitter_printf(emitter, fmt, *(const type *)value); \
161
162 switch (value_type) {
163 case emitter_type_bool:
164 emitter_gen_fmt(fmt, FMT_SIZE, "s", justify, width);
165 emitter_printf(emitter, fmt, *(const bool *)value ?
166 "true" : "false");
167 break;
168 case emitter_type_int:
169 EMIT_SIMPLE(int, "d")
170 break;
171 case emitter_type_unsigned:
172 EMIT_SIMPLE(unsigned, "u")
173 break;
174 case emitter_type_ssize:
175 EMIT_SIMPLE(ssize_t, "zd")
176 break;
177 case emitter_type_size:
178 EMIT_SIMPLE(size_t, "zu")
179 break;
180 case emitter_type_string:
181 str_written = malloc_snprintf(buf, BUF_SIZE, "\"%s\"",
182 *(const char *const *)value);
183 /*
184 * We control the strings we output; we shouldn't get anything
185 * anywhere near the fmt size.
186 */
187 assert(str_written < BUF_SIZE);
188 emitter_gen_fmt(fmt, FMT_SIZE, "s", justify, width);
189 emitter_printf(emitter, fmt, buf);
190 break;
191 case emitter_type_uint32:
192 EMIT_SIMPLE(uint32_t, FMTu32)
193 break;
194 case emitter_type_uint64:
195 EMIT_SIMPLE(uint64_t, FMTu64)
196 break;
197 case emitter_type_title:
198 EMIT_SIMPLE(char *const, "s");
199 break;
200 default:
201 unreachable();
202 }
203 #undef BUF_SIZE
204 #undef FMT_SIZE
205 }
206
207
208 /* Internal functions. In json mode, tracks nesting state. */
209 static inline void
emitter_nest_inc(emitter_t * emitter)210 emitter_nest_inc(emitter_t *emitter) {
211 emitter->nesting_depth++;
212 emitter->item_at_depth = false;
213 }
214
215 static inline void
emitter_nest_dec(emitter_t * emitter)216 emitter_nest_dec(emitter_t *emitter) {
217 emitter->nesting_depth--;
218 emitter->item_at_depth = true;
219 }
220
221 static inline void
emitter_indent(emitter_t * emitter)222 emitter_indent(emitter_t *emitter) {
223 int amount = emitter->nesting_depth;
224 const char *indent_str;
225 if (emitter->output == emitter_output_json) {
226 indent_str = "\t";
227 } else {
228 amount *= 2;
229 indent_str = " ";
230 }
231 for (int i = 0; i < amount; i++) {
232 emitter_printf(emitter, "%s", indent_str);
233 }
234 }
235
236 static inline void
emitter_json_key_prefix(emitter_t * emitter)237 emitter_json_key_prefix(emitter_t *emitter) {
238 emitter_printf(emitter, "%s\n", emitter->item_at_depth ? "," : "");
239 emitter_indent(emitter);
240 }
241
242 static inline void
emitter_begin(emitter_t * emitter)243 emitter_begin(emitter_t *emitter) {
244 if (emitter->output == emitter_output_json) {
245 assert(emitter->nesting_depth == 0);
246 emitter_printf(emitter, "{");
247 emitter_nest_inc(emitter);
248 } else {
249 // tabular init
250 emitter_printf(emitter, "%s", "");
251 }
252 }
253
254 static inline void
emitter_end(emitter_t * emitter)255 emitter_end(emitter_t *emitter) {
256 if (emitter->output == emitter_output_json) {
257 assert(emitter->nesting_depth == 1);
258 emitter_nest_dec(emitter);
259 emitter_printf(emitter, "\n}\n");
260 }
261 }
262
263 /*
264 * Note emits a different kv pair as well, but only in table mode. Omits the
265 * note if table_note_key is NULL.
266 */
267 static inline void
emitter_kv_note(emitter_t * emitter,const char * json_key,const char * table_key,emitter_type_t value_type,const void * value,const char * table_note_key,emitter_type_t table_note_value_type,const void * table_note_value)268 emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,
269 emitter_type_t value_type, const void *value,
270 const char *table_note_key, emitter_type_t table_note_value_type,
271 const void *table_note_value) {
272 if (emitter->output == emitter_output_json) {
273 assert(emitter->nesting_depth > 0);
274 emitter_json_key_prefix(emitter);
275 emitter_printf(emitter, "\"%s\": ", json_key);
276 emitter_print_value(emitter, emitter_justify_none, -1,
277 value_type, value);
278 } else {
279 emitter_indent(emitter);
280 emitter_printf(emitter, "%s: ", table_key);
281 emitter_print_value(emitter, emitter_justify_none, -1,
282 value_type, value);
283 if (table_note_key != NULL) {
284 emitter_printf(emitter, " (%s: ", table_note_key);
285 emitter_print_value(emitter, emitter_justify_none, -1,
286 table_note_value_type, table_note_value);
287 emitter_printf(emitter, ")");
288 }
289 emitter_printf(emitter, "\n");
290 }
291 emitter->item_at_depth = true;
292 }
293
294 static inline void
emitter_kv(emitter_t * emitter,const char * json_key,const char * table_key,emitter_type_t value_type,const void * value)295 emitter_kv(emitter_t *emitter, const char *json_key, const char *table_key,
296 emitter_type_t value_type, const void *value) {
297 emitter_kv_note(emitter, json_key, table_key, value_type, value, NULL,
298 emitter_type_bool, NULL);
299 }
300
301 static inline void
emitter_json_kv(emitter_t * emitter,const char * json_key,emitter_type_t value_type,const void * value)302 emitter_json_kv(emitter_t *emitter, const char *json_key,
303 emitter_type_t value_type, const void *value) {
304 if (emitter->output == emitter_output_json) {
305 emitter_kv(emitter, json_key, NULL, value_type, value);
306 }
307 }
308
309 static inline void
emitter_table_kv(emitter_t * emitter,const char * table_key,emitter_type_t value_type,const void * value)310 emitter_table_kv(emitter_t *emitter, const char *table_key,
311 emitter_type_t value_type, const void *value) {
312 if (emitter->output == emitter_output_table) {
313 emitter_kv(emitter, NULL, table_key, value_type, value);
314 }
315 }
316
317 static inline void
emitter_dict_begin(emitter_t * emitter,const char * json_key,const char * table_header)318 emitter_dict_begin(emitter_t *emitter, const char *json_key,
319 const char *table_header) {
320 if (emitter->output == emitter_output_json) {
321 emitter_json_key_prefix(emitter);
322 emitter_printf(emitter, "\"%s\": {", json_key);
323 emitter_nest_inc(emitter);
324 } else {
325 emitter_indent(emitter);
326 emitter_printf(emitter, "%s\n", table_header);
327 emitter_nest_inc(emitter);
328 }
329 }
330
331 static inline void
emitter_dict_end(emitter_t * emitter)332 emitter_dict_end(emitter_t *emitter) {
333 if (emitter->output == emitter_output_json) {
334 assert(emitter->nesting_depth > 0);
335 emitter_nest_dec(emitter);
336 emitter_printf(emitter, "\n");
337 emitter_indent(emitter);
338 emitter_printf(emitter, "}");
339 } else {
340 emitter_nest_dec(emitter);
341 }
342 }
343
344 static inline void
emitter_json_dict_begin(emitter_t * emitter,const char * json_key)345 emitter_json_dict_begin(emitter_t *emitter, const char *json_key) {
346 if (emitter->output == emitter_output_json) {
347 emitter_dict_begin(emitter, json_key, NULL);
348 }
349 }
350
351 static inline void
emitter_json_dict_end(emitter_t * emitter)352 emitter_json_dict_end(emitter_t *emitter) {
353 if (emitter->output == emitter_output_json) {
354 emitter_dict_end(emitter);
355 }
356 }
357
358 static inline void
emitter_table_dict_begin(emitter_t * emitter,const char * table_key)359 emitter_table_dict_begin(emitter_t *emitter, const char *table_key) {
360 if (emitter->output == emitter_output_table) {
361 emitter_dict_begin(emitter, NULL, table_key);
362 }
363 }
364
365 static inline void
emitter_table_dict_end(emitter_t * emitter)366 emitter_table_dict_end(emitter_t *emitter) {
367 if (emitter->output == emitter_output_table) {
368 emitter_dict_end(emitter);
369 }
370 }
371
372 static inline void
emitter_json_arr_begin(emitter_t * emitter,const char * json_key)373 emitter_json_arr_begin(emitter_t *emitter, const char *json_key) {
374 if (emitter->output == emitter_output_json) {
375 emitter_json_key_prefix(emitter);
376 emitter_printf(emitter, "\"%s\": [", json_key);
377 emitter_nest_inc(emitter);
378 }
379 }
380
381 static inline void
emitter_json_arr_end(emitter_t * emitter)382 emitter_json_arr_end(emitter_t *emitter) {
383 if (emitter->output == emitter_output_json) {
384 assert(emitter->nesting_depth > 0);
385 emitter_nest_dec(emitter);
386 emitter_printf(emitter, "\n");
387 emitter_indent(emitter);
388 emitter_printf(emitter, "]");
389 }
390 }
391
392 static inline void
emitter_json_arr_obj_begin(emitter_t * emitter)393 emitter_json_arr_obj_begin(emitter_t *emitter) {
394 if (emitter->output == emitter_output_json) {
395 emitter_json_key_prefix(emitter);
396 emitter_printf(emitter, "{");
397 emitter_nest_inc(emitter);
398 }
399 }
400
401 static inline void
emitter_json_arr_obj_end(emitter_t * emitter)402 emitter_json_arr_obj_end(emitter_t *emitter) {
403 if (emitter->output == emitter_output_json) {
404 assert(emitter->nesting_depth > 0);
405 emitter_nest_dec(emitter);
406 emitter_printf(emitter, "\n");
407 emitter_indent(emitter);
408 emitter_printf(emitter, "}");
409 }
410 }
411
412 static inline void
emitter_json_arr_value(emitter_t * emitter,emitter_type_t value_type,const void * value)413 emitter_json_arr_value(emitter_t *emitter, emitter_type_t value_type,
414 const void *value) {
415 if (emitter->output == emitter_output_json) {
416 emitter_json_key_prefix(emitter);
417 emitter_print_value(emitter, emitter_justify_none, -1,
418 value_type, value);
419 }
420 }
421
422 static inline void
emitter_table_row(emitter_t * emitter,emitter_row_t * row)423 emitter_table_row(emitter_t *emitter, emitter_row_t *row) {
424 if (emitter->output != emitter_output_table) {
425 return;
426 }
427 emitter_col_t *col;
428 ql_foreach(col, &row->cols, link) {
429 emitter_print_value(emitter, col->justify, col->width,
430 col->type, (const void *)&col->bool_val);
431 }
432 emitter_table_printf(emitter, "\n");
433 }
434
435 #endif /* JEMALLOC_INTERNAL_EMITTER_H */
436