xref: /netbsd-src/external/bsd/jemalloc.old/include/jemalloc/internal/emitter.h (revision 8e33eff89e26cf71871ead62f0d5063e1313c33a)
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 const char * __attribute__((__format_arg__(3)))
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 	fmt_specifier++;
123 	if (justify == emitter_justify_none) {
124 		written = malloc_snprintf(out_fmt, out_size,
125 		    "%%%s", fmt_specifier);
126 	} else if (justify == emitter_justify_left) {
127 		written = malloc_snprintf(out_fmt, out_size,
128 		    "%%-%d%s", width, fmt_specifier);
129 	} else {
130 		written = malloc_snprintf(out_fmt, out_size,
131 		    "%%%d%s", width, fmt_specifier);
132 	}
133 	/* Only happens in case of bad format string, which *we* choose. */
134 	assert(written <  out_size);
135 	return out_fmt;
136 }
137 
138 /*
139  * Internal.  Emit the given value type in the relevant encoding (so that the
140  * bool true gets mapped to json "true", but the string "true" gets mapped to
141  * json "\"true\"", for instance.
142  *
143  * Width is ignored if justify is emitter_justify_none.
144  */
145 static inline void
146 emitter_print_value(emitter_t *emitter, emitter_justify_t justify, int width,
147     emitter_type_t value_type, const void *value) {
148 	size_t str_written;
149 #define BUF_SIZE 256
150 #define FMT_SIZE 10
151 	/*
152 	 * We dynamically generate a format string to emit, to let us use the
153 	 * snprintf machinery.  This is kinda hacky, but gets the job done
154 	 * quickly without having to think about the various snprintf edge
155 	 * cases.
156 	 */
157 	char fmt[FMT_SIZE];
158 	char buf[BUF_SIZE];
159 
160 #define EMIT_SIMPLE(type, format)					\
161 	emitter_printf(emitter,						\
162 	    emitter_gen_fmt(fmt, FMT_SIZE, format, justify, width),	\
163 	    *(const type *)value);
164 
165 	switch (value_type) {
166 	case emitter_type_bool:
167 		emitter_printf(emitter,
168 		    emitter_gen_fmt(fmt, FMT_SIZE, "%s", justify, width),
169 		    *(const bool *)value ?  "true" : "false");
170 		break;
171 	case emitter_type_int:
172 		EMIT_SIMPLE(int, "%d")
173 		break;
174 	case emitter_type_unsigned:
175 		EMIT_SIMPLE(unsigned, "%u")
176 		break;
177 	case emitter_type_ssize:
178 		EMIT_SIMPLE(ssize_t, "%zd")
179 		break;
180 	case emitter_type_size:
181 		EMIT_SIMPLE(size_t, "%zu")
182 		break;
183 	case emitter_type_string:
184 		str_written = malloc_snprintf(buf, BUF_SIZE, "\"%s\"",
185 		    *(const char *const *)value);
186 		/*
187 		 * We control the strings we output; we shouldn't get anything
188 		 * anywhere near the fmt size.
189 		 */
190 		assert(str_written < BUF_SIZE);
191 		emitter_printf(emitter,
192 		    emitter_gen_fmt(fmt, FMT_SIZE, "%s", justify, width), buf);
193 		break;
194 	case emitter_type_uint32:
195 		EMIT_SIMPLE(uint32_t, "%" FMTu32)
196 		break;
197 	case emitter_type_uint64:
198 		EMIT_SIMPLE(uint64_t, "%" FMTu64)
199 		break;
200 	case emitter_type_title:
201 		EMIT_SIMPLE(char *const, "%s");
202 		break;
203 	default:
204 		unreachable();
205 	}
206 #undef BUF_SIZE
207 #undef FMT_SIZE
208 }
209 
210 
211 /* Internal functions.  In json mode, tracks nesting state. */
212 static inline void
213 emitter_nest_inc(emitter_t *emitter) {
214 	emitter->nesting_depth++;
215 	emitter->item_at_depth = false;
216 }
217 
218 static inline void
219 emitter_nest_dec(emitter_t *emitter) {
220 	emitter->nesting_depth--;
221 	emitter->item_at_depth = true;
222 }
223 
224 static inline void
225 emitter_indent(emitter_t *emitter) {
226 	int amount = emitter->nesting_depth;
227 	const char *indent_str;
228 	if (emitter->output == emitter_output_json) {
229 		indent_str = "\t";
230 	} else {
231 		amount *= 2;
232 		indent_str = " ";
233 	}
234 	for (int i = 0; i < amount; i++) {
235 		emitter_printf(emitter, "%s", indent_str);
236 	}
237 }
238 
239 static inline void
240 emitter_json_key_prefix(emitter_t *emitter) {
241 	emitter_printf(emitter, "%s\n", emitter->item_at_depth ? "," : "");
242 	emitter_indent(emitter);
243 }
244 
245 static inline void
246 emitter_begin(emitter_t *emitter) {
247 	if (emitter->output == emitter_output_json) {
248 		assert(emitter->nesting_depth == 0);
249 		emitter_printf(emitter, "{");
250 		emitter_nest_inc(emitter);
251 	} else {
252 		// tabular init
253 		emitter_printf(emitter, "%s", "");
254 	}
255 }
256 
257 static inline void
258 emitter_end(emitter_t *emitter) {
259 	if (emitter->output == emitter_output_json) {
260 		assert(emitter->nesting_depth == 1);
261 		emitter_nest_dec(emitter);
262 		emitter_printf(emitter, "\n}\n");
263 	}
264 }
265 
266 /*
267  * Note emits a different kv pair as well, but only in table mode.  Omits the
268  * note if table_note_key is NULL.
269  */
270 static inline void
271 emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,
272     emitter_type_t value_type, const void *value,
273     const char *table_note_key, emitter_type_t table_note_value_type,
274     const void *table_note_value) {
275 	if (emitter->output == emitter_output_json) {
276 		assert(emitter->nesting_depth > 0);
277 		emitter_json_key_prefix(emitter);
278 		emitter_printf(emitter, "\"%s\": ", json_key);
279 		emitter_print_value(emitter, emitter_justify_none, -1,
280 		    value_type, value);
281 	} else {
282 		emitter_indent(emitter);
283 		emitter_printf(emitter, "%s: ", table_key);
284 		emitter_print_value(emitter, emitter_justify_none, -1,
285 		    value_type, value);
286 		if (table_note_key != NULL) {
287 			emitter_printf(emitter, " (%s: ", table_note_key);
288 			emitter_print_value(emitter, emitter_justify_none, -1,
289 			    table_note_value_type, table_note_value);
290 			emitter_printf(emitter, ")");
291 		}
292 		emitter_printf(emitter, "\n");
293 	}
294 	emitter->item_at_depth = true;
295 }
296 
297 static inline void
298 emitter_kv(emitter_t *emitter, const char *json_key, const char *table_key,
299     emitter_type_t value_type, const void *value) {
300 	emitter_kv_note(emitter, json_key, table_key, value_type, value, NULL,
301 	    emitter_type_bool, NULL);
302 }
303 
304 static inline void
305 emitter_json_kv(emitter_t *emitter, const char *json_key,
306     emitter_type_t value_type, const void *value) {
307 	if (emitter->output == emitter_output_json) {
308 		emitter_kv(emitter, json_key, NULL, value_type, value);
309 	}
310 }
311 
312 static inline void
313 emitter_table_kv(emitter_t *emitter, const char *table_key,
314     emitter_type_t value_type, const void *value) {
315 	if (emitter->output == emitter_output_table) {
316 		emitter_kv(emitter, NULL, table_key, value_type, value);
317 	}
318 }
319 
320 static inline void
321 emitter_dict_begin(emitter_t *emitter, const char *json_key,
322     const char *table_header) {
323 	if (emitter->output == emitter_output_json) {
324 		emitter_json_key_prefix(emitter);
325 		emitter_printf(emitter, "\"%s\": {", json_key);
326 		emitter_nest_inc(emitter);
327 	} else {
328 		emitter_indent(emitter);
329 		emitter_printf(emitter, "%s\n", table_header);
330 		emitter_nest_inc(emitter);
331 	}
332 }
333 
334 static inline void
335 emitter_dict_end(emitter_t *emitter) {
336 	if (emitter->output == emitter_output_json) {
337 		assert(emitter->nesting_depth > 0);
338 		emitter_nest_dec(emitter);
339 		emitter_printf(emitter, "\n");
340 		emitter_indent(emitter);
341 		emitter_printf(emitter, "}");
342 	} else {
343 		emitter_nest_dec(emitter);
344 	}
345 }
346 
347 static inline void
348 emitter_json_dict_begin(emitter_t *emitter, const char *json_key) {
349 	if (emitter->output == emitter_output_json) {
350 		emitter_dict_begin(emitter, json_key, NULL);
351 	}
352 }
353 
354 static inline void
355 emitter_json_dict_end(emitter_t *emitter) {
356 	if (emitter->output == emitter_output_json) {
357 		emitter_dict_end(emitter);
358 	}
359 }
360 
361 static inline void
362 emitter_table_dict_begin(emitter_t *emitter, const char *table_key) {
363 	if (emitter->output == emitter_output_table) {
364 		emitter_dict_begin(emitter, NULL, table_key);
365 	}
366 }
367 
368 static inline void
369 emitter_table_dict_end(emitter_t *emitter) {
370 	if (emitter->output == emitter_output_table) {
371 		emitter_dict_end(emitter);
372 	}
373 }
374 
375 static inline void
376 emitter_json_arr_begin(emitter_t *emitter, const char *json_key) {
377 	if (emitter->output == emitter_output_json) {
378 		emitter_json_key_prefix(emitter);
379 		emitter_printf(emitter, "\"%s\": [", json_key);
380 		emitter_nest_inc(emitter);
381 	}
382 }
383 
384 static inline void
385 emitter_json_arr_end(emitter_t *emitter) {
386 	if (emitter->output == emitter_output_json) {
387 		assert(emitter->nesting_depth > 0);
388 		emitter_nest_dec(emitter);
389 		emitter_printf(emitter, "\n");
390 		emitter_indent(emitter);
391 		emitter_printf(emitter, "]");
392 	}
393 }
394 
395 static inline void
396 emitter_json_arr_obj_begin(emitter_t *emitter) {
397 	if (emitter->output == emitter_output_json) {
398 		emitter_json_key_prefix(emitter);
399 		emitter_printf(emitter, "{");
400 		emitter_nest_inc(emitter);
401 	}
402 }
403 
404 static inline void
405 emitter_json_arr_obj_end(emitter_t *emitter) {
406 	if (emitter->output == emitter_output_json) {
407 		assert(emitter->nesting_depth > 0);
408 		emitter_nest_dec(emitter);
409 		emitter_printf(emitter, "\n");
410 		emitter_indent(emitter);
411 		emitter_printf(emitter, "}");
412 	}
413 }
414 
415 static inline void
416 emitter_json_arr_value(emitter_t *emitter, emitter_type_t value_type,
417     const void *value) {
418 	if (emitter->output == emitter_output_json) {
419 		emitter_json_key_prefix(emitter);
420 		emitter_print_value(emitter, emitter_justify_none, -1,
421 		    value_type, value);
422 	}
423 }
424 
425 static inline void
426 emitter_table_row(emitter_t *emitter, emitter_row_t *row) {
427 	if (emitter->output != emitter_output_table) {
428 		return;
429 	}
430 	emitter_col_t *col;
431 	ql_foreach(col, &row->cols, link) {
432 		emitter_print_value(emitter, col->justify, col->width,
433 		    col->type, (const void *)&col->bool_val);
434 	}
435 	emitter_table_printf(emitter, "\n");
436 }
437 
438 #endif /* JEMALLOC_INTERNAL_EMITTER_H */
439