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