Small refactoring of emitter

- Make API more clear for using as standalone json emitter
- Support cases that weren't possible before, e.g.
	- emitting primitive values in an array
	- emitting nested arrays
This commit is contained in:
Tyler Etzel 2018-07-05 10:31:43 -07:00 committed by David Goldblatt
parent 41b7372ead
commit eb261e53a6
3 changed files with 345 additions and 243 deletions

View File

@ -60,17 +60,6 @@ struct emitter_row_s {
ql_head(emitter_col_t) cols; ql_head(emitter_col_t) cols;
}; };
static inline void
emitter_row_init(emitter_row_t *row) {
ql_new(&row->cols);
}
static inline void
emitter_col_init(emitter_col_t *col, emitter_row_t *row) {
ql_elm_new(col, link);
ql_tail_insert(&row->cols, col, link);
}
typedef struct emitter_s emitter_t; typedef struct emitter_s emitter_t;
struct emitter_s { struct emitter_s {
emitter_output_t output; emitter_output_t output;
@ -80,18 +69,10 @@ struct emitter_s {
int nesting_depth; int nesting_depth;
/* True if we've already emitted a value at the given depth. */ /* True if we've already emitted a value at the given depth. */
bool item_at_depth; bool item_at_depth;
/* True if we emitted a key and will emit corresponding value next. */
bool emitted_key;
}; };
static inline void
emitter_init(emitter_t *emitter, emitter_output_t emitter_output,
void (*write_cb)(void *, const char *), void *cbopaque) {
emitter->output = emitter_output;
emitter->write_cb = write_cb;
emitter->cbopaque = cbopaque;
emitter->item_at_depth = false;
emitter->nesting_depth = 0;
}
/* Internal convenience function. Write to the emitter the given string. */ /* Internal convenience function. Write to the emitter the given string. */
JEMALLOC_FORMAT_PRINTF(2, 3) JEMALLOC_FORMAT_PRINTF(2, 3)
static inline void static inline void
@ -103,18 +84,6 @@ emitter_printf(emitter_t *emitter, const char *format, ...) {
va_end(ap); va_end(ap);
} }
/* Write to the emitter the given string, but only in table mode. */
JEMALLOC_FORMAT_PRINTF(2, 3)
static inline void
emitter_table_printf(emitter_t *emitter, const char *format, ...) {
if (emitter->output == emitter_output_table) {
va_list ap;
va_start(ap, format);
malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
va_end(ap);
}
}
static inline void static inline void
emitter_gen_fmt(char *out_fmt, size_t out_size, const char *fmt_specifier, emitter_gen_fmt(char *out_fmt, size_t out_size, const char *fmt_specifier,
emitter_justify_t justify, int width) { emitter_justify_t justify, int width) {
@ -235,47 +204,143 @@ emitter_indent(emitter_t *emitter) {
static inline void static inline void
emitter_json_key_prefix(emitter_t *emitter) { emitter_json_key_prefix(emitter_t *emitter) {
if (emitter->emitted_key) {
emitter->emitted_key = false;
return;
}
emitter_printf(emitter, "%s\n", emitter->item_at_depth ? "," : ""); emitter_printf(emitter, "%s\n", emitter->item_at_depth ? "," : "");
emitter_indent(emitter); emitter_indent(emitter);
} }
static inline void /******************************************************************************/
emitter_begin(emitter_t *emitter) { /* Public functions for emitter_t. */
if (emitter->output == emitter_output_json) {
assert(emitter->nesting_depth == 0);
emitter_printf(emitter, "{");
emitter_nest_inc(emitter);
} else {
// tabular init
emitter_printf(emitter, "%s", "");
}
}
static inline void static inline void
emitter_end(emitter_t *emitter) { emitter_init(emitter_t *emitter, emitter_output_t emitter_output,
if (emitter->output == emitter_output_json) { void (*write_cb)(void *, const char *), void *cbopaque) {
assert(emitter->nesting_depth == 1); emitter->output = emitter_output;
emitter_nest_dec(emitter); emitter->write_cb = write_cb;
emitter_printf(emitter, "\n}\n"); emitter->cbopaque = cbopaque;
} emitter->item_at_depth = false;
emitter->emitted_key = false;
emitter->nesting_depth = 0;
} }
/* /******************************************************************************/
* Note emits a different kv pair as well, but only in table mode. Omits the /* JSON public API. */
* note if table_note_key is NULL.
/*
* Emits a key (e.g. as appears in an object). The next json entity emitted will
* be the corresponding value.
*/ */
static inline void static inline void
emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key, emitter_json_key(emitter_t *emitter, const char *json_key) {
if (emitter->output == emitter_output_json) {
emitter_json_key_prefix(emitter);
emitter_printf(emitter, "\"%s\": ", json_key);
emitter->emitted_key = true;
}
}
static inline void
emitter_json_value(emitter_t *emitter, emitter_type_t value_type,
const void *value) {
if (emitter->output == emitter_output_json) {
emitter_json_key_prefix(emitter);
emitter_print_value(emitter, emitter_justify_none, -1,
value_type, value);
emitter->item_at_depth = true;
}
}
/* Shorthand for calling emitter_json_key and then emitter_json_value. */
static inline void
emitter_json_kv(emitter_t *emitter, const char *json_key,
emitter_type_t value_type, const void *value) {
emitter_json_key(emitter, json_key);
emitter_json_value(emitter, value_type, value);
}
static inline void
emitter_json_array_begin(emitter_t *emitter) {
if (emitter->output == emitter_output_json) {
emitter_json_key_prefix(emitter);
emitter_printf(emitter, "[");
emitter_nest_inc(emitter);
}
}
/* Shorthand for calling emitter_json_key and then emitter_json_array_begin. */
static inline void
emitter_json_array_kv_begin(emitter_t *emitter, const char *json_key) {
emitter_json_key(emitter, json_key);
emitter_json_array_begin(emitter);
}
static inline void
emitter_json_array_end(emitter_t *emitter) {
if (emitter->output == emitter_output_json) {
assert(emitter->nesting_depth > 0);
emitter_nest_dec(emitter);
emitter_printf(emitter, "\n");
emitter_indent(emitter);
emitter_printf(emitter, "]");
}
}
static inline void
emitter_json_object_begin(emitter_t *emitter) {
if (emitter->output == emitter_output_json) {
emitter_json_key_prefix(emitter);
emitter_printf(emitter, "{");
emitter_nest_inc(emitter);
}
}
/* Shorthand for calling emitter_json_key and then emitter_json_object_begin. */
static inline void
emitter_json_object_kv_begin(emitter_t *emitter, const char *json_key) {
emitter_json_key(emitter, json_key);
emitter_json_object_begin(emitter);
}
static inline void
emitter_json_object_end(emitter_t *emitter) {
if (emitter->output == emitter_output_json) {
assert(emitter->nesting_depth > 0);
emitter_nest_dec(emitter);
emitter_printf(emitter, "\n");
emitter_indent(emitter);
emitter_printf(emitter, "}");
}
}
/******************************************************************************/
/* Table public API. */
static inline void
emitter_table_dict_begin(emitter_t *emitter, const char *table_key) {
if (emitter->output == emitter_output_table) {
emitter_indent(emitter);
emitter_printf(emitter, "%s\n", table_key);
emitter_nest_inc(emitter);
}
}
static inline void
emitter_table_dict_end(emitter_t *emitter) {
if (emitter->output == emitter_output_table) {
emitter_nest_dec(emitter);
}
}
static inline void
emitter_table_kv_note(emitter_t *emitter, const char *table_key,
emitter_type_t value_type, const void *value, emitter_type_t value_type, const void *value,
const char *table_note_key, emitter_type_t table_note_value_type, const char *table_note_key, emitter_type_t table_note_value_type,
const void *table_note_value) { const void *table_note_value) {
if (emitter->output == emitter_output_json) { if (emitter->output == emitter_output_table) {
assert(emitter->nesting_depth > 0);
emitter_json_key_prefix(emitter);
emitter_printf(emitter, "\"%s\": ", json_key);
emitter_print_value(emitter, emitter_justify_none, -1,
value_type, value);
} else {
emitter_indent(emitter); emitter_indent(emitter);
emitter_printf(emitter, "%s: ", table_key); emitter_printf(emitter, "%s: ", table_key);
emitter_print_value(emitter, emitter_justify_none, -1, emitter_print_value(emitter, emitter_justify_none, -1,
@ -292,130 +357,22 @@ emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,
} }
static inline void static inline void
emitter_kv(emitter_t *emitter, const char *json_key, const char *table_key, emitter_table_kv(emitter_t *emitter, const char *table_key,
emitter_type_t value_type, const void *value) { emitter_type_t value_type, const void *value) {
emitter_kv_note(emitter, json_key, table_key, value_type, value, NULL, emitter_table_kv_note(emitter, table_key, value_type, value, NULL,
emitter_type_bool, NULL); emitter_type_bool, NULL);
} }
static inline void
emitter_json_kv(emitter_t *emitter, const char *json_key,
emitter_type_t value_type, const void *value) {
if (emitter->output == emitter_output_json) {
emitter_kv(emitter, json_key, NULL, value_type, value);
}
}
/* Write to the emitter the given string, but only in table mode. */
JEMALLOC_FORMAT_PRINTF(2, 3)
static inline void static inline void
emitter_table_kv(emitter_t *emitter, const char *table_key, emitter_table_printf(emitter_t *emitter, const char *format, ...) {
emitter_type_t value_type, const void *value) {
if (emitter->output == emitter_output_table) { if (emitter->output == emitter_output_table) {
emitter_kv(emitter, NULL, table_key, value_type, value); va_list ap;
} va_start(ap, format);
} malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
va_end(ap);
static inline void
emitter_dict_begin(emitter_t *emitter, const char *json_key,
const char *table_header) {
if (emitter->output == emitter_output_json) {
emitter_json_key_prefix(emitter);
emitter_printf(emitter, "\"%s\": {", json_key);
emitter_nest_inc(emitter);
} else {
emitter_indent(emitter);
emitter_printf(emitter, "%s\n", table_header);
emitter_nest_inc(emitter);
}
}
static inline void
emitter_dict_end(emitter_t *emitter) {
if (emitter->output == emitter_output_json) {
assert(emitter->nesting_depth > 0);
emitter_nest_dec(emitter);
emitter_printf(emitter, "\n");
emitter_indent(emitter);
emitter_printf(emitter, "}");
} else {
emitter_nest_dec(emitter);
}
}
static inline void
emitter_json_dict_begin(emitter_t *emitter, const char *json_key) {
if (emitter->output == emitter_output_json) {
emitter_dict_begin(emitter, json_key, NULL);
}
}
static inline void
emitter_json_dict_end(emitter_t *emitter) {
if (emitter->output == emitter_output_json) {
emitter_dict_end(emitter);
}
}
static inline void
emitter_table_dict_begin(emitter_t *emitter, const char *table_key) {
if (emitter->output == emitter_output_table) {
emitter_dict_begin(emitter, NULL, table_key);
}
}
static inline void
emitter_table_dict_end(emitter_t *emitter) {
if (emitter->output == emitter_output_table) {
emitter_dict_end(emitter);
}
}
static inline void
emitter_json_arr_begin(emitter_t *emitter, const char *json_key) {
if (emitter->output == emitter_output_json) {
emitter_json_key_prefix(emitter);
emitter_printf(emitter, "\"%s\": [", json_key);
emitter_nest_inc(emitter);
}
}
static inline void
emitter_json_arr_end(emitter_t *emitter) {
if (emitter->output == emitter_output_json) {
assert(emitter->nesting_depth > 0);
emitter_nest_dec(emitter);
emitter_printf(emitter, "\n");
emitter_indent(emitter);
emitter_printf(emitter, "]");
}
}
static inline void
emitter_json_arr_obj_begin(emitter_t *emitter) {
if (emitter->output == emitter_output_json) {
emitter_json_key_prefix(emitter);
emitter_printf(emitter, "{");
emitter_nest_inc(emitter);
}
}
static inline void
emitter_json_arr_obj_end(emitter_t *emitter) {
if (emitter->output == emitter_output_json) {
assert(emitter->nesting_depth > 0);
emitter_nest_dec(emitter);
emitter_printf(emitter, "\n");
emitter_indent(emitter);
emitter_printf(emitter, "}");
}
}
static inline void
emitter_json_arr_value(emitter_t *emitter, emitter_type_t value_type,
const void *value) {
if (emitter->output == emitter_output_json) {
emitter_json_key_prefix(emitter);
emitter_print_value(emitter, emitter_justify_none, -1,
value_type, value);
} }
} }
@ -432,4 +389,93 @@ emitter_table_row(emitter_t *emitter, emitter_row_t *row) {
emitter_table_printf(emitter, "\n"); emitter_table_printf(emitter, "\n");
} }
static inline void
emitter_row_init(emitter_row_t *row) {
ql_new(&row->cols);
}
static inline void
emitter_col_init(emitter_col_t *col, emitter_row_t *row) {
ql_elm_new(col, link);
ql_tail_insert(&row->cols, col, link);
}
/******************************************************************************/
/*
* Generalized public API. Emits using either JSON or table, according to
* settings in the emitter_t. */
/*
* Note emits a different kv pair as well, but only in table mode. Omits the
* note if table_note_key is NULL.
*/
static inline void
emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,
emitter_type_t value_type, const void *value,
const char *table_note_key, emitter_type_t table_note_value_type,
const void *table_note_value) {
if (emitter->output == emitter_output_json) {
emitter_json_key(emitter, json_key);
emitter_json_value(emitter, value_type, value);
} else {
emitter_table_kv_note(emitter, table_key, value_type, value,
table_note_key, table_note_value_type, table_note_value);
}
emitter->item_at_depth = true;
}
static inline void
emitter_kv(emitter_t *emitter, const char *json_key, const char *table_key,
emitter_type_t value_type, const void *value) {
emitter_kv_note(emitter, json_key, table_key, value_type, value, NULL,
emitter_type_bool, NULL);
}
static inline void
emitter_dict_begin(emitter_t *emitter, const char *json_key,
const char *table_header) {
if (emitter->output == emitter_output_json) {
emitter_json_key(emitter, json_key);
emitter_json_object_begin(emitter);
} else {
emitter_table_dict_begin(emitter, table_header);
}
}
static inline void
emitter_dict_end(emitter_t *emitter) {
if (emitter->output == emitter_output_json) {
emitter_json_object_end(emitter);
} else {
emitter_table_dict_end(emitter);
}
}
static inline void
emitter_begin(emitter_t *emitter) {
if (emitter->output == emitter_output_json) {
assert(emitter->nesting_depth == 0);
emitter_printf(emitter, "{");
emitter_nest_inc(emitter);
} else {
/*
* This guarantees that we always call write_cb at least once.
* This is useful if some invariant is established by each call
* to write_cb, but doesn't hold initially: e.g., some buffer
* holds a null-terminated string.
*/
emitter_printf(emitter, "%s", "");
}
}
static inline void
emitter_end(emitter_t *emitter) {
if (emitter->output == emitter_output_json) {
assert(emitter->nesting_depth == 1);
emitter_nest_dec(emitter);
emitter_printf(emitter, "\n}\n");
}
}
#endif /* JEMALLOC_INTERNAL_EMITTER_H */ #endif /* JEMALLOC_INTERNAL_EMITTER_H */

View File

@ -287,7 +287,7 @@ stats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i) {
header_col_size.width -=5; header_col_size.width -=5;
emitter_table_printf(emitter, "bins:"); emitter_table_printf(emitter, "bins:");
emitter_table_row(emitter, &header_row); emitter_table_row(emitter, &header_row);
emitter_json_arr_begin(emitter, "bins"); emitter_json_array_kv_begin(emitter, "bins");
for (j = 0, in_gap = false; j < nbins; j++) { for (j = 0, in_gap = false; j < nbins; j++) {
uint64_t nslabs; uint64_t nslabs;
@ -333,7 +333,7 @@ stats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i) {
col_mutex32); col_mutex32);
} }
emitter_json_arr_obj_begin(emitter); emitter_json_object_begin(emitter);
emitter_json_kv(emitter, "nmalloc", emitter_type_uint64, emitter_json_kv(emitter, "nmalloc", emitter_type_uint64,
&nmalloc); &nmalloc);
emitter_json_kv(emitter, "ndalloc", emitter_type_uint64, emitter_json_kv(emitter, "ndalloc", emitter_type_uint64,
@ -351,12 +351,12 @@ stats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i) {
emitter_json_kv(emitter, "curslabs", emitter_type_size, emitter_json_kv(emitter, "curslabs", emitter_type_size,
&curslabs); &curslabs);
if (mutex) { if (mutex) {
emitter_json_dict_begin(emitter, "mutex"); emitter_json_object_kv_begin(emitter, "mutex");
mutex_stats_emit(emitter, NULL, col_mutex64, mutex_stats_emit(emitter, NULL, col_mutex64,
col_mutex32); col_mutex32);
emitter_json_dict_end(emitter); emitter_json_object_end(emitter);
} }
emitter_json_arr_obj_end(emitter); emitter_json_object_end(emitter);
size_t availregs = nregs * curslabs; size_t availregs = nregs * curslabs;
char util[6]; char util[6];
@ -400,7 +400,7 @@ stats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i) {
emitter_table_row(emitter, &row); emitter_table_row(emitter, &row);
} }
emitter_json_arr_end(emitter); /* Close "bins". */ emitter_json_array_end(emitter); /* Close "bins". */
if (in_gap) { if (in_gap) {
emitter_table_printf(emitter, " ---\n"); emitter_table_printf(emitter, " ---\n");
@ -447,7 +447,7 @@ stats_arena_lextents_print(emitter_t *emitter, unsigned i) {
header_size.width -= 6; header_size.width -= 6;
emitter_table_printf(emitter, "large:"); emitter_table_printf(emitter, "large:");
emitter_table_row(emitter, &header_row); emitter_table_row(emitter, &header_row);
emitter_json_arr_begin(emitter, "lextents"); emitter_json_array_kv_begin(emitter, "lextents");
for (j = 0, in_gap = false; j < nlextents; j++) { for (j = 0, in_gap = false; j < nlextents; j++) {
uint64_t nmalloc, ndalloc, nrequests; uint64_t nmalloc, ndalloc, nrequests;
@ -471,10 +471,10 @@ stats_arena_lextents_print(emitter_t *emitter, unsigned i) {
CTL_M2_M4_GET("stats.arenas.0.lextents.0.curlextents", i, j, CTL_M2_M4_GET("stats.arenas.0.lextents.0.curlextents", i, j,
&curlextents, size_t); &curlextents, size_t);
emitter_json_arr_obj_begin(emitter); emitter_json_object_begin(emitter);
emitter_json_kv(emitter, "curlextents", emitter_type_size, emitter_json_kv(emitter, "curlextents", emitter_type_size,
&curlextents); &curlextents);
emitter_json_arr_obj_end(emitter); emitter_json_object_end(emitter);
col_size.size_val = lextent_size; col_size.size_val = lextent_size;
col_ind.unsigned_val = nbins + j; col_ind.unsigned_val = nbins + j;
@ -488,7 +488,7 @@ stats_arena_lextents_print(emitter_t *emitter, unsigned i) {
emitter_table_row(emitter, &row); emitter_table_row(emitter, &row);
} }
} }
emitter_json_arr_end(emitter); /* Close "lextents". */ emitter_json_array_end(emitter); /* Close "lextents". */
if (in_gap) { if (in_gap) {
emitter_table_printf(emitter, " ---\n"); emitter_table_printf(emitter, " ---\n");
} }
@ -504,19 +504,19 @@ stats_arena_mutexes_print(emitter_t *emitter, unsigned arena_ind) {
emitter_row_init(&row); emitter_row_init(&row);
mutex_stats_init_cols(&row, "", &col_name, col64, col32); mutex_stats_init_cols(&row, "", &col_name, col64, col32);
emitter_json_dict_begin(emitter, "mutexes"); emitter_json_object_kv_begin(emitter, "mutexes");
emitter_table_row(emitter, &row); emitter_table_row(emitter, &row);
for (mutex_prof_arena_ind_t i = 0; i < mutex_prof_num_arena_mutexes; for (mutex_prof_arena_ind_t i = 0; i < mutex_prof_num_arena_mutexes;
i++) { i++) {
const char *name = arena_mutex_names[i]; const char *name = arena_mutex_names[i];
emitter_json_dict_begin(emitter, name); emitter_json_object_kv_begin(emitter, name);
mutex_stats_read_arena(arena_ind, i, name, &col_name, col64, mutex_stats_read_arena(arena_ind, i, name, &col_name, col64,
col32); col32);
mutex_stats_emit(emitter, &row, col64, col32); mutex_stats_emit(emitter, &row, col64, col32);
emitter_json_dict_end(emitter); /* Close the mutex dict. */ emitter_json_object_end(emitter); /* Close the mutex dict. */
} }
emitter_json_dict_end(emitter); /* End "mutexes". */ emitter_json_object_end(emitter); /* End "mutexes". */
} }
static void static void
@ -738,7 +738,7 @@ stats_arena_print(emitter_t *emitter, unsigned i, bool bins, bool large,
alloc_count_##name.type = emitter_type_##valtype; \ alloc_count_##name.type = emitter_type_##valtype; \
alloc_count_##name.valtype##_val = small_or_large##_##name; alloc_count_##name.valtype##_val = small_or_large##_##name;
emitter_json_dict_begin(emitter, "small"); emitter_json_object_kv_begin(emitter, "small");
alloc_count_title.str_val = "small:"; alloc_count_title.str_val = "small:";
GET_AND_EMIT_ALLOC_STAT(small, allocated, size) GET_AND_EMIT_ALLOC_STAT(small, allocated, size)
@ -747,9 +747,9 @@ stats_arena_print(emitter_t *emitter, unsigned i, bool bins, bool large,
GET_AND_EMIT_ALLOC_STAT(small, nrequests, uint64) GET_AND_EMIT_ALLOC_STAT(small, nrequests, uint64)
emitter_table_row(emitter, &alloc_count_row); emitter_table_row(emitter, &alloc_count_row);
emitter_json_dict_end(emitter); /* Close "small". */ emitter_json_object_end(emitter); /* Close "small". */
emitter_json_dict_begin(emitter, "large"); emitter_json_object_kv_begin(emitter, "large");
alloc_count_title.str_val = "large:"; alloc_count_title.str_val = "large:";
GET_AND_EMIT_ALLOC_STAT(large, allocated, size) GET_AND_EMIT_ALLOC_STAT(large, allocated, size)
@ -758,7 +758,7 @@ stats_arena_print(emitter_t *emitter, unsigned i, bool bins, bool large,
GET_AND_EMIT_ALLOC_STAT(large, nrequests, uint64) GET_AND_EMIT_ALLOC_STAT(large, nrequests, uint64)
emitter_table_row(emitter, &alloc_count_row); emitter_table_row(emitter, &alloc_count_row);
emitter_json_dict_end(emitter); /* Close "large". */ emitter_json_object_end(emitter); /* Close "large". */
#undef GET_AND_EMIT_ALLOC_STAT #undef GET_AND_EMIT_ALLOC_STAT
@ -980,7 +980,7 @@ stats_general_print(emitter_t *emitter) {
* The json output sticks arena info into an "arenas" dict; the table * The json output sticks arena info into an "arenas" dict; the table
* output puts them at the top-level. * output puts them at the top-level.
*/ */
emitter_json_dict_begin(emitter, "arenas"); emitter_json_object_kv_begin(emitter, "arenas");
CTL_GET("arenas.narenas", &uv, unsigned); CTL_GET("arenas.narenas", &uv, unsigned);
emitter_kv(emitter, "narenas", "Arenas", emitter_type_unsigned, &uv); emitter_kv(emitter, "narenas", "Arenas", emitter_type_unsigned, &uv);
@ -1021,9 +1021,9 @@ stats_general_print(emitter_t *emitter) {
* (not just omit the printing). * (not just omit the printing).
*/ */
if (emitter->output == emitter_output_json) { if (emitter->output == emitter_output_json) {
emitter_json_arr_begin(emitter, "bin"); emitter_json_array_kv_begin(emitter, "bin");
for (unsigned i = 0; i < nbins; i++) { for (unsigned i = 0; i < nbins; i++) {
emitter_json_arr_obj_begin(emitter); emitter_json_object_begin(emitter);
CTL_M2_GET("arenas.bin.0.size", i, &sv, size_t); CTL_M2_GET("arenas.bin.0.size", i, &sv, size_t);
emitter_json_kv(emitter, "size", emitter_type_size, emitter_json_kv(emitter, "size", emitter_type_size,
@ -1037,9 +1037,9 @@ stats_general_print(emitter_t *emitter) {
emitter_json_kv(emitter, "slab_size", emitter_type_size, emitter_json_kv(emitter, "slab_size", emitter_type_size,
&sv); &sv);
emitter_json_arr_obj_end(emitter); emitter_json_object_end(emitter);
} }
emitter_json_arr_end(emitter); /* Close "bin". */ emitter_json_array_end(emitter); /* Close "bin". */
} }
unsigned nlextents; unsigned nlextents;
@ -1048,20 +1048,20 @@ stats_general_print(emitter_t *emitter) {
emitter_type_unsigned, &nlextents); emitter_type_unsigned, &nlextents);
if (emitter->output == emitter_output_json) { if (emitter->output == emitter_output_json) {
emitter_json_arr_begin(emitter, "lextent"); emitter_json_array_kv_begin(emitter, "lextent");
for (unsigned i = 0; i < nlextents; i++) { for (unsigned i = 0; i < nlextents; i++) {
emitter_json_arr_obj_begin(emitter); emitter_json_object_begin(emitter);
CTL_M2_GET("arenas.lextent.0.size", i, &sv, size_t); CTL_M2_GET("arenas.lextent.0.size", i, &sv, size_t);
emitter_json_kv(emitter, "size", emitter_type_size, emitter_json_kv(emitter, "size", emitter_type_size,
&sv); &sv);
emitter_json_arr_obj_end(emitter); emitter_json_object_end(emitter);
} }
emitter_json_arr_end(emitter); /* Close "lextent". */ emitter_json_array_end(emitter); /* Close "lextent". */
} }
emitter_json_dict_end(emitter); /* Close "arenas" */ emitter_json_object_end(emitter); /* Close "arenas" */
} }
static void static void
@ -1098,7 +1098,7 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
} }
/* Generic global stats. */ /* Generic global stats. */
emitter_json_dict_begin(emitter, "stats"); emitter_json_object_kv_begin(emitter, "stats");
emitter_json_kv(emitter, "allocated", emitter_type_size, &allocated); emitter_json_kv(emitter, "allocated", emitter_type_size, &allocated);
emitter_json_kv(emitter, "active", emitter_type_size, &active); emitter_json_kv(emitter, "active", emitter_type_size, &active);
emitter_json_kv(emitter, "metadata", emitter_type_size, &metadata); emitter_json_kv(emitter, "metadata", emitter_type_size, &metadata);
@ -1114,14 +1114,14 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
resident, mapped, retained); resident, mapped, retained);
/* Background thread stats. */ /* Background thread stats. */
emitter_json_dict_begin(emitter, "background_thread"); emitter_json_object_kv_begin(emitter, "background_thread");
emitter_json_kv(emitter, "num_threads", emitter_type_size, emitter_json_kv(emitter, "num_threads", emitter_type_size,
&num_background_threads); &num_background_threads);
emitter_json_kv(emitter, "num_runs", emitter_type_uint64, emitter_json_kv(emitter, "num_runs", emitter_type_uint64,
&background_thread_num_runs); &background_thread_num_runs);
emitter_json_kv(emitter, "run_interval", emitter_type_uint64, emitter_json_kv(emitter, "run_interval", emitter_type_uint64,
&background_thread_run_interval); &background_thread_run_interval);
emitter_json_dict_end(emitter); /* Close "background_thread". */ emitter_json_object_end(emitter); /* Close "background_thread". */
emitter_table_printf(emitter, "Background threads: %zu, " emitter_table_printf(emitter, "Background threads: %zu, "
"num_runs: %"FMTu64", run_interval: %"FMTu64" ns\n", "num_runs: %"FMTu64", run_interval: %"FMTu64" ns\n",
@ -1138,25 +1138,25 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
mutex_stats_init_cols(&row, "", &name, col64, col32); mutex_stats_init_cols(&row, "", &name, col64, col32);
emitter_table_row(emitter, &row); emitter_table_row(emitter, &row);
emitter_json_dict_begin(emitter, "mutexes"); emitter_json_object_kv_begin(emitter, "mutexes");
for (int i = 0; i < mutex_prof_num_global_mutexes; i++) { for (int i = 0; i < mutex_prof_num_global_mutexes; i++) {
mutex_stats_read_global(global_mutex_names[i], &name, mutex_stats_read_global(global_mutex_names[i], &name,
col64, col32); col64, col32);
emitter_json_dict_begin(emitter, global_mutex_names[i]); emitter_json_object_kv_begin(emitter, global_mutex_names[i]);
mutex_stats_emit(emitter, &row, col64, col32); mutex_stats_emit(emitter, &row, col64, col32);
emitter_json_dict_end(emitter); emitter_json_object_end(emitter);
} }
emitter_json_dict_end(emitter); /* Close "mutexes". */ emitter_json_object_end(emitter); /* Close "mutexes". */
} }
emitter_json_dict_end(emitter); /* Close "stats". */ emitter_json_object_end(emitter); /* Close "stats". */
if (merged || destroyed || unmerged) { if (merged || destroyed || unmerged) {
unsigned narenas; unsigned narenas;
emitter_json_dict_begin(emitter, "stats.arenas"); emitter_json_object_kv_begin(emitter, "stats.arenas");
CTL_GET("arenas.narenas", &narenas, unsigned); CTL_GET("arenas.narenas", &narenas, unsigned);
size_t mib[3]; size_t mib[3];
@ -1185,10 +1185,10 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
if (merged && (ninitialized > 1 || !unmerged)) { if (merged && (ninitialized > 1 || !unmerged)) {
/* Print merged arena stats. */ /* Print merged arena stats. */
emitter_table_printf(emitter, "Merged arenas stats:\n"); emitter_table_printf(emitter, "Merged arenas stats:\n");
emitter_json_dict_begin(emitter, "merged"); emitter_json_object_kv_begin(emitter, "merged");
stats_arena_print(emitter, MALLCTL_ARENAS_ALL, bins, stats_arena_print(emitter, MALLCTL_ARENAS_ALL, bins,
large, mutex); large, mutex);
emitter_json_dict_end(emitter); /* Close "merged". */ emitter_json_object_end(emitter); /* Close "merged". */
} }
/* Destroyed stats. */ /* Destroyed stats. */
@ -1196,10 +1196,10 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
/* Print destroyed arena stats. */ /* Print destroyed arena stats. */
emitter_table_printf(emitter, emitter_table_printf(emitter,
"Destroyed arenas stats:\n"); "Destroyed arenas stats:\n");
emitter_json_dict_begin(emitter, "destroyed"); emitter_json_object_kv_begin(emitter, "destroyed");
stats_arena_print(emitter, MALLCTL_ARENAS_DESTROYED, stats_arena_print(emitter, MALLCTL_ARENAS_DESTROYED,
bins, large, mutex); bins, large, mutex);
emitter_json_dict_end(emitter); /* Close "destroyed". */ emitter_json_object_end(emitter); /* Close "destroyed". */
} }
/* Unmerged stats. */ /* Unmerged stats. */
@ -1209,18 +1209,18 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
char arena_ind_str[20]; char arena_ind_str[20];
malloc_snprintf(arena_ind_str, malloc_snprintf(arena_ind_str,
sizeof(arena_ind_str), "%u", i); sizeof(arena_ind_str), "%u", i);
emitter_json_dict_begin(emitter, emitter_json_object_kv_begin(emitter,
arena_ind_str); arena_ind_str);
emitter_table_printf(emitter, emitter_table_printf(emitter,
"arenas[%s]:\n", arena_ind_str); "arenas[%s]:\n", arena_ind_str);
stats_arena_print(emitter, i, bins, stats_arena_print(emitter, i, bins,
large, mutex); large, mutex);
/* Close "<arena-ind>". */ /* Close "<arena-ind>". */
emitter_json_dict_end(emitter); emitter_json_object_end(emitter);
} }
} }
} }
emitter_json_dict_end(emitter); /* Close "stats.arenas". */ emitter_json_object_end(emitter); /* Close "stats.arenas". */
} }
} }
@ -1273,7 +1273,7 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
cbopaque); cbopaque);
emitter_begin(&emitter); emitter_begin(&emitter);
emitter_table_printf(&emitter, "___ Begin jemalloc statistics ___\n"); emitter_table_printf(&emitter, "___ Begin jemalloc statistics ___\n");
emitter_json_dict_begin(&emitter, "jemalloc"); emitter_json_object_kv_begin(&emitter, "jemalloc");
if (general) { if (general) {
stats_general_print(&emitter); stats_general_print(&emitter);
@ -1283,7 +1283,7 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
bins, large, mutex); bins, large, mutex);
} }
emitter_json_dict_end(&emitter); /* Closes the "jemalloc" dict. */ emitter_json_object_end(&emitter); /* Closes the "jemalloc" dict. */
emitter_table_printf(&emitter, "--- End jemalloc statistics ---\n"); emitter_table_printf(&emitter, "--- End jemalloc statistics ---\n");
emitter_end(&emitter); emitter_end(&emitter);
} }

