Home | History | Annotate | Download | only in internal
      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