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