Expose stats table columns in JSON output and simplify code

This commit is contained in:
Slobodan Predolac 2026-07-17 09:24:26 -07:00
parent e1b5edcc62
commit 82906161eb
4 changed files with 163 additions and 217 deletions

View file

@ -669,10 +669,11 @@ static const char *sparse_row_table =
" ---\n";
typedef struct {
size_t size;
uint64_t count;
uint64_t prof_count;
uint64_t requests;
size_t size;
uint64_t count;
uint64_t prof_count;
const char *label;
uint64_t requests;
} col_table_test_row_t;
#define COL_TABLE_TEST_GETTER(name, member, value_member) \
@ -684,6 +685,7 @@ typedef struct {
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(label, label, str_val)
COL_TABLE_TEST_GETTER(requests, requests, uint64_val)
#undef COL_TABLE_TEST_GETTER
@ -691,6 +693,7 @@ enum {
COL_TABLE_TEST_SIZE,
COL_TABLE_TEST_COUNT,
COL_TABLE_TEST_PROF_COUNT,
COL_TABLE_TEST_LABEL,
COL_TABLE_TEST_REQUESTS,
COL_TABLE_TEST_NCOLS
};
@ -704,6 +707,8 @@ static const emitter_col_desc_t col_table_test_descs[] = {
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},
{"label", "label", emitter_justify_right, 7, emitter_type_title, 0,
col_table_test_get_label},
{"requests", "requests", emitter_justify_right, 10,
emitter_type_uint64, 0, col_table_test_get_requests},
};
@ -726,15 +731,9 @@ TEST_BEGIN(test_col_desc_flags) {
}
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};
col_table_test_row_t value = {42, 7, 100, "row", 9};
emitter_row_t row, header_row;
emitter_col_t cols[COL_TABLE_TEST_NCOLS];
emitter_col_t header_cols[COL_TABLE_TEST_NCOLS];
@ -748,9 +747,7 @@ emit_col_table(emitter_t *emitter) {
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]));
COL_TABLE_TEST_NCOLS, COL_TABLE_TEST_FLAG_PROF, cols);
emitter_json_object_end(emitter);
emitter_table_row(emitter, &row);
emitter_json_array_end(emitter);
@ -761,17 +758,19 @@ static const char *col_table_json =
"{\n"
"\t\"rows\": [\n"
"\t\t{\n"
"\t\t\t\"requests\": 9,\n"
"\t\t\t\"count\": 7,\n"
"\t\t\t\"prof_count\": 100,\n"
"\t\t\t\"count\": 7\n"
"\t\t\t\"label\": \"row\",\n"
"\t\t\t\"requests\": 9\n"
"\t\t}\n"
"\t]\n"
"}\n";
static const char *col_table_json_compact =
"{\"rows\":[{\"requests\":9,\"prof_count\":100,\"count\":7}]}";
"{\"rows\":[{\"count\":7,\"prof_count\":100,\"label\":\"row\","
"\"requests\":9}]}";
static const char *col_table_table =
"mock:size count prof requests\n"
" 42 7 100 9\n";
"mock:size count prof label requests\n"
" 42 7 100 row 9\n";
#define GENERATE_TEST(feature) \
TEST_BEGIN(test_##feature) { \

View file

@ -288,6 +288,70 @@ json_find_named_array(
return array_begin;
}
static void
expect_json_keys_before(const char *begin, const char *end,
const char *const *keys, size_t nkeys, const char *section_name) {
for (size_t i = 0; i < nkeys; i++) {
char search_key[128];
size_t written = malloc_snprintf(
search_key, sizeof(search_key), "\"%s\":", keys[i]);
expect_zu_lt(written, sizeof(search_key), "JSON key is too long");
const char *found = strstr(begin, search_key);
expect_true(found != NULL && found < end,
"%s should contain %s", section_name, keys[i]);
}
}
TEST_BEGIN(test_json_stats_table_columns) {
test_skip_if(!config_stats);
uint64_t epoch = 1;
expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
0, "Unexpected mallctl() failure");
stats_buf_t sbuf;
stats_buf_init(&sbuf);
malloc_stats_print(stats_buf_write_cb, &sbuf, "Ja");
const char *bins_end = NULL;
const char *bins = json_find_named_array(sbuf.buf, "bins", &bins_end);
expect_ptr_not_null(bins, "JSON output should contain bins");
const char *bin_keys[] = {"size", "ind", "allocated", "nmalloc_ps",
"nshards", "regs", "pgs", "util", "nslabs"};
expect_json_keys_before(bins, bins_end, bin_keys,
sizeof(bin_keys) / sizeof(bin_keys[0]), "bins");
const char *lextents_end = NULL;
const char *lextents = json_find_named_array(
sbuf.buf, "lextents", &lextents_end);
expect_ptr_not_null(lextents, "JSON output should contain lextents");
const char *lextent_keys[] = {"size", "ind", "allocated", "nmalloc",
"nmalloc_ps", "ndalloc", "ndalloc_ps", "nrequests",
"nrequests_ps"};
expect_json_keys_before(lextents, lextents_end, lextent_keys,
sizeof(lextent_keys) / sizeof(lextent_keys[0]), "lextents");
const char *extents_end = NULL;
const char *extents = json_find_named_array(
sbuf.buf, "extents", &extents_end);
expect_ptr_not_null(extents, "JSON output should contain extents");
const char *extent_keys[] = {"size", "ind", "ntotal", "total_bytes"};
expect_json_keys_before(extents, extents_end, extent_keys,
sizeof(extent_keys) / sizeof(extent_keys[0]), "extents");
const char *full_slabs_end = NULL;
const char *full_slabs = json_find_named_object(
sbuf.buf, "full_slabs", &full_slabs_end);
expect_ptr_not_null(
full_slabs, "JSON output should contain full_slabs");
const char *hpa_keys[] = {"size", "ind"};
expect_json_keys_before(full_slabs, full_slabs_end, hpa_keys,
sizeof(hpa_keys) / sizeof(hpa_keys[0]), "full_slabs");
stats_buf_fini(&sbuf);
}
TEST_END
TEST_BEGIN(test_json_stats_mutexes) {
test_skip_if(!config_stats);
@ -502,7 +566,8 @@ TEST_END
int
main(void) {
return test_no_reentrancy(test_json_stats_mutexes,
return test_no_reentrancy(test_json_stats_table_columns,
test_json_stats_mutexes,
test_hpa_shard_json_ndirty_huge,
test_hpa_shard_json_contains_sec_stats,
test_hpa_shard_json_contains_retained_stats);