xref: /freebsd/contrib/jemalloc/include/jemalloc/internal/emitter.h (revision c43cad87172039ccf38172129c79755ea79e6102)
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 *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 
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