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