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

@ -591,13 +591,12 @@ 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.
* Descriptor-driven column tables. A descriptor array defines table and JSON
* order; columns with a NULL json_key are table-only. 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 {
@ -666,17 +665,18 @@ emitter_col_table_fill(const emitter_col_desc_t *descs, size_t ndescs,
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) {
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];
emitter_col_t *cols) {
for (size_t i = 0; i < ndescs; i++) {
const emitter_col_desc_t *desc = &descs[i];
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);
if (desc->json_key != NULL) {
emitter_type_t json_type = cols[i].type == emitter_type_title
? emitter_type_string : cols[i].type;
emitter_json_kv(emitter, desc->json_key, json_type,
(const void *)&cols[i].bool_val);
}
}
}

View file

@ -782,54 +782,27 @@ stats_bin_col_get_util(const void *vrow, emitter_col_t *col) {
col->str_val = row->util;
}
enum {
BIN_COL_SIZE,
BIN_COL_IND,
BIN_COL_ALLOCATED,
BIN_COL_NMALLOC,
BIN_COL_NMALLOC_PS,
BIN_COL_NDALLOC,
BIN_COL_NDALLOC_PS,
BIN_COL_NREQUESTS,
BIN_COL_NREQUESTS_PS,
BIN_COL_PROF_LIVE_REQUESTED,
BIN_COL_PROF_LIVE_COUNT,
BIN_COL_PROF_ACCUM_REQUESTED,
BIN_COL_PROF_ACCUM_COUNT,
BIN_COL_NSHARDS,
BIN_COL_CURREGS,
BIN_COL_CURSLABS,
BIN_COL_NONFULL_SLABS,
BIN_COL_REGS,
BIN_COL_PGS,
BIN_COL_SPACER,
BIN_COL_UTIL,
BIN_COL_NFILLS,
BIN_COL_NFILLS_PS,
BIN_COL_NFLUSHES,
BIN_COL_NFLUSHES_PS,
BIN_COL_NSLABS,
BIN_COL_NRESLABS,
BIN_COL_NRESLABS_PS,
BIN_COL_COUNT
};
#define BIN_COL_SIZE 0
#define BIN_DESC(key, label, width, type, flags, name) \
{key, label, emitter_justify_right, width, emitter_type_##type, flags, \
stats_bin_col_get_##name}
static const emitter_col_desc_t stats_bin_cols[] = {
BIN_DESC(NULL, "size", 20, size, STATS_COL_FLAG_NONE, size),
BIN_DESC(NULL, "ind", 4, unsigned, STATS_COL_FLAG_NONE, ind),
BIN_DESC(NULL, "allocated", 14, size, STATS_COL_FLAG_NONE, allocated),
BIN_DESC("size", "size", 20, size, STATS_COL_FLAG_NONE, size),
BIN_DESC("ind", "ind", 4, unsigned, STATS_COL_FLAG_NONE, ind),
BIN_DESC("allocated", "allocated", 14, size, STATS_COL_FLAG_NONE,
allocated),
BIN_DESC("nmalloc", "nmalloc", 14, uint64, STATS_COL_FLAG_NONE,
nmalloc),
BIN_DESC(NULL, "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE, nmalloc_ps),
BIN_DESC("nmalloc_ps", "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE,
nmalloc_ps),
BIN_DESC("ndalloc", "ndalloc", 14, uint64, STATS_COL_FLAG_NONE,
ndalloc),
BIN_DESC(NULL, "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE, ndalloc_ps),
BIN_DESC("ndalloc_ps", "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE,
ndalloc_ps),
BIN_DESC("nrequests", "nrequests", 15, uint64, STATS_COL_FLAG_NONE,
nrequests),
BIN_DESC(NULL, "(#/sec)", 10, uint64, STATS_COL_FLAG_NONE,
BIN_DESC("nrequests_ps", "(#/sec)", 10, uint64, STATS_COL_FLAG_NONE,
nrequests_ps),
BIN_DESC("prof_live_requested", "prof_live_requested", 21, uint64,
STATS_COL_FLAG_PROF, prof_live_requested),
@ -839,48 +812,33 @@ static const emitter_col_desc_t stats_bin_cols[] = {
STATS_COL_FLAG_PROF, prof_accum_requested),
BIN_DESC("prof_accum_count", "prof_accum_count", 17, uint64,
STATS_COL_FLAG_PROF, prof_accum_count),
BIN_DESC(NULL, "nshards", 9, unsigned, STATS_COL_FLAG_NONE, nshards),
BIN_DESC("nshards", "nshards", 9, unsigned, STATS_COL_FLAG_NONE,
nshards),
BIN_DESC("curregs", "curregs", 13, size, STATS_COL_FLAG_NONE, curregs),
BIN_DESC("curslabs", "curslabs", 13, size, STATS_COL_FLAG_NONE,
curslabs),
BIN_DESC("nonfull_slabs", "nonfull_slabs", 15, size,
STATS_COL_FLAG_NONE,
nonfull_slabs),
BIN_DESC(NULL, "regs", 5, unsigned, STATS_COL_FLAG_NONE, regs),
BIN_DESC(NULL, "pgs", 4, size, STATS_COL_FLAG_NONE, pgs),
BIN_DESC("regs", "regs", 5, unsigned, STATS_COL_FLAG_NONE, regs),
BIN_DESC("pgs", "pgs", 4, size, STATS_COL_FLAG_NONE, pgs),
BIN_DESC(NULL, " ", 1, title, STATS_COL_FLAG_NONE, spacer),
BIN_DESC(NULL, "util", 6, title, STATS_COL_FLAG_NONE, util),
BIN_DESC("util", "util", 6, title, STATS_COL_FLAG_NONE, util),
BIN_DESC("nfills", "nfills", 13, uint64, STATS_COL_FLAG_NONE, nfills),
BIN_DESC(NULL, "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE, nfills_ps),
BIN_DESC("nfills_ps", "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE,
nfills_ps),
BIN_DESC("nflushes", "nflushes", 13, uint64, STATS_COL_FLAG_NONE,
nflushes),
BIN_DESC(NULL, "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE,
BIN_DESC("nflushes_ps", "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE,
nflushes_ps),
BIN_DESC(NULL, "nslabs", 13, uint64, STATS_COL_FLAG_NONE, nslabs),
BIN_DESC("nslabs", "nslabs", 13, uint64, STATS_COL_FLAG_NONE, nslabs),
BIN_DESC("nreslabs", "nreslabs", 13, uint64, STATS_COL_FLAG_NONE,
nreslabs),
BIN_DESC(NULL, "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE,
BIN_DESC("nreslabs_ps", "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE,
nreslabs_ps),
};
#undef BIN_DESC
_Static_assert(sizeof(stats_bin_cols) / sizeof(stats_bin_cols[0]) ==
BIN_COL_COUNT, "stats_bin_cols must match BIN_COL_COUNT");
static const unsigned stats_bin_json_order[] = {
BIN_COL_NMALLOC,
BIN_COL_NDALLOC,
BIN_COL_CURREGS,
BIN_COL_NREQUESTS,
BIN_COL_PROF_LIVE_REQUESTED,
BIN_COL_PROF_LIVE_COUNT,
BIN_COL_PROF_ACCUM_REQUESTED,
BIN_COL_PROF_ACCUM_COUNT,
BIN_COL_NFILLS,
BIN_COL_NFLUSHES,
BIN_COL_NRESLABS,
BIN_COL_CURSLABS,
BIN_COL_NONFULL_SLABS,
};
#define BIN_COL_COUNT (sizeof(stats_bin_cols) / sizeof(stats_bin_cols[0]))
static void
stats_emit_arena_bin_row(emitter_t *emitter, emitter_row_t *table_row,
@ -892,8 +850,7 @@ stats_emit_arena_bin_row(emitter_t *emitter, emitter_row_t *table_row,
stats_bin_cols, BIN_COL_COUNT, active_flags, cols, row);
emitter_json_object_begin(emitter);
emitter_col_table_emit_json(emitter, stats_bin_cols, BIN_COL_COUNT,
active_flags, cols, stats_bin_json_order,
sizeof(stats_bin_json_order) / sizeof(stats_bin_json_order[0]));
active_flags, cols);
if (mutex) {
emitter_json_object_kv_begin(emitter, "mutex");
mutex_stats_emit(emitter, NULL, mutex64, mutex32);
@ -1068,41 +1025,29 @@ stats_lextent_col_get_allocated(const void *vrow, emitter_col_t *col) {
col->size_val = row->lextent->curlextents * row->lextent->lextent_size;
}
enum {
LEXTENT_COL_SIZE,
LEXTENT_COL_IND,
LEXTENT_COL_ALLOCATED,
LEXTENT_COL_NMALLOC,
LEXTENT_COL_NMALLOC_PS,
LEXTENT_COL_NDALLOC,
LEXTENT_COL_NDALLOC_PS,
LEXTENT_COL_NREQUESTS,
LEXTENT_COL_NREQUESTS_PS,
LEXTENT_COL_PROF_LIVE_REQUESTED,
LEXTENT_COL_PROF_LIVE_COUNT,
LEXTENT_COL_PROF_ACCUM_REQUESTED,
LEXTENT_COL_PROF_ACCUM_COUNT,
LEXTENT_COL_CURLEXTENTS,
LEXTENT_COL_COUNT
};
#define LEXTENT_COL_SIZE 0
#define LEXTENT_DESC(key, label, width, type, flags, name) \
{key, label, emitter_justify_right, width, emitter_type_##type, flags, \
stats_lextent_col_get_##name}
static const emitter_col_desc_t stats_lextent_cols[] = {
LEXTENT_DESC(NULL, "size", 20, size, STATS_COL_FLAG_NONE, size),
LEXTENT_DESC(NULL, "ind", 4, unsigned, STATS_COL_FLAG_NONE, ind),
LEXTENT_DESC(NULL, "allocated", 13, size, STATS_COL_FLAG_NONE,
LEXTENT_DESC("size", "size", 20, size, STATS_COL_FLAG_NONE, size),
LEXTENT_DESC("ind", "ind", 4, unsigned, STATS_COL_FLAG_NONE, ind),
LEXTENT_DESC("allocated", "allocated", 13, size, STATS_COL_FLAG_NONE,
allocated),
LEXTENT_DESC(NULL, "nmalloc", 13, uint64, STATS_COL_FLAG_NONE, nmalloc),
LEXTENT_DESC(NULL, "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE,
LEXTENT_DESC("nmalloc", "nmalloc", 13, uint64, STATS_COL_FLAG_NONE,
nmalloc),
LEXTENT_DESC("nmalloc_ps", "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE,
nmalloc_ps),
LEXTENT_DESC(NULL, "ndalloc", 13, uint64, STATS_COL_FLAG_NONE, ndalloc),
LEXTENT_DESC(NULL, "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE,
LEXTENT_DESC("ndalloc", "ndalloc", 13, uint64, STATS_COL_FLAG_NONE,
ndalloc),
LEXTENT_DESC("ndalloc_ps", "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE,
ndalloc_ps),
LEXTENT_DESC(NULL, "nrequests", 13, uint64, STATS_COL_FLAG_NONE,
LEXTENT_DESC("nrequests", "nrequests", 13, uint64,
STATS_COL_FLAG_NONE,
nrequests),
LEXTENT_DESC(NULL, "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE,
LEXTENT_DESC("nrequests_ps", "(#/sec)", 8, uint64,
STATS_COL_FLAG_NONE,
nrequests_ps),
LEXTENT_DESC("prof_live_requested", "prof_live_requested", 21, uint64,
STATS_COL_FLAG_PROF, prof_live_requested),
@ -1116,16 +1061,8 @@ static const emitter_col_desc_t stats_lextent_cols[] = {
STATS_COL_FLAG_NONE, curlextents),
};
#undef LEXTENT_DESC
_Static_assert(sizeof(stats_lextent_cols) / sizeof(stats_lextent_cols[0]) ==
LEXTENT_COL_COUNT, "stats_lextent_cols must match LEXTENT_COL_COUNT");
static const unsigned stats_lextent_json_order[] = {
LEXTENT_COL_PROF_LIVE_REQUESTED,
LEXTENT_COL_PROF_LIVE_COUNT,
LEXTENT_COL_PROF_ACCUM_REQUESTED,
LEXTENT_COL_PROF_ACCUM_COUNT,
LEXTENT_COL_CURLEXTENTS,
};
#define LEXTENT_COL_COUNT \
(sizeof(stats_lextent_cols) / sizeof(stats_lextent_cols[0]))
static void
stats_emit_arena_lextent_row(emitter_t *emitter, emitter_row_t *table_row,
@ -1134,16 +1071,14 @@ stats_emit_arena_lextent_row(emitter_t *emitter, emitter_row_t *table_row,
unsigned active_flags = stats_col_active_flags(prof_stats_on);
emitter_col_table_fill(stats_lextent_cols, LEXTENT_COL_COUNT,
active_flags, cols, row);
emitter_json_object_begin(emitter);
emitter_col_table_emit_json(emitter, stats_lextent_cols,
LEXTENT_COL_COUNT, active_flags, cols);
emitter_json_object_end(emitter);
char size_buf[48];
stats_size_col_set(&cols[LEXTENT_COL_SIZE], row->lextent->lextent_size,
prev_size, size_buf, sizeof(size_buf));
emitter_json_object_begin(emitter);
emitter_col_table_emit_json(emitter, stats_lextent_cols,
LEXTENT_COL_COUNT, active_flags, cols, stats_lextent_json_order,
sizeof(stats_lextent_json_order) /
sizeof(stats_lextent_json_order[0]));
emitter_table_sparse_row(emitter, table_row, is_gap);
emitter_json_object_end(emitter);
}
JEMALLOC_COLD
@ -1241,29 +1176,15 @@ stats_extent_col_get_ind(const void *vrow, emitter_col_t *col) {
col->unsigned_val = row->ind;
}
enum {
EXTENT_COL_SIZE,
EXTENT_COL_IND,
EXTENT_COL_NDIRTY,
EXTENT_COL_DIRTY,
EXTENT_COL_NMUZZY,
EXTENT_COL_MUZZY,
EXTENT_COL_NRETAINED,
EXTENT_COL_RETAINED,
EXTENT_COL_NPINNED,
EXTENT_COL_PINNED,
EXTENT_COL_NTOTAL,
EXTENT_COL_TOTAL,
EXTENT_COL_COUNT
};
#define EXTENT_COL_SIZE 0
#define EXTENT_DESC(key, label, name) \
{key, label, emitter_justify_right, 13, emitter_type_size, \
STATS_COL_FLAG_NONE, stats_extent_col_get_##name}
static const emitter_col_desc_t stats_extent_cols[] = {
{NULL, "size", emitter_justify_right, 20, emitter_type_size,
{"size", "size", emitter_justify_right, 20, emitter_type_size,
STATS_COL_FLAG_NONE, stats_extent_col_get_size},
{NULL, "ind", emitter_justify_right, 4, emitter_type_unsigned,
{"ind", "ind", emitter_justify_right, 4, emitter_type_unsigned,
STATS_COL_FLAG_NONE, stats_extent_col_get_ind},
EXTENT_DESC("ndirty", "ndirty", ndirty),
EXTENT_DESC("dirty_bytes", "dirty", dirty),
@ -1273,23 +1194,12 @@ static const emitter_col_desc_t stats_extent_cols[] = {
EXTENT_DESC("retained_bytes", "retained", retained),
EXTENT_DESC("npinned", "npinned", npinned),
EXTENT_DESC("pinned_bytes", "pinned", pinned),
EXTENT_DESC(NULL, "ntotal", ntotal),
EXTENT_DESC(NULL, "total", total),
EXTENT_DESC("ntotal", "ntotal", ntotal),
EXTENT_DESC("total_bytes", "total", total),
};
#undef EXTENT_DESC
_Static_assert(sizeof(stats_extent_cols) / sizeof(stats_extent_cols[0]) ==
EXTENT_COL_COUNT, "stats_extent_cols must match EXTENT_COL_COUNT");
static const unsigned stats_extent_json_order[] = {
EXTENT_COL_NDIRTY,
EXTENT_COL_NMUZZY,
EXTENT_COL_NRETAINED,
EXTENT_COL_NPINNED,
EXTENT_COL_DIRTY,
EXTENT_COL_MUZZY,
EXTENT_COL_RETAINED,
EXTENT_COL_PINNED,
};
#define EXTENT_COL_COUNT \
(sizeof(stats_extent_cols) / sizeof(stats_extent_cols[0]))
static void
stats_emit_arena_extent_row(emitter_t *emitter, emitter_row_t *table_row,
@ -1297,15 +1207,13 @@ stats_emit_arena_extent_row(emitter_t *emitter, emitter_row_t *table_row,
size_t prev_size, bool is_gap) {
emitter_col_table_fill(stats_extent_cols, EXTENT_COL_COUNT,
STATS_COL_FLAG_NONE, cols, row);
emitter_json_object_begin(emitter);
emitter_col_table_emit_json(emitter, stats_extent_cols, EXTENT_COL_COUNT,
STATS_COL_FLAG_NONE, cols);
emitter_json_object_end(emitter);
char size_buf[48];
stats_size_col_set(&cols[EXTENT_COL_SIZE], row->size, prev_size,
size_buf, sizeof(size_buf));
emitter_json_object_begin(emitter);
emitter_col_table_emit_json(emitter, stats_extent_cols, EXTENT_COL_COUNT,
STATS_COL_FLAG_NONE, cols, stats_extent_json_order,
sizeof(stats_extent_json_order) /
sizeof(stats_extent_json_order[0]));
emitter_json_object_end(emitter);
emitter_table_sparse_row(emitter, table_row, is_gap);
}
@ -1573,26 +1481,15 @@ stats_hpa_slab_col_get_ind(const void *vrow, emitter_col_t *col) {
}
}
enum {
HPA_SLAB_COL_SIZE,
HPA_SLAB_COL_IND,
HPA_SLAB_COL_NPAGESLABS_HUGE,
HPA_SLAB_COL_NACTIVE_HUGE,
HPA_SLAB_COL_NDIRTY_HUGE,
HPA_SLAB_COL_NPAGESLABS_NONHUGE,
HPA_SLAB_COL_NACTIVE_NONHUGE,
HPA_SLAB_COL_NDIRTY_NONHUGE,
HPA_SLAB_COL_NRETAINED_NONHUGE,
HPA_SLAB_COL_COUNT
};
#define HPA_SLAB_COL_SIZE 0
#define HPA_SLAB_DESC(name, width) \
{#name, #name, emitter_justify_right, width, emitter_type_size, \
STATS_COL_FLAG_NONE, stats_hpa_slab_col_get_##name}
static const emitter_col_desc_t stats_hpa_slab_cols[] = {
{NULL, "size", emitter_justify_right, 20, emitter_type_size,
{"size", "size", emitter_justify_right, 20, emitter_type_size,
STATS_COL_FLAG_NONE, stats_hpa_slab_col_get_size},
{NULL, "ind", emitter_justify_right, 4, emitter_type_unsigned,
{"ind", "ind", emitter_justify_right, 4, emitter_type_unsigned,
STATS_COL_FLAG_NONE, stats_hpa_slab_col_get_ind},
HPA_SLAB_DESC(npageslabs_huge, 16),
HPA_SLAB_DESC(nactive_huge, 16),
@ -1603,20 +1500,8 @@ static const emitter_col_desc_t stats_hpa_slab_cols[] = {
HPA_SLAB_DESC(nretained_nonhuge, 20),
};
#undef HPA_SLAB_DESC
_Static_assert(
sizeof(stats_hpa_slab_cols) / sizeof(stats_hpa_slab_cols[0]) ==
HPA_SLAB_COL_COUNT,
"stats_hpa_slab_cols must match HPA_SLAB_COL_COUNT");
static const unsigned stats_hpa_slab_json_order[] = {
HPA_SLAB_COL_NPAGESLABS_HUGE,
HPA_SLAB_COL_NACTIVE_HUGE,
HPA_SLAB_COL_NDIRTY_HUGE,
HPA_SLAB_COL_NPAGESLABS_NONHUGE,
HPA_SLAB_COL_NACTIVE_NONHUGE,
HPA_SLAB_COL_NDIRTY_NONHUGE,
HPA_SLAB_COL_NRETAINED_NONHUGE,
};
#define HPA_SLAB_COL_COUNT \
(sizeof(stats_hpa_slab_cols) / sizeof(stats_hpa_slab_cols[0]))
static void
stats_emit_arena_hpa_slab_row(emitter_t *emitter,
@ -1625,22 +1510,19 @@ stats_emit_arena_hpa_slab_row(emitter_t *emitter,
bool sparse, bool is_gap) {
emitter_col_table_fill(stats_hpa_slab_cols, HPA_SLAB_COL_COUNT,
STATS_COL_FLAG_NONE, cols, row);
char size_buf[48];
if (row->size_title == NULL) {
stats_size_col_set(&cols[HPA_SLAB_COL_SIZE], row->size,
row->prev_size, size_buf, sizeof(size_buf));
}
if (json_key != NULL) {
emitter_json_object_kv_begin(emitter, json_key);
} else {
emitter_json_object_begin(emitter);
}
emitter_col_table_emit_json(emitter, stats_hpa_slab_cols,
HPA_SLAB_COL_COUNT, STATS_COL_FLAG_NONE, cols,
stats_hpa_slab_json_order,
sizeof(stats_hpa_slab_json_order) /
sizeof(stats_hpa_slab_json_order[0]));
HPA_SLAB_COL_COUNT, STATS_COL_FLAG_NONE, cols);
emitter_json_object_end(emitter);
char size_buf[48];
if (row->size_title == NULL) {
stats_size_col_set(&cols[HPA_SLAB_COL_SIZE], row->size,
row->prev_size, size_buf, sizeof(size_buf));
}
if (sparse) {
emitter_table_sparse_row(emitter, table_row, is_gap);
} else {

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);