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