mirror of
https://github.com/jemalloc/jemalloc.git
synced 2026-07-22 04:03:11 +00:00
Refactor stats tables around descriptor-driven row emission
This commit is contained in:
@@ -590,6 +590,99 @@ emitter_sparse_row(emitter_t *emitter, emitter_row_t *row, bool is_gap) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Descriptor-driven column tables. A descriptor array defines table order;
|
||||||
|
* callers provide a separate index array when JSON requires a different
|
||||||
|
* subset or ordering. The getter lets one engine serve different row types
|
||||||
|
* while keeping derived values (rates, utilization, and so on) with the
|
||||||
|
* table-specific code. A column is active when all its required_flags are
|
||||||
|
* present in the active_flags supplied by the caller; the emitter assigns no
|
||||||
|
* domain-specific meaning to those bits.
|
||||||
|
*/
|
||||||
|
typedef struct emitter_col_desc_s emitter_col_desc_t;
|
||||||
|
struct emitter_col_desc_s {
|
||||||
|
const char *json_key;
|
||||||
|
const char *table_label;
|
||||||
|
emitter_justify_t justify;
|
||||||
|
int width;
|
||||||
|
emitter_type_t type;
|
||||||
|
unsigned required_flags;
|
||||||
|
void (*get)(const void *row, emitter_col_t *col);
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
emitter_col_desc_active(const emitter_col_desc_t *desc,
|
||||||
|
unsigned active_flags) {
|
||||||
|
return (desc->required_flags & ~active_flags) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_col_table_build(const emitter_col_desc_t *descs, size_t ndescs,
|
||||||
|
unsigned active_flags, emitter_row_t *row, emitter_col_t *cols,
|
||||||
|
emitter_row_t *header_row, emitter_col_t *header_cols) {
|
||||||
|
emitter_row_init(row);
|
||||||
|
emitter_row_init(header_row);
|
||||||
|
for (size_t i = 0; i < ndescs; i++) {
|
||||||
|
if (!emitter_col_desc_active(&descs[i], active_flags)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
emitter_col_init(&cols[i], row);
|
||||||
|
cols[i].justify = descs[i].justify;
|
||||||
|
cols[i].width = descs[i].width;
|
||||||
|
cols[i].type = descs[i].type;
|
||||||
|
|
||||||
|
emitter_col_init(&header_cols[i], header_row);
|
||||||
|
header_cols[i].justify = descs[i].justify;
|
||||||
|
header_cols[i].width = descs[i].width;
|
||||||
|
header_cols[i].type = emitter_type_title;
|
||||||
|
header_cols[i].str_val = descs[i].table_label != NULL
|
||||||
|
? descs[i].table_label : descs[i].json_key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_col_table_header(emitter_t *emitter, emitter_row_t *header_row,
|
||||||
|
emitter_col_t *first_header_col, const char *table_prefix,
|
||||||
|
const char *json_key) {
|
||||||
|
first_header_col->width -= (int)strlen(table_prefix);
|
||||||
|
emitter_table_printf(emitter, "%s", table_prefix);
|
||||||
|
emitter_table_row(emitter, header_row);
|
||||||
|
emitter_json_array_kv_begin(emitter, json_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_col_table_fill(const emitter_col_desc_t *descs, size_t ndescs,
|
||||||
|
unsigned active_flags, emitter_col_t *cols, const void *row) {
|
||||||
|
for (size_t i = 0; i < ndescs; i++) {
|
||||||
|
if (!emitter_col_desc_active(&descs[i], active_flags)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cols[i].type = descs[i].type;
|
||||||
|
descs[i].get(row, &cols[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_col_table_emit_json(emitter_t *emitter,
|
||||||
|
const emitter_col_desc_t *descs, size_t ndescs, unsigned active_flags,
|
||||||
|
emitter_col_t *cols, const unsigned *json_order, size_t json_order_len) {
|
||||||
|
if (!emitter_outputs_json(emitter)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < json_order_len; i++) {
|
||||||
|
unsigned col_ind = json_order[i];
|
||||||
|
assert(col_ind < ndescs);
|
||||||
|
const emitter_col_desc_t *desc = &descs[col_ind];
|
||||||
|
if (!emitter_col_desc_active(desc, active_flags)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert(desc->json_key != NULL);
|
||||||
|
emitter_json_kv(emitter, desc->json_key, cols[col_ind].type,
|
||||||
|
(const void *)&cols[col_ind].bool_val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
emitter_begin(emitter_t *emitter) {
|
emitter_begin(emitter_t *emitter) {
|
||||||
if (emitter_outputs_json(emitter)) {
|
if (emitter_outputs_json(emitter)) {
|
||||||
|
|||||||
963
src/stats.c
963
src/stats.c
File diff suppressed because it is too large
Load Diff
@@ -668,6 +668,111 @@ static const char *sparse_row_table =
|
|||||||
" 2\n"
|
" 2\n"
|
||||||
" ---\n";
|
" ---\n";
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint64_t count;
|
||||||
|
uint64_t prof_count;
|
||||||
|
uint64_t requests;
|
||||||
|
} col_table_test_row_t;
|
||||||
|
|
||||||
|
#define COL_TABLE_TEST_GETTER(name, member, value_member) \
|
||||||
|
static void \
|
||||||
|
col_table_test_get_##name(const void *vrow, emitter_col_t *col) { \
|
||||||
|
const col_table_test_row_t *row = vrow; \
|
||||||
|
col->value_member = row->member; \
|
||||||
|
}
|
||||||
|
COL_TABLE_TEST_GETTER(size, size, size_val)
|
||||||
|
COL_TABLE_TEST_GETTER(count, count, uint64_val)
|
||||||
|
COL_TABLE_TEST_GETTER(prof_count, prof_count, uint64_val)
|
||||||
|
COL_TABLE_TEST_GETTER(requests, requests, uint64_val)
|
||||||
|
#undef COL_TABLE_TEST_GETTER
|
||||||
|
|
||||||
|
enum {
|
||||||
|
COL_TABLE_TEST_SIZE,
|
||||||
|
COL_TABLE_TEST_COUNT,
|
||||||
|
COL_TABLE_TEST_PROF_COUNT,
|
||||||
|
COL_TABLE_TEST_REQUESTS,
|
||||||
|
COL_TABLE_TEST_NCOLS
|
||||||
|
};
|
||||||
|
|
||||||
|
#define COL_TABLE_TEST_FLAG_PROF (1U << 0)
|
||||||
|
|
||||||
|
static const emitter_col_desc_t col_table_test_descs[] = {
|
||||||
|
{NULL, "size", emitter_justify_right, 8, emitter_type_size, 0,
|
||||||
|
col_table_test_get_size},
|
||||||
|
{"count", "count", emitter_justify_right, 8, emitter_type_uint64,
|
||||||
|
0, col_table_test_get_count},
|
||||||
|
{"prof_count", "prof", emitter_justify_right, 6, emitter_type_uint64,
|
||||||
|
COL_TABLE_TEST_FLAG_PROF, col_table_test_get_prof_count},
|
||||||
|
{"requests", "requests", emitter_justify_right, 10,
|
||||||
|
emitter_type_uint64, 0, col_table_test_get_requests},
|
||||||
|
};
|
||||||
|
_Static_assert(
|
||||||
|
sizeof(col_table_test_descs) / sizeof(col_table_test_descs[0]) ==
|
||||||
|
COL_TABLE_TEST_NCOLS,
|
||||||
|
"col_table_test_descs must match COL_TABLE_TEST_NCOLS");
|
||||||
|
|
||||||
|
TEST_BEGIN(test_col_desc_flags) {
|
||||||
|
expect_true(emitter_col_desc_active(
|
||||||
|
&col_table_test_descs[COL_TABLE_TEST_COUNT], 0),
|
||||||
|
"A column without requirements should always be active");
|
||||||
|
expect_false(emitter_col_desc_active(
|
||||||
|
&col_table_test_descs[COL_TABLE_TEST_PROF_COUNT], 0),
|
||||||
|
"A column with an unmet requirement should be inactive");
|
||||||
|
expect_true(emitter_col_desc_active(
|
||||||
|
&col_table_test_descs[COL_TABLE_TEST_PROF_COUNT],
|
||||||
|
COL_TABLE_TEST_FLAG_PROF),
|
||||||
|
"A column with a satisfied requirement should be active");
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
static const unsigned col_table_test_json_order[] = {
|
||||||
|
COL_TABLE_TEST_REQUESTS,
|
||||||
|
COL_TABLE_TEST_PROF_COUNT,
|
||||||
|
COL_TABLE_TEST_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
emit_col_table(emitter_t *emitter) {
|
||||||
|
col_table_test_row_t value = {42, 7, 100, 9};
|
||||||
|
emitter_row_t row, header_row;
|
||||||
|
emitter_col_t cols[COL_TABLE_TEST_NCOLS];
|
||||||
|
emitter_col_t header_cols[COL_TABLE_TEST_NCOLS];
|
||||||
|
|
||||||
|
emitter_begin(emitter);
|
||||||
|
emitter_col_table_build(col_table_test_descs, COL_TABLE_TEST_NCOLS,
|
||||||
|
COL_TABLE_TEST_FLAG_PROF, &row, cols, &header_row, header_cols);
|
||||||
|
emitter_col_table_header(
|
||||||
|
emitter, &header_row, &header_cols[0], "mock:", "rows");
|
||||||
|
emitter_col_table_fill(col_table_test_descs, COL_TABLE_TEST_NCOLS,
|
||||||
|
COL_TABLE_TEST_FLAG_PROF, cols, &value);
|
||||||
|
emitter_json_object_begin(emitter);
|
||||||
|
emitter_col_table_emit_json(emitter, col_table_test_descs,
|
||||||
|
COL_TABLE_TEST_NCOLS, COL_TABLE_TEST_FLAG_PROF, cols,
|
||||||
|
col_table_test_json_order, sizeof(col_table_test_json_order) /
|
||||||
|
sizeof(col_table_test_json_order[0]));
|
||||||
|
emitter_json_object_end(emitter);
|
||||||
|
emitter_table_row(emitter, &row);
|
||||||
|
emitter_json_array_end(emitter);
|
||||||
|
emitter_end(emitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *col_table_json =
|
||||||
|
"{\n"
|
||||||
|
"\t\"rows\": [\n"
|
||||||
|
"\t\t{\n"
|
||||||
|
"\t\t\t\"requests\": 9,\n"
|
||||||
|
"\t\t\t\"prof_count\": 100,\n"
|
||||||
|
"\t\t\t\"count\": 7\n"
|
||||||
|
"\t\t}\n"
|
||||||
|
"\t]\n"
|
||||||
|
"}\n";
|
||||||
|
static const char *col_table_json_compact =
|
||||||
|
"{\"rows\":[{\"requests\":9,\"prof_count\":100,\"count\":7}]}";
|
||||||
|
static const char *col_table_table =
|
||||||
|
"mock:size count prof requests\n"
|
||||||
|
" 42 7 100 9\n";
|
||||||
|
|
||||||
#define GENERATE_TEST(feature) \
|
#define GENERATE_TEST(feature) \
|
||||||
TEST_BEGIN(test_##feature) { \
|
TEST_BEGIN(test_##feature) { \
|
||||||
expect_emit_output(emit_##feature, feature##_json, \
|
expect_emit_output(emit_##feature, feature##_json, \
|
||||||
@@ -685,10 +790,12 @@ GENERATE_TEST(json_nested_array)
|
|||||||
GENERATE_TEST(table_row)
|
GENERATE_TEST(table_row)
|
||||||
GENERATE_TEST(row)
|
GENERATE_TEST(row)
|
||||||
GENERATE_TEST(sparse_row)
|
GENERATE_TEST(sparse_row)
|
||||||
|
GENERATE_TEST(col_table)
|
||||||
|
|
||||||
int
|
int
|
||||||
main(void) {
|
main(void) {
|
||||||
return test_no_reentrancy(test_dict, test_table_printf,
|
return test_no_reentrancy(test_dict, test_table_printf,
|
||||||
test_nested_dict, test_types, test_modal, test_json_array,
|
test_nested_dict, test_types, test_modal, test_json_array,
|
||||||
test_json_nested_array, test_table_row, test_row, test_sparse_row);
|
test_json_nested_array, test_table_row, test_row, test_sparse_row,
|
||||||
|
test_col_table, test_col_desc_flags);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user