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 80c8fcb4f9
commit 04aad97faa
4 changed files with 295 additions and 226 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; * Descriptor-driven column tables. A descriptor array defines table and JSON
* callers provide a separate index array when JSON requires a different * order; columns with a NULL json_key are table-only. The getter lets one
* subset or ordering. The getter lets one engine serve different row types * engine serve different row types while keeping derived values (rates,
* while keeping derived values (rates, utilization, and so on) with the * utilization, and so on) with the table-specific code. A column is active
* table-specific code. A column is active when all its required_flags are * when all its required_flags are present in the active_flags supplied by the
* present in the active_flags supplied by the caller; the emitter assigns no * caller; the emitter assigns no domain-specific meaning to those bits.
* domain-specific meaning to those bits.
*/ */
typedef struct emitter_col_desc_s emitter_col_desc_t; typedef struct emitter_col_desc_s emitter_col_desc_t;
struct emitter_col_desc_s { struct emitter_col_desc_s {
@@ -666,20 +665,21 @@ emitter_col_table_fill(const emitter_col_desc_t *descs, size_t ndescs,
static inline void static inline void
emitter_col_table_emit_json(emitter_t *emitter, emitter_col_table_emit_json(emitter_t *emitter,
const emitter_col_desc_t *descs, size_t ndescs, unsigned active_flags, 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) { emitter_col_t *cols) {
if (!emitter_outputs_json(emitter)) { if (!emitter_outputs_json(emitter)) {
return; return;
} }
for (size_t i = 0; i < json_order_len; i++) { for (size_t i = 0; i < ndescs; i++) {
unsigned col_ind = json_order[i]; const emitter_col_desc_t *desc = &descs[i];
assert(col_ind < ndescs);
const emitter_col_desc_t *desc = &descs[col_ind];
if (!emitter_col_desc_active(desc, active_flags)) { if (!emitter_col_desc_active(desc, active_flags)) {
continue; continue;
} }
assert(desc->json_key != NULL); if (desc->json_key != NULL) {
emitter_json_kv(emitter, desc->json_key, cols[col_ind].type, emitter_type_t json_type = cols[i].type == emitter_type_title
(const void *)&cols[col_ind].bool_val); ? 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; col->str_val = row->util;
} }
enum { #define BIN_COL_SIZE 0
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_DESC(key, label, width, type, flags, name) \ #define BIN_DESC(key, label, width, type, flags, name) \
{key, label, emitter_justify_right, width, emitter_type_##type, flags, \ {key, label, emitter_justify_right, width, emitter_type_##type, flags, \
stats_bin_col_get_##name} stats_bin_col_get_##name}
static const emitter_col_desc_t stats_bin_cols[] = { static const emitter_col_desc_t stats_bin_cols[] = {
BIN_DESC(NULL, "size", 20, size, STATS_COL_FLAG_NONE, size), BIN_DESC("size", "size", 20, size, STATS_COL_FLAG_NONE, size),
BIN_DESC(NULL, "ind", 4, unsigned, STATS_COL_FLAG_NONE, ind), BIN_DESC("ind", "ind", 4, unsigned, STATS_COL_FLAG_NONE, ind),
BIN_DESC(NULL, "allocated", 14, size, STATS_COL_FLAG_NONE, allocated), BIN_DESC("allocated", "allocated", 14, size, STATS_COL_FLAG_NONE,
allocated),
BIN_DESC("nmalloc", "nmalloc", 14, uint64, STATS_COL_FLAG_NONE, BIN_DESC("nmalloc", "nmalloc", 14, uint64, STATS_COL_FLAG_NONE,
nmalloc), 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, BIN_DESC("ndalloc", "ndalloc", 14, uint64, STATS_COL_FLAG_NONE,
ndalloc), 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, BIN_DESC("nrequests", "nrequests", 15, uint64, STATS_COL_FLAG_NONE,
nrequests), nrequests),
BIN_DESC(NULL, "(#/sec)", 10, uint64, STATS_COL_FLAG_NONE, BIN_DESC("nrequests_ps", "(#/sec)", 10, uint64, STATS_COL_FLAG_NONE,
nrequests_ps), nrequests_ps),
BIN_DESC("prof_live_requested", "prof_live_requested", 21, uint64, BIN_DESC("prof_live_requested", "prof_live_requested", 21, uint64,
STATS_COL_FLAG_PROF, prof_live_requested), 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), STATS_COL_FLAG_PROF, prof_accum_requested),
BIN_DESC("prof_accum_count", "prof_accum_count", 17, uint64, BIN_DESC("prof_accum_count", "prof_accum_count", 17, uint64,
STATS_COL_FLAG_PROF, prof_accum_count), 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("curregs", "curregs", 13, size, STATS_COL_FLAG_NONE, curregs),
BIN_DESC("curslabs", "curslabs", 13, size, STATS_COL_FLAG_NONE, BIN_DESC("curslabs", "curslabs", 13, size, STATS_COL_FLAG_NONE,
curslabs), curslabs),
BIN_DESC("nonfull_slabs", "nonfull_slabs", 15, size, BIN_DESC("nonfull_slabs", "nonfull_slabs", 15, size,
STATS_COL_FLAG_NONE, STATS_COL_FLAG_NONE,
nonfull_slabs), nonfull_slabs),
BIN_DESC(NULL, "regs", 5, unsigned, STATS_COL_FLAG_NONE, regs), BIN_DESC("regs", "regs", 5, unsigned, STATS_COL_FLAG_NONE, regs),
BIN_DESC(NULL, "pgs", 4, size, STATS_COL_FLAG_NONE, pgs), BIN_DESC("pgs", "pgs", 4, size, STATS_COL_FLAG_NONE, pgs),
BIN_DESC(NULL, " ", 1, title, STATS_COL_FLAG_NONE, spacer), 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("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, BIN_DESC("nflushes", "nflushes", 13, uint64, STATS_COL_FLAG_NONE,
nflushes), nflushes),
BIN_DESC(NULL, "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE, BIN_DESC("nflushes_ps", "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE,
nflushes_ps), 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, BIN_DESC("nreslabs", "nreslabs", 13, uint64, STATS_COL_FLAG_NONE,
nreslabs), nreslabs),
BIN_DESC(NULL, "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE, BIN_DESC("nreslabs_ps", "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE,
nreslabs_ps), nreslabs_ps),
}; };
#undef BIN_DESC #undef BIN_DESC
_Static_assert(sizeof(stats_bin_cols) / sizeof(stats_bin_cols[0]) == #define BIN_COL_COUNT (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,
};
static void static void
stats_emit_arena_bin_row(emitter_t *emitter, emitter_row_t *table_row, 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); stats_bin_cols, BIN_COL_COUNT, active_flags, cols, row);
emitter_json_object_begin(emitter); emitter_json_object_begin(emitter);
emitter_col_table_emit_json(emitter, stats_bin_cols, BIN_COL_COUNT, emitter_col_table_emit_json(emitter, stats_bin_cols, BIN_COL_COUNT,
active_flags, cols, stats_bin_json_order, active_flags, cols);
sizeof(stats_bin_json_order) / sizeof(stats_bin_json_order[0]));
if (mutex) { if (mutex) {
emitter_json_object_kv_begin(emitter, "mutex"); emitter_json_object_kv_begin(emitter, "mutex");
mutex_stats_emit(emitter, NULL, mutex64, mutex32); 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; col->size_val = row->lextent->curlextents * row->lextent->lextent_size;
} }
enum { #define LEXTENT_COL_SIZE 0
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_DESC(key, label, width, type, flags, name) \ #define LEXTENT_DESC(key, label, width, type, flags, name) \
{key, label, emitter_justify_right, width, emitter_type_##type, flags, \ {key, label, emitter_justify_right, width, emitter_type_##type, flags, \
stats_lextent_col_get_##name} stats_lextent_col_get_##name}
static const emitter_col_desc_t stats_lextent_cols[] = { static const emitter_col_desc_t stats_lextent_cols[] = {
LEXTENT_DESC(NULL, "size", 20, size, STATS_COL_FLAG_NONE, size), LEXTENT_DESC("size", "size", 20, size, STATS_COL_FLAG_NONE, size),
LEXTENT_DESC(NULL, "ind", 4, unsigned, STATS_COL_FLAG_NONE, ind), LEXTENT_DESC("ind", "ind", 4, unsigned, STATS_COL_FLAG_NONE, ind),
LEXTENT_DESC(NULL, "allocated", 13, size, STATS_COL_FLAG_NONE, LEXTENT_DESC("allocated", "allocated", 13, size, STATS_COL_FLAG_NONE,
allocated), allocated),
LEXTENT_DESC(NULL, "nmalloc", 13, uint64, STATS_COL_FLAG_NONE, nmalloc), LEXTENT_DESC("nmalloc", "nmalloc", 13, uint64, STATS_COL_FLAG_NONE,
LEXTENT_DESC(NULL, "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE, nmalloc),
LEXTENT_DESC("nmalloc_ps", "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE,
nmalloc_ps), nmalloc_ps),
LEXTENT_DESC(NULL, "ndalloc", 13, uint64, STATS_COL_FLAG_NONE, ndalloc), LEXTENT_DESC("ndalloc", "ndalloc", 13, uint64, STATS_COL_FLAG_NONE,
LEXTENT_DESC(NULL, "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE, ndalloc),
LEXTENT_DESC("ndalloc_ps", "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE,
ndalloc_ps), ndalloc_ps),
LEXTENT_DESC(NULL, "nrequests", 13, uint64, STATS_COL_FLAG_NONE, LEXTENT_DESC("nrequests", "nrequests", 13, uint64,
STATS_COL_FLAG_NONE,
nrequests), nrequests),
LEXTENT_DESC(NULL, "(#/sec)", 8, uint64, STATS_COL_FLAG_NONE, LEXTENT_DESC("nrequests_ps", "(#/sec)", 8, uint64,
STATS_COL_FLAG_NONE,
nrequests_ps), nrequests_ps),
LEXTENT_DESC("prof_live_requested", "prof_live_requested", 21, uint64, LEXTENT_DESC("prof_live_requested", "prof_live_requested", 21, uint64,
STATS_COL_FLAG_PROF, prof_live_requested), 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), STATS_COL_FLAG_NONE, curlextents),
}; };
#undef LEXTENT_DESC #undef LEXTENT_DESC
_Static_assert(sizeof(stats_lextent_cols) / sizeof(stats_lextent_cols[0]) == #define LEXTENT_COL_COUNT \
LEXTENT_COL_COUNT, "stats_lextent_cols must match LEXTENT_COL_COUNT"); (sizeof(stats_lextent_cols) / sizeof(stats_lextent_cols[0]))
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,
};
static void static void
stats_emit_arena_lextent_row(emitter_t *emitter, emitter_row_t *table_row, stats_emit_arena_lextent_row(emitter_t *emitter, emitter_row_t *table_row,
@@ -1135,15 +1072,13 @@ stats_emit_arena_lextent_row(emitter_t *emitter, emitter_row_t *table_row,
unsigned active_flags = stats_col_active_flags(prof_stats_on); unsigned active_flags = stats_col_active_flags(prof_stats_on);
emitter_col_table_fill(stats_lextent_cols, LEXTENT_COL_COUNT, emitter_col_table_fill(stats_lextent_cols, LEXTENT_COL_COUNT,
active_flags, cols, row); active_flags, cols, row);
stats_size_col_set(&cols[LEXTENT_COL_SIZE], row->lextent->lextent_size,
prev_size, size_buf, size_buf_size);
emitter_json_object_begin(emitter); emitter_json_object_begin(emitter);
emitter_col_table_emit_json(emitter, stats_lextent_cols, emitter_col_table_emit_json(emitter, stats_lextent_cols,
LEXTENT_COL_COUNT, active_flags, cols, stats_lextent_json_order, LEXTENT_COL_COUNT, active_flags, cols);
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); emitter_json_object_end(emitter);
stats_size_col_set(&cols[LEXTENT_COL_SIZE], row->lextent->lextent_size,
prev_size, size_buf, size_buf_size);
emitter_table_sparse_row(emitter, table_row, is_gap);
} }
JEMALLOC_COLD JEMALLOC_COLD
@@ -1242,29 +1177,15 @@ stats_extent_col_get_ind(const void *vrow, emitter_col_t *col) {
col->unsigned_val = row->ind; col->unsigned_val = row->ind;
} }
enum { #define EXTENT_COL_SIZE 0
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_DESC(key, label, name) \ #define EXTENT_DESC(key, label, name) \
{key, label, emitter_justify_right, 13, emitter_type_size, \ {key, label, emitter_justify_right, 13, emitter_type_size, \
STATS_COL_FLAG_NONE, stats_extent_col_get_##name} STATS_COL_FLAG_NONE, stats_extent_col_get_##name}
static const emitter_col_desc_t stats_extent_cols[] = { 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}, 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}, STATS_COL_FLAG_NONE, stats_extent_col_get_ind},
EXTENT_DESC("ndirty", "ndirty", ndirty), EXTENT_DESC("ndirty", "ndirty", ndirty),
EXTENT_DESC("dirty_bytes", "dirty", dirty), EXTENT_DESC("dirty_bytes", "dirty", dirty),
@@ -1274,23 +1195,12 @@ static const emitter_col_desc_t stats_extent_cols[] = {
EXTENT_DESC("retained_bytes", "retained", retained), EXTENT_DESC("retained_bytes", "retained", retained),
EXTENT_DESC("npinned", "npinned", npinned), EXTENT_DESC("npinned", "npinned", npinned),
EXTENT_DESC("pinned_bytes", "pinned", pinned), EXTENT_DESC("pinned_bytes", "pinned", pinned),
EXTENT_DESC(NULL, "ntotal", ntotal), EXTENT_DESC("ntotal", "ntotal", ntotal),
EXTENT_DESC(NULL, "total", total), EXTENT_DESC("total_bytes", "total", total),
}; };
#undef EXTENT_DESC #undef EXTENT_DESC
_Static_assert(sizeof(stats_extent_cols) / sizeof(stats_extent_cols[0]) == #define EXTENT_COL_COUNT \
EXTENT_COL_COUNT, "stats_extent_cols must match EXTENT_COL_COUNT"); (sizeof(stats_extent_cols) / sizeof(stats_extent_cols[0]))
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,
};
static void static void
stats_emit_arena_extent_row(emitter_t *emitter, emitter_row_t *table_row, stats_emit_arena_extent_row(emitter_t *emitter, emitter_row_t *table_row,
@@ -1298,14 +1208,12 @@ stats_emit_arena_extent_row(emitter_t *emitter, emitter_row_t *table_row,
size_t prev_size, char *size_buf, size_t size_buf_size, bool is_gap) { size_t prev_size, char *size_buf, size_t size_buf_size, bool is_gap) {
emitter_col_table_fill(stats_extent_cols, EXTENT_COL_COUNT, emitter_col_table_fill(stats_extent_cols, EXTENT_COL_COUNT,
STATS_COL_FLAG_NONE, cols, row); STATS_COL_FLAG_NONE, cols, row);
stats_size_col_set(&cols[EXTENT_COL_SIZE], row->size, prev_size,
size_buf, size_buf_size);
emitter_json_object_begin(emitter); emitter_json_object_begin(emitter);
emitter_col_table_emit_json(emitter, stats_extent_cols, EXTENT_COL_COUNT, emitter_col_table_emit_json(emitter, stats_extent_cols, EXTENT_COL_COUNT,
STATS_COL_FLAG_NONE, cols, stats_extent_json_order, STATS_COL_FLAG_NONE, cols);
sizeof(stats_extent_json_order) /
sizeof(stats_extent_json_order[0]));
emitter_json_object_end(emitter); emitter_json_object_end(emitter);
stats_size_col_set(&cols[EXTENT_COL_SIZE], row->size, prev_size,
size_buf, size_buf_size);
emitter_table_sparse_row(emitter, table_row, is_gap); emitter_table_sparse_row(emitter, table_row, is_gap);
} }
@@ -1575,26 +1483,15 @@ stats_hpa_slab_col_get_ind(const void *vrow, emitter_col_t *col) {
} }
} }
enum { #define HPA_SLAB_COL_SIZE 0
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_DESC(name, width) \ #define HPA_SLAB_DESC(name, width) \
{#name, #name, emitter_justify_right, width, emitter_type_size, \ {#name, #name, emitter_justify_right, width, emitter_type_size, \
STATS_COL_FLAG_NONE, stats_hpa_slab_col_get_##name} STATS_COL_FLAG_NONE, stats_hpa_slab_col_get_##name}
static const emitter_col_desc_t stats_hpa_slab_cols[] = { 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}, 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}, STATS_COL_FLAG_NONE, stats_hpa_slab_col_get_ind},
HPA_SLAB_DESC(npageslabs_huge, 16), HPA_SLAB_DESC(npageslabs_huge, 16),
HPA_SLAB_DESC(nactive_huge, 16), HPA_SLAB_DESC(nactive_huge, 16),
@@ -1605,20 +1502,8 @@ static const emitter_col_desc_t stats_hpa_slab_cols[] = {
HPA_SLAB_DESC(nretained_nonhuge, 20), HPA_SLAB_DESC(nretained_nonhuge, 20),
}; };
#undef HPA_SLAB_DESC #undef HPA_SLAB_DESC
_Static_assert( #define HPA_SLAB_COL_COUNT \
sizeof(stats_hpa_slab_cols) / sizeof(stats_hpa_slab_cols[0]) == (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,
};
static void static void
stats_emit_arena_hpa_slab_row(emitter_t *emitter, stats_emit_arena_hpa_slab_row(emitter_t *emitter,
@@ -1627,21 +1512,18 @@ stats_emit_arena_hpa_slab_row(emitter_t *emitter,
char *size_buf, size_t size_buf_size, bool sparse, bool is_gap) { char *size_buf, size_t size_buf_size, bool sparse, bool is_gap) {
emitter_col_table_fill(stats_hpa_slab_cols, HPA_SLAB_COL_COUNT, emitter_col_table_fill(stats_hpa_slab_cols, HPA_SLAB_COL_COUNT,
STATS_COL_FLAG_NONE, cols, row); STATS_COL_FLAG_NONE, cols, row);
if (row->size_title == NULL) {
stats_size_col_set(&cols[HPA_SLAB_COL_SIZE], row->size,
row->prev_size, size_buf, size_buf_size);
}
if (json_key != NULL) { if (json_key != NULL) {
emitter_json_object_kv_begin(emitter, json_key); emitter_json_object_kv_begin(emitter, json_key);
} else { } else {
emitter_json_object_begin(emitter); emitter_json_object_begin(emitter);
} }
emitter_col_table_emit_json(emitter, stats_hpa_slab_cols, emitter_col_table_emit_json(emitter, stats_hpa_slab_cols,
HPA_SLAB_COL_COUNT, STATS_COL_FLAG_NONE, 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]));
emitter_json_object_end(emitter); emitter_json_object_end(emitter);
if (row->size_title == NULL) {
stats_size_col_set(&cols[HPA_SLAB_COL_SIZE], row->size,
row->prev_size, size_buf, size_buf_size);
}
if (sparse) { if (sparse) {
emitter_table_sparse_row(emitter, table_row, is_gap); emitter_table_sparse_row(emitter, table_row, is_gap);
} else { } else {

View File

@@ -672,6 +672,7 @@ typedef struct {
size_t size; size_t size;
uint64_t count; uint64_t count;
uint64_t prof_count; uint64_t prof_count;
const char *label;
uint64_t requests; uint64_t requests;
} col_table_test_row_t; } col_table_test_row_t;
@@ -684,6 +685,7 @@ typedef struct {
COL_TABLE_TEST_GETTER(size, size, size_val) COL_TABLE_TEST_GETTER(size, size, size_val)
COL_TABLE_TEST_GETTER(count, count, uint64_val) COL_TABLE_TEST_GETTER(count, count, uint64_val)
COL_TABLE_TEST_GETTER(prof_count, prof_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) COL_TABLE_TEST_GETTER(requests, requests, uint64_val)
#undef COL_TABLE_TEST_GETTER #undef COL_TABLE_TEST_GETTER
@@ -691,6 +693,7 @@ enum {
COL_TABLE_TEST_SIZE, COL_TABLE_TEST_SIZE,
COL_TABLE_TEST_COUNT, COL_TABLE_TEST_COUNT,
COL_TABLE_TEST_PROF_COUNT, COL_TABLE_TEST_PROF_COUNT,
COL_TABLE_TEST_LABEL,
COL_TABLE_TEST_REQUESTS, COL_TABLE_TEST_REQUESTS,
COL_TABLE_TEST_NCOLS COL_TABLE_TEST_NCOLS
}; };
@@ -704,6 +707,8 @@ static const emitter_col_desc_t col_table_test_descs[] = {
0, col_table_test_get_count}, 0, col_table_test_get_count},
{"prof_count", "prof", emitter_justify_right, 6, emitter_type_uint64, {"prof_count", "prof", emitter_justify_right, 6, emitter_type_uint64,
COL_TABLE_TEST_FLAG_PROF, col_table_test_get_prof_count}, 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, {"requests", "requests", emitter_justify_right, 10,
emitter_type_uint64, 0, col_table_test_get_requests}, emitter_type_uint64, 0, col_table_test_get_requests},
}; };
@@ -726,15 +731,9 @@ TEST_BEGIN(test_col_desc_flags) {
} }
TEST_END 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 static void
emit_col_table(emitter_t *emitter) { 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_row_t row, header_row;
emitter_col_t cols[COL_TABLE_TEST_NCOLS]; emitter_col_t cols[COL_TABLE_TEST_NCOLS];
emitter_col_t header_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); COL_TABLE_TEST_FLAG_PROF, cols, &value);
emitter_json_object_begin(emitter); emitter_json_object_begin(emitter);
emitter_col_table_emit_json(emitter, col_table_test_descs, emitter_col_table_emit_json(emitter, col_table_test_descs,
COL_TABLE_TEST_NCOLS, COL_TABLE_TEST_FLAG_PROF, cols, 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_json_object_end(emitter);
emitter_table_row(emitter, &row); emitter_table_row(emitter, &row);
emitter_json_array_end(emitter); emitter_json_array_end(emitter);
@@ -761,17 +758,19 @@ static const char *col_table_json =
"{\n" "{\n"
"\t\"rows\": [\n" "\t\"rows\": [\n"
"\t\t{\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\"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\t}\n"
"\t]\n" "\t]\n"
"}\n"; "}\n";
static const char *col_table_json_compact = 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 = static const char *col_table_table =
"mock:size count prof requests\n" "mock:size count prof label requests\n"
" 42 7 100 9\n"; " 42 7 100 row 9\n";
#define GENERATE_TEST(feature) \ #define GENERATE_TEST(feature) \
TEST_BEGIN(test_##feature) { \ TEST_BEGIN(test_##feature) { \

View File

@@ -442,6 +442,95 @@ expect_json_uint_eq(json_fragment_t object, const char *key,
key); key);
} }
static void
expect_json_string_eq(json_fragment_t object, const char *key,
const char *expected, const char *name) {
json_fragment_t value;
bool missing = json_object_member(object, key, &value);
expect_false(missing, "%s is missing %s", name, key);
if (missing) {
return;
}
expect_true(json_fragment_string_eq(value, expected),
"%s.%s has an unexpected value", name, key);
}
static uint64_t
expected_rate(uint64_t value, uint64_t uptime_ns) {
const uint64_t billion = 1000000000;
if (uptime_ns == 0 || value == 0) {
return 0;
}
return uptime_ns < billion ? value : value / (uptime_ns / billion);
}
static void
expected_utilization(size_t curregs, size_t availregs, char result[6]) {
if (availregs == 0) {
malloc_snprintf(result, 6, "1");
return;
}
assert_zu_le(curregs, availregs, "Cached bin stats should be consistent");
unsigned n = (unsigned)(((uint64_t)curregs * 1000) / availregs);
if (n < 10) {
malloc_snprintf(result, 6, "0.00%u", n);
} else if (n < 100) {
malloc_snprintf(result, 6, "0.0%u", n);
} else if (n < 1000) {
malloc_snprintf(result, 6, "0.%u", n);
} else {
malloc_snprintf(result, 6, "1");
}
}
static size_t
read_size_ctl(const char *name) {
size_t value;
size_t sz = sizeof(value);
assert_d_eq(mallctl(name, &value, &sz, NULL, 0), 0,
"mallctl failed for %s", name);
return value;
}
static uint64_t
read_uint64_ctl(const char *name) {
uint64_t value;
size_t sz = sizeof(value);
assert_d_eq(mallctl(name, &value, &sz, NULL, 0), 0,
"mallctl failed for %s", name);
return value;
}
static uint32_t
read_uint32_ctl(const char *name) {
uint32_t value;
size_t sz = sizeof(value);
assert_d_eq(mallctl(name, &value, &sz, NULL, 0), 0,
"mallctl failed for %s", name);
return value;
}
static size_t
read_size_field(const char *prefix, const char *suffix) {
char name[192];
malloc_snprintf(name, sizeof(name), "%s.%s", prefix, suffix);
return read_size_ctl(name);
}
static uint64_t
read_uint64_field(const char *prefix, const char *suffix) {
char name[192];
malloc_snprintf(name, sizeof(name), "%s.%s", prefix, suffix);
return read_uint64_ctl(name);
}
static uint32_t
read_uint32_field(const char *prefix, const char *suffix) {
char name[192];
malloc_snprintf(name, sizeof(name), "%s.%s", prefix, suffix);
return read_uint32_ctl(name);
}
static bool static bool
stats_json_print_opts(stats_buf_t *sbuf, const char *opts, stats_json_print_opts(stats_buf_t *sbuf, const char *opts,
json_fragment_t *jemalloc) { json_fragment_t *jemalloc) {
@@ -886,12 +975,20 @@ static const ctl_field_t hpa_slab_fields[] = {
static const char *const hpa_slab_keys[] = {"npageslabs_huge", static const char *const hpa_slab_keys[] = {"npageslabs_huge",
"nactive_huge", "ndirty_huge", "npageslabs_nonhuge", "nactive_huge", "ndirty_huge", "npageslabs_nonhuge",
"nactive_nonhuge", "ndirty_nonhuge", "nretained_nonhuge"}; "nactive_nonhuge", "ndirty_nonhuge", "nretained_nonhuge"};
static const char *const hpa_slab_row_keys[] = {"size", "ind"};
static void static void
expect_hpa_slab(json_fragment_t slab, const char *ctl_prefix, expect_hpa_slab(json_fragment_t slab, const char *ctl_prefix,
const char *name) { const char *name, bool table_row) {
expect_json_object_keys( expect_zu_eq(json_object_size(slab), ARRAY_COUNT(hpa_slab_keys)
+ (table_row ? ARRAY_COUNT(hpa_slab_row_keys) : 0),
"%s has an unexpected number of keys", name);
expect_json_object_has_keys(
slab, hpa_slab_keys, ARRAY_COUNT(hpa_slab_keys), name); slab, hpa_slab_keys, ARRAY_COUNT(hpa_slab_keys), name);
if (table_row) {
expect_json_object_has_keys(slab, hpa_slab_row_keys,
ARRAY_COUNT(hpa_slab_row_keys), name);
}
expect_json_ctl_fields(slab, ctl_prefix, hpa_slab_fields, expect_json_ctl_fields(slab, ctl_prefix, hpa_slab_fields,
ARRAY_COUNT(hpa_slab_fields)); ARRAY_COUNT(hpa_slab_fields));
@@ -972,7 +1069,7 @@ TEST_BEGIN(test_json_stats_hpa) {
char slab_prefix[128]; char slab_prefix[128];
malloc_snprintf( malloc_snprintf(
slab_prefix, sizeof(slab_prefix), "%s.slabs", hpa_prefix); slab_prefix, sizeof(slab_prefix), "%s.slabs", hpa_prefix);
expect_hpa_slab(slabs, slab_prefix, "hpa_shard.slabs"); expect_hpa_slab(slabs, slab_prefix, "hpa_shard.slabs", false);
const char *const summary_names[] = {"full_slabs", "empty_slabs"}; const char *const summary_names[] = {"full_slabs", "empty_slabs"};
for (size_t i = 0; i < ARRAY_COUNT(summary_names); i++) { for (size_t i = 0; i < ARRAY_COUNT(summary_names); i++) {
@@ -981,7 +1078,10 @@ TEST_BEGIN(test_json_stats_hpa) {
"hpa_shard is missing %s", summary_names[i]); "hpa_shard is missing %s", summary_names[i]);
malloc_snprintf(slab_prefix, sizeof(slab_prefix), "%s.%s", malloc_snprintf(slab_prefix, sizeof(slab_prefix), "%s.%s",
hpa_prefix, summary_names[i]); hpa_prefix, summary_names[i]);
expect_hpa_slab(summary, slab_prefix, summary_names[i]); expect_hpa_slab(summary, slab_prefix, summary_names[i], true);
expect_json_string_eq(
summary, "size", i == 0 ? "full" : "empty", summary_names[i]);
expect_json_string_eq(summary, "ind", "-", summary_names[i]);
} }
json_fragment_t nonfull; json_fragment_t nonfull;
@@ -1000,7 +1100,10 @@ TEST_BEGIN(test_json_stats_hpa) {
char row_name[64]; char row_name[64];
malloc_snprintf( malloc_snprintf(
row_name, sizeof(row_name), "nonfull_slabs[%zu]", i); row_name, sizeof(row_name), "nonfull_slabs[%zu]", i);
expect_hpa_slab(slab, slab_prefix, row_name); expect_hpa_slab(slab, slab_prefix, row_name, true);
expect_json_uint_eq(
slab, "size", sz_pind2sz((pszind_t)i), row_name);
expect_json_uint_eq(slab, "ind", i, row_name);
} }
json_fragment_t distribution; json_fragment_t distribution;
@@ -1036,18 +1139,26 @@ static const ctl_field_t bin_fields[] = {
{"nrequests", "nrequests", ctl_field_uint64}, {"nrequests", "nrequests", ctl_field_uint64},
{"nfills", "nfills", ctl_field_uint64}, {"nfills", "nfills", ctl_field_uint64},
{"nflushes", "nflushes", ctl_field_uint64}, {"nflushes", "nflushes", ctl_field_uint64},
{"nslabs", "nslabs", ctl_field_uint64},
{"nreslabs", "nreslabs", ctl_field_uint64}, {"nreslabs", "nreslabs", ctl_field_uint64},
{"curslabs", "curslabs", ctl_field_size}, {"curslabs", "curslabs", ctl_field_size},
{"nonfull_slabs", "nonfull_slabs", ctl_field_size}, {"nonfull_slabs", "nonfull_slabs", ctl_field_size},
}; };
static const char *const bin_keys[] = {"nmalloc", "ndalloc", "curregs", static const char *const bin_keys[] = {"size", "ind", "allocated",
"nrequests", "nfills", "nflushes", "nreslabs", "curslabs", "nmalloc", "nmalloc_ps", "ndalloc", "ndalloc_ps", "nrequests",
"nonfull_slabs", "mutex"}; "nrequests_ps", "nshards", "curregs", "curslabs", "nonfull_slabs",
"regs", "pgs", "util", "nfills", "nfills_ps", "nflushes",
"nflushes_ps", "nslabs", "nreslabs", "nreslabs_ps", "mutex"};
static const ctl_field_t lextent_fields[] = { static const ctl_field_t lextent_fields[] = {
{"nmalloc", "nmalloc", ctl_field_uint64},
{"ndalloc", "ndalloc", ctl_field_uint64},
{"nrequests", "nrequests", ctl_field_uint64},
{"curlextents", "curlextents", ctl_field_size}, {"curlextents", "curlextents", ctl_field_size},
}; };
static const char *const lextent_keys[] = {"curlextents"}; static const char *const lextent_keys[] = {"size", "ind", "allocated",
"nmalloc", "nmalloc_ps", "ndalloc", "ndalloc_ps", "nrequests",
"nrequests_ps", "curlextents"};
static const ctl_field_t extent_fields[] = { static const ctl_field_t extent_fields[] = {
{"ndirty", "ndirty", ctl_field_size}, {"ndirty", "ndirty", ctl_field_size},
@@ -1059,9 +1170,10 @@ static const ctl_field_t extent_fields[] = {
{"retained_bytes", "retained_bytes", ctl_field_size}, {"retained_bytes", "retained_bytes", ctl_field_size},
{"pinned_bytes", "pinned_bytes", ctl_field_size}, {"pinned_bytes", "pinned_bytes", ctl_field_size},
}; };
static const char *const extent_keys[] = {"ndirty", "nmuzzy", "nretained", static const char *const extent_keys[] = {"size", "ind", "ndirty",
"npinned", "dirty_bytes", "muzzy_bytes", "retained_bytes", "dirty_bytes", "nmuzzy", "muzzy_bytes", "nretained",
"pinned_bytes"}; "retained_bytes", "npinned", "pinned_bytes", "ntotal",
"total_bytes"};
static const char *const prof_keys[] = {"prof_live_requested", static const char *const prof_keys[] = {"prof_live_requested",
"prof_live_count", "prof_accum_requested", "prof_accum_count"}; "prof_live_count", "prof_accum_requested", "prof_accum_count"};
@@ -1109,6 +1221,10 @@ TEST_BEGIN(test_json_stats_arena_tables) {
sz = sizeof(nlextents); sz = sizeof(nlextents);
expect_d_eq(mallctl("arenas.nlextents", &nlextents, &sz, NULL, 0), 0, expect_d_eq(mallctl("arenas.nlextents", &nlextents, &sz, NULL, 0), 0,
"mallctl failed for arenas.nlextents"); "mallctl failed for arenas.nlextents");
char arena_prefix[64];
malloc_snprintf(arena_prefix, sizeof(arena_prefix), "stats.arenas.%u",
MALLCTL_ARENAS_ALL);
uint64_t uptime = read_uint64_field(arena_prefix, "uptime");
json_fragment_t bins; json_fragment_t bins;
expect_false(json_object_member(merged, "bins", &bins), expect_false(json_object_member(merged, "bins", &bins),
@@ -1136,6 +1252,44 @@ TEST_BEGIN(test_json_stats_arena_tables) {
"stats.arenas.%u.bins.%u", MALLCTL_ARENAS_ALL, i); "stats.arenas.%u.bins.%u", MALLCTL_ARENAS_ALL, i);
expect_json_ctl_fields( expect_json_ctl_fields(
bin, ctl_prefix, bin_fields, ARRAY_COUNT(bin_fields)); bin, ctl_prefix, bin_fields, ARRAY_COUNT(bin_fields));
char meta_prefix[64];
malloc_snprintf(
meta_prefix, sizeof(meta_prefix), "arenas.bin.%u", i);
size_t bin_size = read_size_field(meta_prefix, "size");
uint32_t nregs = read_uint32_field(meta_prefix, "nregs");
size_t slab_size = read_size_field(meta_prefix, "slab_size");
uint32_t nshards = read_uint32_field(meta_prefix, "nshards");
size_t curregs = read_size_field(ctl_prefix, "curregs");
size_t curslabs = read_size_field(ctl_prefix, "curslabs");
uint64_t nmalloc = read_uint64_field(ctl_prefix, "nmalloc");
uint64_t ndalloc = read_uint64_field(ctl_prefix, "ndalloc");
uint64_t nrequests = read_uint64_field(ctl_prefix, "nrequests");
uint64_t nfills = read_uint64_field(ctl_prefix, "nfills");
uint64_t nflushes = read_uint64_field(ctl_prefix, "nflushes");
uint64_t nreslabs = read_uint64_field(ctl_prefix, "nreslabs");
expect_json_uint_eq(bin, "size", bin_size, row_name);
expect_json_uint_eq(bin, "ind", i, row_name);
expect_json_uint_eq(
bin, "allocated", curregs * bin_size, row_name);
expect_json_uint_eq(bin, "nmalloc_ps",
expected_rate(nmalloc, uptime), row_name);
expect_json_uint_eq(bin, "ndalloc_ps",
expected_rate(ndalloc, uptime), row_name);
expect_json_uint_eq(bin, "nrequests_ps",
expected_rate(nrequests, uptime), row_name);
expect_json_uint_eq(bin, "nshards", nshards, row_name);
expect_json_uint_eq(bin, "regs", nregs, row_name);
expect_json_uint_eq(bin, "pgs", slab_size / PAGE, row_name);
expect_json_uint_eq(bin, "nfills_ps",
expected_rate(nfills, uptime), row_name);
expect_json_uint_eq(bin, "nflushes_ps",
expected_rate(nflushes, uptime), row_name);
expect_json_uint_eq(bin, "nreslabs_ps",
expected_rate(nreslabs, uptime), row_name);
char util[6];
expected_utilization(curregs, nregs * curslabs, util);
expect_json_string_eq(bin, "util", util, row_name);
if (prof_stats_present) { if (prof_stats_present) {
expect_json_object_has_keys( expect_json_object_has_keys(
bin, prof_keys, ARRAY_COUNT(prof_keys), row_name); bin, prof_keys, ARRAY_COUNT(prof_keys), row_name);
@@ -1175,6 +1329,24 @@ TEST_BEGIN(test_json_stats_arena_tables) {
"stats.arenas.%u.lextents.%u", MALLCTL_ARENAS_ALL, i); "stats.arenas.%u.lextents.%u", MALLCTL_ARENAS_ALL, i);
expect_json_ctl_fields(lextent, ctl_prefix, lextent_fields, expect_json_ctl_fields(lextent, ctl_prefix, lextent_fields,
ARRAY_COUNT(lextent_fields)); ARRAY_COUNT(lextent_fields));
char meta_prefix[64];
malloc_snprintf(meta_prefix, sizeof(meta_prefix),
"arenas.lextent.%u", i);
size_t lextent_size = read_size_field(meta_prefix, "size");
size_t curlextents = read_size_field(ctl_prefix, "curlextents");
uint64_t nmalloc = read_uint64_field(ctl_prefix, "nmalloc");
uint64_t ndalloc = read_uint64_field(ctl_prefix, "ndalloc");
uint64_t nrequests = read_uint64_field(ctl_prefix, "nrequests");
expect_json_uint_eq(lextent, "size", lextent_size, row_name);
expect_json_uint_eq(lextent, "ind", nbins + i, row_name);
expect_json_uint_eq(lextent, "allocated",
curlextents * lextent_size, row_name);
expect_json_uint_eq(lextent, "nmalloc_ps",
expected_rate(nmalloc, uptime), row_name);
expect_json_uint_eq(lextent, "ndalloc_ps",
expected_rate(ndalloc, uptime), row_name);
expect_json_uint_eq(lextent, "nrequests_ps",
expected_rate(nrequests, uptime), row_name);
if (prof_stats_present) { if (prof_stats_present) {
expect_json_object_has_keys(lextent, prof_keys, expect_json_object_has_keys(lextent, prof_keys,
ARRAY_COUNT(prof_keys), row_name); ARRAY_COUNT(prof_keys), row_name);
@@ -1203,6 +1375,22 @@ TEST_BEGIN(test_json_stats_arena_tables) {
"stats.arenas.%u.extents.%u", MALLCTL_ARENAS_ALL, i); "stats.arenas.%u.extents.%u", MALLCTL_ARENAS_ALL, i);
expect_json_ctl_fields(extent, ctl_prefix, extent_fields, expect_json_ctl_fields(extent, ctl_prefix, extent_fields,
ARRAY_COUNT(extent_fields)); ARRAY_COUNT(extent_fields));
size_t ndirty = read_size_field(ctl_prefix, "ndirty");
size_t nmuzzy = read_size_field(ctl_prefix, "nmuzzy");
size_t nretained = read_size_field(ctl_prefix, "nretained");
size_t npinned = read_size_field(ctl_prefix, "npinned");
size_t dirty_bytes = read_size_field(ctl_prefix, "dirty_bytes");
size_t muzzy_bytes = read_size_field(ctl_prefix, "muzzy_bytes");
size_t retained_bytes = read_size_field(
ctl_prefix, "retained_bytes");
size_t pinned_bytes = read_size_field(ctl_prefix, "pinned_bytes");
expect_json_uint_eq(extent, "size", sz_pind2sz(i), row_name);
expect_json_uint_eq(extent, "ind", i, row_name);
expect_json_uint_eq(extent, "ntotal",
ndirty + nmuzzy + nretained + npinned, row_name);
expect_json_uint_eq(extent, "total_bytes",
dirty_bytes + muzzy_bytes + retained_bytes + pinned_bytes,
row_name);
} }
stats_buf_fini(&sbuf); stats_buf_fini(&sbuf);