xref: /freebsd/contrib/jemalloc/include/jemalloc/internal/emitter.h (revision d3d381b2b194b4d24853e92eecef55f262688d1a)
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
64 emitter_row_init(emitter_row_t *row) {
65 	ql_new(&row->cols);
66 }
67 
68 static inline void
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
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
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
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
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
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
210 emitter_nest_inc(emitter_t *emitter) {
211 	emitter->nesting_depth++;
212 	emitter->item_at_depth = false;
213 }
214 
215 static inline void
216 emitter_nest_dec(emitter_t *emitter) {
217 	emitter->nesting_depth--;
218 	emitter->item_at_depth = true;
219 }
220 
221 static inline void
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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