View File

@ -169,7 +169,7 @@ static void emit_nested_dict(emitter_t *emitter) {
emitter_end(emitter); emitter_end(emitter);
} }
static const char *nested_dict_json = static const char *nested_object_json =
"{\n" "{\n"
"\t\"json1\": {\n" "\t\"json1\": {\n"
"\t\t\"json2\": {\n" "\t\t\"json2\": {\n"
@ -183,7 +183,7 @@ static const char *nested_dict_json =
"\t}\n" "\t}\n"
"}\n"; "}\n";
static const char *nested_dict_table = static const char *nested_object_table =
"Dict 1\n" "Dict 1\n"
" Dict 2\n" " Dict 2\n"
" A primitive: 123\n" " A primitive: 123\n"
@ -192,8 +192,8 @@ static const char *nested_dict_table =
" Another primitive: 123\n"; " Another primitive: 123\n";
TEST_BEGIN(test_nested_dict) { TEST_BEGIN(test_nested_dict) {
assert_emit_output(&emit_nested_dict, nested_dict_json, assert_emit_output(&emit_nested_dict, nested_object_json,
nested_dict_table); nested_object_table);
} }
TEST_END TEST_END
@ -256,13 +256,14 @@ emit_modal(emitter_t *emitter) {
int val = 123; int val = 123;
emitter_begin(emitter); emitter_begin(emitter);
emitter_dict_begin(emitter, "j0", "T0"); emitter_dict_begin(emitter, "j0", "T0");
emitter_json_dict_begin(emitter, "j1"); emitter_json_key(emitter, "j1");
emitter_json_object_begin(emitter);
emitter_kv(emitter, "i1", "I1", emitter_type_int, &val); emitter_kv(emitter, "i1", "I1", emitter_type_int, &val);
emitter_json_kv(emitter, "i2", emitter_type_int, &val); emitter_json_kv(emitter, "i2", emitter_type_int, &val);
emitter_table_kv(emitter, "I3", emitter_type_int, &val); emitter_table_kv(emitter, "I3", emitter_type_int, &val);
emitter_table_dict_begin(emitter, "T1"); emitter_table_dict_begin(emitter, "T1");
emitter_kv(emitter, "i4", "I4", emitter_type_int, &val); emitter_kv(emitter, "i4", "I4", emitter_type_int, &val);
emitter_json_dict_end(emitter); /* Close j1 */ emitter_json_object_end(emitter); /* Close j1 */
emitter_kv(emitter, "i5", "I5", emitter_type_int, &val); emitter_kv(emitter, "i5", "I5", emitter_type_int, &val);
emitter_table_dict_end(emitter); /* Close T1 */ emitter_table_dict_end(emitter); /* Close T1 */
emitter_kv(emitter, "i6", "I6", emitter_type_int, &val); emitter_kv(emitter, "i6", "I6", emitter_type_int, &val);
@ -302,24 +303,26 @@ emit_json_arr(emitter_t *emitter) {
int ival = 123; int ival = 123;
emitter_begin(emitter); emitter_begin(emitter);
emitter_json_dict_begin(emitter, "dict"); emitter_json_key(emitter, "dict");
emitter_json_arr_begin(emitter, "arr"); emitter_json_object_begin(emitter);
emitter_json_arr_obj_begin(emitter); emitter_json_key(emitter, "arr");
emitter_json_array_begin(emitter);
emitter_json_object_begin(emitter);
emitter_json_kv(emitter, "foo", emitter_type_int, &ival); emitter_json_kv(emitter, "foo", emitter_type_int, &ival);
emitter_json_arr_obj_end(emitter); /* Close arr[0] */ emitter_json_object_end(emitter); /* Close arr[0] */
/* arr[1] and arr[2] are primitives. */ /* arr[1] and arr[2] are primitives. */
emitter_json_arr_value(emitter, emitter_type_int, &ival); emitter_json_value(emitter, emitter_type_int, &ival);
emitter_json_arr_value(emitter, emitter_type_int, &ival); emitter_json_value(emitter, emitter_type_int, &ival);
emitter_json_arr_obj_begin(emitter); emitter_json_object_begin(emitter);
emitter_json_kv(emitter, "bar", emitter_type_int, &ival); emitter_json_kv(emitter, "bar", emitter_type_int, &ival);
emitter_json_kv(emitter, "baz", emitter_type_int, &ival); emitter_json_kv(emitter, "baz", emitter_type_int, &ival);
emitter_json_arr_obj_end(emitter); /* Close arr[3]. */ emitter_json_object_end(emitter); /* Close arr[3]. */
emitter_json_arr_end(emitter); /* Close arr. */ emitter_json_array_end(emitter); /* Close arr. */
emitter_json_dict_end(emitter); /* Close dict. */ emitter_json_object_end(emitter); /* Close dict. */
emitter_end(emitter); emitter_end(emitter);
} }
static const char *json_arr_json = static const char *json_array_json =
"{\n" "{\n"
"\t\"dict\": {\n" "\t\"dict\": {\n"
"\t\t\"arr\": [\n" "\t\t\"arr\": [\n"
@ -336,10 +339,62 @@ static const char *json_arr_json =
"\t}\n" "\t}\n"
"}\n"; "}\n";
static const char *json_arr_table = ""; static const char *json_array_table = "";
TEST_BEGIN(test_json_arr) { TEST_BEGIN(test_json_arr) {
assert_emit_output(&emit_json_arr, json_arr_json, json_arr_table); assert_emit_output(&emit_json_arr, json_array_json, json_array_table);
}
TEST_END
static void
emit_json_nested_array(emitter_t *emitter) {
int ival = 123;
char *sval = "foo";
emitter_begin(emitter);
emitter_json_array_begin(emitter);
emitter_json_array_begin(emitter);
emitter_json_value(emitter, emitter_type_int, &ival);
emitter_json_value(emitter, emitter_type_string, &sval);
emitter_json_value(emitter, emitter_type_int, &ival);
emitter_json_value(emitter, emitter_type_string, &sval);
emitter_json_array_end(emitter);
emitter_json_array_begin(emitter);
emitter_json_value(emitter, emitter_type_int, &ival);
emitter_json_array_end(emitter);
emitter_json_array_begin(emitter);
emitter_json_value(emitter, emitter_type_string, &sval);
emitter_json_value(emitter, emitter_type_int, &ival);
emitter_json_array_end(emitter);
emitter_json_array_begin(emitter);
emitter_json_array_end(emitter);
emitter_json_array_end(emitter);
emitter_end(emitter);
}
static const char *json_nested_array_json =
"{\n"
"\t[\n"
"\t\t[\n"
"\t\t\t123,\n"
"\t\t\t\"foo\",\n"
"\t\t\t123,\n"
"\t\t\t\"foo\"\n"
"\t\t],\n"
"\t\t[\n"
"\t\t\t123\n"
"\t\t],\n"
"\t\t[\n"
"\t\t\t\"foo\",\n"
"\t\t\t123\n"
"\t\t],\n"
"\t\t[\n"
"\t\t]\n"
"\t]\n"
"}\n";
TEST_BEGIN(test_json_nested_arr) {
assert_emit_output(&emit_json_nested_array, json_nested_array_json,
json_array_table);
} }
TEST_END TEST_END
@ -409,5 +464,6 @@ main(void) {
test_types, test_types,
test_modal, test_modal,
test_json_arr, test_json_arr,
test_json_nested_arr,
test_table_row); test_table_row);
} }