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