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