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_json_compact, |
10 | emitter_output_table |
11 | }; |
12 | |
13 | typedef enum emitter_justify_e emitter_justify_t; |
14 | enum 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 | |
21 | typedef enum emitter_type_e emitter_type_t; |
22 | enum 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 | |
39 | typedef struct emitter_col_s emitter_col_t; |
40 | struct 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 | |
62 | typedef struct emitter_row_s emitter_row_t; |
63 | struct emitter_row_s { |
64 | ql_head(emitter_col_t) cols; |
65 | }; |
66 | |
67 | typedef struct emitter_s emitter_t; |
68 | struct 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 | |
80 | static inline bool |
81 | emitter_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. */ |
87 | JEMALLOC_FORMAT_PRINTF(2, 3) |
88 | static inline void |
89 | emitter_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 | |
97 | static inline const char * JEMALLOC_FORMAT_ARG(3) |
98 | emitter_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 | */ |
124 | static inline void |
125 | emitter_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. */ |
194 | static inline void |
195 | emitter_nest_inc(emitter_t *emitter) { |
196 | emitter->nesting_depth++; |
197 | emitter->item_at_depth = false; |
198 | } |
199 | |
200 | static inline void |
201 | emitter_nest_dec(emitter_t *emitter) { |
202 | emitter->nesting_depth--; |
203 | emitter->item_at_depth = true; |
204 | } |
205 | |
206 | static inline void |
207 | emitter_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 | |
222 | static inline void |
223 | emitter_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 | |
241 | static inline void |
242 | emitter_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 | */ |
259 | static inline void |
260 | emitter_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 | |
269 | static inline void |
270 | emitter_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. */ |
281 | static inline void |
282 | emitter_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 | |
288 | static inline void |
289 | emitter_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. */ |
298 | static inline void |
299 | emitter_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 | |
304 | static inline void |
305 | emitter_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 | |
317 | static inline void |
318 | emitter_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. */ |
327 | static inline void |
328 | emitter_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 | |
333 | static inline void |
334 | emitter_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 | |
350 | static inline void |
351 | emitter_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 | |
359 | static inline void |
360 | emitter_table_dict_end(emitter_t *emitter) { |
361 | if (emitter->output == emitter_output_table) { |
362 | emitter_nest_dec(emitter); |
363 | } |
364 | } |
365 | |
366 | static inline void |
367 | emitter_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 | |
387 | static inline void |
388 | emitter_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. */ |
396 | JEMALLOC_FORMAT_PRINTF(2, 3) |
397 | static inline void |
398 | emitter_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 | |
407 | static inline void |
408 | emitter_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 | |
420 | static inline void |
421 | emitter_row_init(emitter_row_t *row) { |
422 | ql_new(&row->cols); |
423 | } |
424 | |
425 | static inline void |
426 | emitter_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 | */ |
441 | static inline void |
442 | emitter_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 | |
456 | static inline void |
457 | emitter_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 | |
463 | static inline void |
464 | emitter_dict_begin(emitter_t *emitter, const char *json_key, |
465 | const char *) { |
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 | |
474 | static inline void |
475 | emitter_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 | |
483 | static inline void |
484 | emitter_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 | |
500 | static inline void |
501 | emitter_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 | |