Home | History | Annotate | Line # | Download | only in unit
      1  1.1  christos #include "test/jemalloc_test.h"
      2  1.1  christos 
      3  1.1  christos typedef struct {
      4  1.1  christos 	char  *buf;
      5  1.1  christos 	size_t len;
      6  1.1  christos 	size_t capacity;
      7  1.1  christos } stats_buf_t;
      8  1.1  christos 
      9  1.1  christos static void
     10  1.1  christos stats_buf_init(stats_buf_t *sbuf) {
     11  1.1  christos 	/* 1MB buffer should be enough since per-arena stats are omitted. */
     12  1.1  christos 	sbuf->capacity = 1 << 20;
     13  1.1  christos 	sbuf->buf = mallocx(sbuf->capacity, MALLOCX_TCACHE_NONE);
     14  1.1  christos 	assert_ptr_not_null(sbuf->buf, "Failed to allocate stats buffer");
     15  1.1  christos 	sbuf->len = 0;
     16  1.1  christos 	sbuf->buf[0] = '\0';
     17  1.1  christos }
     18  1.1  christos 
     19  1.1  christos static void
     20  1.1  christos stats_buf_fini(stats_buf_t *sbuf) {
     21  1.1  christos 	dallocx(sbuf->buf, MALLOCX_TCACHE_NONE);
     22  1.1  christos }
     23  1.1  christos 
     24  1.1  christos static void
     25  1.1  christos stats_buf_write_cb(void *opaque, const char *str) {
     26  1.1  christos 	stats_buf_t *sbuf = (stats_buf_t *)opaque;
     27  1.1  christos 	size_t       slen = strlen(str);
     28  1.1  christos 
     29  1.1  christos 	if (sbuf->len + slen + 1 > sbuf->capacity) {
     30  1.1  christos 		return;
     31  1.1  christos 	}
     32  1.1  christos 	memcpy(&sbuf->buf[sbuf->len], str, slen + 1);
     33  1.1  christos 	sbuf->len += slen;
     34  1.1  christos }
     35  1.1  christos 
     36  1.1  christos static bool
     37  1.1  christos json_extract_uint64(const char *json, const char *key, uint64_t *result) {
     38  1.1  christos 	char   search_key[128];
     39  1.1  christos 	size_t key_len;
     40  1.1  christos 
     41  1.1  christos 	key_len = snprintf(search_key, sizeof(search_key), "\"%s\":", key);
     42  1.1  christos 	if (key_len >= sizeof(search_key)) {
     43  1.1  christos 		return true;
     44  1.1  christos 	}
     45  1.1  christos 
     46  1.1  christos 	const char *pos = strstr(json, search_key);
     47  1.1  christos 	if (pos == NULL) {
     48  1.1  christos 		return true;
     49  1.1  christos 	}
     50  1.1  christos 
     51  1.1  christos 	pos += key_len;
     52  1.1  christos 	while (*pos == ' ' || *pos == '\t' || *pos == '\n') {
     53  1.1  christos 		pos++;
     54  1.1  christos 	}
     55  1.1  christos 
     56  1.1  christos 	char    *endptr;
     57  1.1  christos 	uint64_t value = strtoull(pos, &endptr, 10);
     58  1.1  christos 	if (endptr == pos) {
     59  1.1  christos 		return true;
     60  1.1  christos 	}
     61  1.1  christos 
     62  1.1  christos 	*result = value;
     63  1.1  christos 	return false;
     64  1.1  christos }
     65  1.1  christos 
     66  1.1  christos static const char *
     67  1.1  christos json_find_section(const char *json, const char *section_name) {
     68  1.1  christos 	char   search_pattern[128];
     69  1.1  christos 	size_t pattern_len;
     70  1.1  christos 
     71  1.1  christos 	pattern_len = snprintf(
     72  1.1  christos 	    search_pattern, sizeof(search_pattern), "\"%s\":", section_name);
     73  1.1  christos 	if (pattern_len >= sizeof(search_pattern)) {
     74  1.1  christos 		return NULL;
     75  1.1  christos 	}
     76  1.1  christos 
     77  1.1  christos 	return strstr(json, search_pattern);
     78  1.1  christos }
     79  1.1  christos 
     80  1.1  christos static void
     81  1.1  christos verify_mutex_json(const char *mutexes_section, const char *mallctl_prefix,
     82  1.1  christos     const char *mutex_name) {
     83  1.1  christos 	char   mallctl_path[128];
     84  1.1  christos 	size_t sz;
     85  1.1  christos 
     86  1.1  christos 	const char *mutex_section = json_find_section(
     87  1.1  christos 	    mutexes_section, mutex_name);
     88  1.1  christos 	expect_ptr_not_null(mutex_section,
     89  1.1  christos 	    "Could not find %s mutex section in JSON", mutex_name);
     90  1.1  christos 
     91  1.1  christos 	uint64_t ctl_num_ops, ctl_num_wait, ctl_num_spin_acq;
     92  1.1  christos 	uint64_t ctl_num_owner_switch, ctl_total_wait_time, ctl_max_wait_time;
     93  1.1  christos 	uint32_t ctl_max_num_thds;
     94  1.1  christos 
     95  1.1  christos 	sz = sizeof(uint64_t);
     96  1.1  christos 	snprintf(mallctl_path, sizeof(mallctl_path), "%s.%s.num_ops",
     97  1.1  christos 	    mallctl_prefix, mutex_name);
     98  1.1  christos 	expect_d_eq(mallctl(mallctl_path, &ctl_num_ops, &sz, NULL, 0), 0,
     99  1.1  christos 	    "Unexpected mallctl() failure for %s", mallctl_path);
    100  1.1  christos 
    101  1.1  christos 	snprintf(mallctl_path, sizeof(mallctl_path), "%s.%s.num_wait",
    102  1.1  christos 	    mallctl_prefix, mutex_name);
    103  1.1  christos 	expect_d_eq(mallctl(mallctl_path, &ctl_num_wait, &sz, NULL, 0), 0,
    104  1.1  christos 	    "Unexpected mallctl() failure for %s", mallctl_path);
    105  1.1  christos 
    106  1.1  christos 	snprintf(mallctl_path, sizeof(mallctl_path), "%s.%s.num_spin_acq",
    107  1.1  christos 	    mallctl_prefix, mutex_name);
    108  1.1  christos 	expect_d_eq(mallctl(mallctl_path, &ctl_num_spin_acq, &sz, NULL, 0), 0,
    109  1.1  christos 	    "Unexpected mallctl() failure for %s", mallctl_path);
    110  1.1  christos 
    111  1.1  christos 	snprintf(mallctl_path, sizeof(mallctl_path), "%s.%s.num_owner_switch",
    112  1.1  christos 	    mallctl_prefix, mutex_name);
    113  1.1  christos 	expect_d_eq(mallctl(mallctl_path, &ctl_num_owner_switch, &sz, NULL, 0),
    114  1.1  christos 	    0, "Unexpected mallctl() failure for %s", mallctl_path);
    115  1.1  christos 
    116  1.1  christos 	snprintf(mallctl_path, sizeof(mallctl_path), "%s.%s.total_wait_time",
    117  1.1  christos 	    mallctl_prefix, mutex_name);
    118  1.1  christos 	expect_d_eq(mallctl(mallctl_path, &ctl_total_wait_time, &sz, NULL, 0),
    119  1.1  christos 	    0, "Unexpected mallctl() failure for %s", mallctl_path);
    120  1.1  christos 
    121  1.1  christos 	snprintf(mallctl_path, sizeof(mallctl_path), "%s.%s.max_wait_time",
    122  1.1  christos 	    mallctl_prefix, mutex_name);
    123  1.1  christos 	expect_d_eq(mallctl(mallctl_path, &ctl_max_wait_time, &sz, NULL, 0), 0,
    124  1.1  christos 	    "Unexpected mallctl() failure for %s", mallctl_path);
    125  1.1  christos 
    126  1.1  christos 	sz = sizeof(uint32_t);
    127  1.1  christos 	snprintf(mallctl_path, sizeof(mallctl_path), "%s.%s.max_num_thds",
    128  1.1  christos 	    mallctl_prefix, mutex_name);
    129  1.1  christos 	expect_d_eq(mallctl(mallctl_path, &ctl_max_num_thds, &sz, NULL, 0), 0,
    130  1.1  christos 	    "Unexpected mallctl() failure for %s", mallctl_path);
    131  1.1  christos 
    132  1.1  christos 	uint64_t json_num_ops, json_num_wait, json_num_spin_acq;
    133  1.1  christos 	uint64_t json_num_owner_switch, json_total_wait_time,
    134  1.1  christos 	    json_max_wait_time;
    135  1.1  christos 	uint64_t json_max_num_thds;
    136  1.1  christos 
    137  1.1  christos 	expect_false(
    138  1.1  christos 	    json_extract_uint64(mutex_section, "num_ops", &json_num_ops),
    139  1.1  christos 	    "%s: num_ops not found in JSON", mutex_name);
    140  1.1  christos 	expect_false(
    141  1.1  christos 	    json_extract_uint64(mutex_section, "num_wait", &json_num_wait),
    142  1.1  christos 	    "%s: num_wait not found in JSON", mutex_name);
    143  1.1  christos 	expect_false(json_extract_uint64(
    144  1.1  christos 	                 mutex_section, "num_spin_acq", &json_num_spin_acq),
    145  1.1  christos 	    "%s: num_spin_acq not found in JSON", mutex_name);
    146  1.1  christos 	expect_false(json_extract_uint64(mutex_section, "num_owner_switch",
    147  1.1  christos 	                 &json_num_owner_switch),
    148  1.1  christos 	    "%s: num_owner_switch not found in JSON", mutex_name);
    149  1.1  christos 	expect_false(json_extract_uint64(mutex_section, "total_wait_time",
    150  1.1  christos 	                 &json_total_wait_time),
    151  1.1  christos 	    "%s: total_wait_time not found in JSON", mutex_name);
    152  1.1  christos 	expect_false(json_extract_uint64(
    153  1.1  christos 	                 mutex_section, "max_wait_time", &json_max_wait_time),
    154  1.1  christos 	    "%s: max_wait_time not found in JSON", mutex_name);
    155  1.1  christos 	expect_false(json_extract_uint64(
    156  1.1  christos 	                 mutex_section, "max_num_thds", &json_max_num_thds),
    157  1.1  christos 	    "%s: max_num_thds not found in JSON", mutex_name);
    158  1.1  christos 
    159  1.1  christos 	expect_u64_eq(json_num_ops, ctl_num_ops,
    160  1.1  christos 	    "%s: JSON num_ops doesn't match mallctl", mutex_name);
    161  1.1  christos 	expect_u64_eq(json_num_wait, ctl_num_wait,
    162  1.1  christos 	    "%s: JSON num_wait doesn't match mallctl", mutex_name);
    163  1.1  christos 	expect_u64_eq(json_num_spin_acq, ctl_num_spin_acq,
    164  1.1  christos 	    "%s: JSON num_spin_acq doesn't match mallctl", mutex_name);
    165  1.1  christos 	expect_u64_eq(json_num_owner_switch, ctl_num_owner_switch,
    166  1.1  christos 	    "%s: JSON num_owner_switch doesn't match mallctl", mutex_name);
    167  1.1  christos 	expect_u64_eq(json_total_wait_time, ctl_total_wait_time,
    168  1.1  christos 	    "%s: JSON total_wait_time doesn't match mallctl", mutex_name);
    169  1.1  christos 	expect_u64_eq(json_max_wait_time, ctl_max_wait_time,
    170  1.1  christos 	    "%s: JSON max_wait_time doesn't match mallctl", mutex_name);
    171  1.1  christos 	expect_u32_eq((uint32_t)json_max_num_thds, ctl_max_num_thds,
    172  1.1  christos 	    "%s: JSON max_num_thds doesn't match mallctl", mutex_name);
    173  1.1  christos }
    174  1.1  christos 
    175  1.1  christos static const char  *global_mutex_names[] = {"background_thread",
    176  1.1  christos      "max_per_bg_thd", "ctl", "prof", "prof_thds_data", "prof_dump",
    177  1.1  christos      "prof_recent_alloc", "prof_recent_dump", "prof_stats"};
    178  1.1  christos static const size_t num_global_mutexes = sizeof(global_mutex_names)
    179  1.1  christos     / sizeof(global_mutex_names[0]);
    180  1.1  christos 
    181  1.1  christos static const char  *arena_mutex_names[] = {"large", "extent_avail",
    182  1.1  christos      "extents_dirty", "extents_muzzy", "extents_retained", "decay_dirty",
    183  1.1  christos      "decay_muzzy", "base", "tcache_list", "hpa_shard", "hpa_shard_grow",
    184  1.1  christos      "hpa_sec"};
    185  1.1  christos static const size_t num_arena_mutexes = sizeof(arena_mutex_names)
    186  1.1  christos     / sizeof(arena_mutex_names[0]);
    187  1.1  christos 
    188  1.1  christos static const char *
    189  1.1  christos json_find_object_end(const char *object_begin) {
    190  1.1  christos 	int depth = 0;
    191  1.1  christos 	for (const char *cur = object_begin; *cur != '\0'; cur++) {
    192  1.1  christos 		if (*cur == '{') {
    193  1.1  christos 			depth++;
    194  1.1  christos 		} else if (*cur == '}') {
    195  1.1  christos 			depth--;
    196  1.1  christos 			if (depth == 0) {
    197  1.1  christos 				return cur;
    198  1.1  christos 			}
    199  1.1  christos 			if (depth < 0) {
    200  1.1  christos 				return NULL;
    201  1.1  christos 			}
    202  1.1  christos 		}
    203  1.1  christos 	}
    204  1.1  christos 	return NULL;
    205  1.1  christos }
    206  1.1  christos 
    207  1.1  christos static const char *
    208  1.1  christos json_find_array_end(const char *array_begin) {
    209  1.1  christos 	int depth = 0;
    210  1.1  christos 	for (const char *cur = array_begin; *cur != '\0'; cur++) {
    211  1.1  christos 		if (*cur == '[') {
    212  1.1  christos 			depth++;
    213  1.1  christos 		} else if (*cur == ']') {
    214  1.1  christos 			depth--;
    215  1.1  christos 			if (depth == 0) {
    216  1.1  christos 				return cur;
    217  1.1  christos 			}
    218  1.1  christos 			if (depth < 0) {
    219  1.1  christos 				return NULL;
    220  1.1  christos 			}
    221  1.1  christos 		}
    222  1.1  christos 	}
    223  1.1  christos 	return NULL;
    224  1.1  christos }
    225  1.1  christos 
    226  1.1  christos static const char *
    227  1.1  christos json_find_previous_hpa_shard_object(
    228  1.1  christos     const char *json, const char *pos, const char **object_end) {
    229  1.1  christos 	*object_end = NULL;
    230  1.1  christos 	const char *found = NULL;
    231  1.1  christos 	const char *cur = json;
    232  1.1  christos 	const char *next;
    233  1.1  christos 
    234  1.1  christos 	while ((next = strstr(cur, "\"hpa_shard\":{")) != NULL && next < pos) {
    235  1.1  christos 		found = strchr(next, '{');
    236  1.1  christos 		cur = next + 1;
    237  1.1  christos 	}
    238  1.1  christos 	if (found == NULL) {
    239  1.1  christos 		return NULL;
    240  1.1  christos 	}
    241  1.1  christos 	*object_end = json_find_object_end(found);
    242  1.1  christos 	return found;
    243  1.1  christos }
    244  1.1  christos 
    245  1.1  christos static const char *
    246  1.1  christos json_find_named_object(
    247  1.1  christos     const char *json, const char *key, const char **object_end) {
    248  1.1  christos 	*object_end = NULL;
    249  1.1  christos 	char   search_key[128];
    250  1.1  christos 	size_t written = malloc_snprintf(
    251  1.1  christos 	    search_key, sizeof(search_key), "\"%s\":{", key);
    252  1.1  christos 	if (written >= sizeof(search_key)) {
    253  1.1  christos 		return NULL;
    254  1.1  christos 	}
    255  1.1  christos 
    256  1.1  christos 	const char *object_begin = strstr(json, search_key);
    257  1.1  christos 	if (object_begin == NULL) {
    258  1.1  christos 		return NULL;
    259  1.1  christos 	}
    260  1.1  christos 	object_begin = strchr(object_begin, '{');
    261  1.1  christos 	if (object_begin == NULL) {
    262  1.1  christos 		return NULL;
    263  1.1  christos 	}
    264  1.1  christos 	*object_end = json_find_object_end(object_begin);
    265  1.1  christos 	return object_begin;
    266  1.1  christos }
    267  1.1  christos 
    268  1.1  christos static const char *
    269  1.1  christos json_find_named_array(
    270  1.1  christos     const char *json, const char *key, const char **array_end) {
    271  1.1  christos 	*array_end = NULL;
    272  1.1  christos 	char   search_key[128];
    273  1.1  christos 	size_t written = malloc_snprintf(
    274  1.1  christos 	    search_key, sizeof(search_key), "\"%s\":[", key);
    275  1.1  christos 	if (written >= sizeof(search_key)) {
    276  1.1  christos 		return NULL;
    277  1.1  christos 	}
    278  1.1  christos 
    279  1.1  christos 	const char *array_begin = strstr(json, search_key);
    280  1.1  christos 	if (array_begin == NULL) {
    281  1.1  christos 		return NULL;
    282  1.1  christos 	}
    283  1.1  christos 	array_begin = strchr(array_begin, '[');
    284  1.1  christos 	if (array_begin == NULL) {
    285  1.1  christos 		return NULL;
    286  1.1  christos 	}
    287  1.1  christos 	*array_end = json_find_array_end(array_begin);
    288  1.1  christos 	return array_begin;
    289  1.1  christos }
    290  1.1  christos 
    291  1.1  christos TEST_BEGIN(test_json_stats_mutexes) {
    292  1.1  christos 	test_skip_if(!config_stats);
    293  1.1  christos 
    294  1.1  christos 	uint64_t epoch;
    295  1.1  christos 	expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
    296  1.1  christos 	    0, "Unexpected mallctl() failure");
    297  1.1  christos 
    298  1.1  christos 	stats_buf_t sbuf;
    299  1.1  christos 	stats_buf_init(&sbuf);
    300  1.1  christos 	/* "J" for JSON format, "a" to omit per-arena stats. */
    301  1.1  christos 	malloc_stats_print(stats_buf_write_cb, &sbuf, "Ja");
    302  1.1  christos 
    303  1.1  christos 	/* Verify global mutexes under stats.mutexes. */
    304  1.1  christos 	const char *global_mutexes_section = json_find_section(
    305  1.1  christos 	    sbuf.buf, "mutexes");
    306  1.1  christos 	expect_ptr_not_null(global_mutexes_section,
    307  1.1  christos 	    "Could not find global mutexes section in JSON output");
    308  1.1  christos 
    309  1.1  christos 	for (size_t i = 0; i < num_global_mutexes; i++) {
    310  1.1  christos 		verify_mutex_json(global_mutexes_section, "stats.mutexes",
    311  1.1  christos 		    global_mutex_names[i]);
    312  1.1  christos 	}
    313  1.1  christos 
    314  1.1  christos 	/* Verify arena mutexes under stats.arenas.merged.mutexes. */
    315  1.1  christos 	const char *arenas_section = json_find_section(
    316  1.1  christos 	    sbuf.buf, "stats.arenas");
    317  1.1  christos 	expect_ptr_not_null(arenas_section,
    318  1.1  christos 	    "Could not find stats.arenas section in JSON output");
    319  1.1  christos 
    320  1.1  christos 	const char *merged_section = json_find_section(
    321  1.1  christos 	    arenas_section, "merged");
    322  1.1  christos 	expect_ptr_not_null(
    323  1.1  christos 	    merged_section, "Could not find merged section in JSON output");
    324  1.1  christos 
    325  1.1  christos 	const char *arena_mutexes_section = json_find_section(
    326  1.1  christos 	    merged_section, "mutexes");
    327  1.1  christos 	expect_ptr_not_null(arena_mutexes_section,
    328  1.1  christos 	    "Could not find arena mutexes section in JSON output");
    329  1.1  christos 
    330  1.1  christos 	for (size_t i = 0; i < num_arena_mutexes; i++) {
    331  1.1  christos 		/*
    332  1.1  christos 		 * MALLCTL_ARENAS_ALL is 4096 representing all arenas in
    333  1.1  christos 		 * mallctl queries.
    334  1.1  christos 		 */
    335  1.1  christos 		verify_mutex_json(arena_mutexes_section,
    336  1.1  christos 		    "stats.arenas.4096.mutexes", arena_mutex_names[i]);
    337  1.1  christos 	}
    338  1.1  christos 
    339  1.1  christos 	stats_buf_fini(&sbuf);
    340  1.1  christos }
    341  1.1  christos TEST_END
    342  1.1  christos 
    343  1.1  christos /*
    344  1.1  christos  * Verify that hpa_shard JSON stats contain "ndirty_huge" key in both
    345  1.1  christos  * full_slabs and empty_slabs sections.  A previous bug emitted duplicate
    346  1.1  christos  * "nactive_huge" instead of "ndirty_huge".
    347  1.1  christos  */
    348  1.1  christos TEST_BEGIN(test_hpa_shard_json_ndirty_huge) {
    349  1.1  christos 	test_skip_if(!config_stats);
    350  1.1  christos 	test_skip_if(!hpa_supported());
    351  1.1  christos 
    352  1.1  christos 	/* Do some allocation to create HPA state. */
    353  1.1  christos 	void *p = mallocx(PAGE, MALLOCX_TCACHE_NONE);
    354  1.1  christos 	expect_ptr_not_null(p, "Unexpected mallocx failure");
    355  1.1  christos 
    356  1.1  christos 	uint64_t epoch = 1;
    357  1.1  christos 	size_t   sz = sizeof(epoch);
    358  1.1  christos 	expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sz), 0,
    359  1.1  christos 	    "Unexpected mallctl() failure");
    360  1.1  christos 
    361  1.1  christos 	stats_buf_t sbuf;
    362  1.1  christos 	stats_buf_init(&sbuf);
    363  1.1  christos 	/* "J" for JSON, include per-arena HPA stats. */
    364  1.1  christos 	malloc_stats_print(stats_buf_write_cb, &sbuf, "J");
    365  1.1  christos 
    366  1.1  christos 	/*
    367  1.1  christos 	 * Find "full_slabs" and check it contains "ndirty_huge".
    368  1.1  christos 	 */
    369  1.1  christos 	const char *full_slabs = strstr(sbuf.buf, "\"full_slabs\"");
    370  1.1  christos 	if (full_slabs != NULL) {
    371  1.1  christos 		const char *empty_slabs = strstr(full_slabs, "\"empty_slabs\"");
    372  1.1  christos 		const char *search_end = empty_slabs != NULL
    373  1.1  christos 		    ? empty_slabs
    374  1.1  christos 		    : sbuf.buf + sbuf.len;
    375  1.1  christos 		/*
    376  1.1  christos 		 * Search for "ndirty_huge" between full_slabs and
    377  1.1  christos 		 * empty_slabs.
    378  1.1  christos 		 */
    379  1.1  christos 		const char *ndirty = full_slabs;
    380  1.1  christos 		bool        found = false;
    381  1.1  christos 		while (ndirty < search_end) {
    382  1.1  christos 			ndirty = strstr(ndirty, "\"ndirty_huge\"");
    383  1.1  christos 			if (ndirty != NULL && ndirty < search_end) {
    384  1.1  christos 				found = true;
    385  1.1  christos 				break;
    386  1.1  christos 			}
    387  1.1  christos 			break;
    388  1.1  christos 		}
    389  1.1  christos 		expect_true(
    390  1.1  christos 		    found, "full_slabs section should contain ndirty_huge key");
    391  1.1  christos 	}
    392  1.1  christos 
    393  1.1  christos 	/*
    394  1.1  christos 	 * Find "empty_slabs" and check it contains "ndirty_huge".
    395  1.1  christos 	 */
    396  1.1  christos 	const char *empty_slabs = strstr(sbuf.buf, "\"empty_slabs\"");
    397  1.1  christos 	if (empty_slabs != NULL) {
    398  1.1  christos 		/* Find the end of the empty_slabs object. */
    399  1.1  christos 		const char *nonfull = strstr(empty_slabs, "\"nonfull_slabs\"");
    400  1.1  christos 		const char *search_end = nonfull != NULL ? nonfull
    401  1.1  christos 		                                         : sbuf.buf + sbuf.len;
    402  1.1  christos 		const char *ndirty = strstr(empty_slabs, "\"ndirty_huge\"");
    403  1.1  christos 		bool        found = (ndirty != NULL && ndirty < search_end);
    404  1.1  christos 		expect_true(found,
    405  1.1  christos 		    "empty_slabs section should contain ndirty_huge key");
    406  1.1  christos 	}
    407  1.1  christos 
    408  1.1  christos 	stats_buf_fini(&sbuf);
    409  1.1  christos 	dallocx(p, MALLOCX_TCACHE_NONE);
    410  1.1  christos }
    411  1.1  christos TEST_END
    412  1.1  christos 
    413  1.1  christos TEST_BEGIN(test_hpa_shard_json_contains_sec_stats) {
    414  1.1  christos 	test_skip_if(!config_stats);
    415  1.1  christos 	test_skip_if(!hpa_supported());
    416  1.1  christos 
    417  1.1  christos 	void *p = mallocx(PAGE, MALLOCX_TCACHE_NONE);
    418  1.1  christos 	expect_ptr_not_null(p, "Unexpected mallocx failure");
    419  1.1  christos 
    420  1.1  christos 	uint64_t epoch = 1;
    421  1.1  christos 	size_t   sz = sizeof(epoch);
    422  1.1  christos 	expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sz), 0,
    423  1.1  christos 	    "Unexpected mallctl() failure");
    424  1.1  christos 
    425  1.1  christos 	stats_buf_t sbuf;
    426  1.1  christos 	stats_buf_init(&sbuf);
    427  1.1  christos 	malloc_stats_print(stats_buf_write_cb, &sbuf, "J");
    428  1.1  christos 
    429  1.1  christos 	const char *sec_bytes = strstr(sbuf.buf, "\"sec_bytes\"");
    430  1.1  christos 	expect_ptr_not_null(sec_bytes, "JSON output should contain sec_bytes");
    431  1.1  christos 	const char *hpa_shard_end = NULL;
    432  1.1  christos 	const char *hpa_shard = json_find_previous_hpa_shard_object(
    433  1.1  christos 	    sbuf.buf, sec_bytes, &hpa_shard_end);
    434  1.1  christos 	expect_ptr_not_null(hpa_shard,
    435  1.1  christos 	    "sec_bytes should be associated with an hpa_shard JSON object");
    436  1.1  christos 	expect_ptr_not_null(hpa_shard_end,
    437  1.1  christos 	    "Could not find end of enclosing hpa_shard JSON object");
    438  1.1  christos 	expect_true(sec_bytes != NULL && sec_bytes < hpa_shard_end,
    439  1.1  christos 	    "sec_bytes should be nested inside hpa_shard JSON object");
    440  1.1  christos 	const char *sec_hits = strstr(hpa_shard, "\"sec_hits\"");
    441  1.1  christos 	expect_true(sec_hits != NULL && sec_hits < hpa_shard_end,
    442  1.1  christos 	    "sec_hits should be nested inside hpa_shard JSON object");
    443  1.1  christos 	const char *sec_misses = strstr(hpa_shard, "\"sec_misses\"");
    444  1.1  christos 	expect_true(sec_misses != NULL && sec_misses < hpa_shard_end,
    445  1.1  christos 	    "sec_misses should be nested inside hpa_shard JSON object");
    446  1.1  christos 
    447  1.1  christos 	stats_buf_fini(&sbuf);
    448  1.1  christos 	dallocx(p, MALLOCX_TCACHE_NONE);
    449  1.1  christos }
    450  1.1  christos TEST_END
    451  1.1  christos 
    452  1.1  christos TEST_BEGIN(test_hpa_shard_json_contains_retained_stats) {
    453  1.1  christos 	test_skip_if(!config_stats);
    454  1.1  christos 	test_skip_if(!hpa_supported());
    455  1.1  christos 
    456  1.1  christos 	void *p = mallocx(PAGE, MALLOCX_TCACHE_NONE);
    457  1.1  christos 	expect_ptr_not_null(p, "Unexpected mallocx failure");
    458  1.1  christos 
    459  1.1  christos 	uint64_t epoch = 1;
    460  1.1  christos 	size_t   sz = sizeof(epoch);
    461  1.1  christos 	expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sz), 0,
    462  1.1  christos 	    "Unexpected mallctl() failure");
    463  1.1  christos 
    464  1.1  christos 	stats_buf_t sbuf;
    465  1.1  christos 	stats_buf_init(&sbuf);
    466  1.1  christos 	malloc_stats_print(stats_buf_write_cb, &sbuf, "J");
    467  1.1  christos 
    468  1.1  christos 	const char *full_slabs_end = NULL;
    469  1.1  christos 	const char *full_slabs = json_find_named_object(
    470  1.1  christos 	    sbuf.buf, "full_slabs", &full_slabs_end);
    471  1.1  christos 	expect_ptr_not_null(
    472  1.1  christos 	    full_slabs, "JSON output should contain full_slabs");
    473  1.1  christos 	const char *full_retained = strstr(full_slabs, "\"nretained_nonhuge\"");
    474  1.1  christos 	expect_true(full_retained != NULL && full_retained < full_slabs_end,
    475  1.1  christos 	    "full_slabs should contain nretained_nonhuge");
    476  1.1  christos 
    477  1.1  christos 	const char *empty_slabs_end = NULL;
    478  1.1  christos 	const char *empty_slabs = json_find_named_object(
    479  1.1  christos 	    sbuf.buf, "empty_slabs", &empty_slabs_end);
    480  1.1  christos 	expect_ptr_not_null(
    481  1.1  christos 	    empty_slabs, "JSON output should contain empty_slabs");
    482  1.1  christos 	const char *empty_retained = strstr(
    483  1.1  christos 	    empty_slabs, "\"nretained_nonhuge\"");
    484  1.1  christos 	expect_true(empty_retained != NULL && empty_retained < empty_slabs_end,
    485  1.1  christos 	    "empty_slabs should contain nretained_nonhuge");
    486  1.1  christos 
    487  1.1  christos 	const char *nonfull_slabs_end = NULL;
    488  1.1  christos 	const char *nonfull_slabs = json_find_named_array(
    489  1.1  christos 	    sbuf.buf, "nonfull_slabs", &nonfull_slabs_end);
    490  1.1  christos 	expect_ptr_not_null(
    491  1.1  christos 	    nonfull_slabs, "JSON output should contain nonfull_slabs");
    492  1.1  christos 	const char *nonfull_retained = strstr(
    493  1.1  christos 	    nonfull_slabs, "\"nretained_nonhuge\"");
    494  1.1  christos 	expect_true(
    495  1.1  christos 	    nonfull_retained != NULL && nonfull_retained < nonfull_slabs_end,
    496  1.1  christos 	    "nonfull_slabs should contain nretained_nonhuge");
    497  1.1  christos 
    498  1.1  christos 	stats_buf_fini(&sbuf);
    499  1.1  christos 	dallocx(p, MALLOCX_TCACHE_NONE);
    500  1.1  christos }
    501  1.1  christos TEST_END
    502  1.1  christos 
    503  1.1  christos int
    504  1.1  christos main(void) {
    505  1.1  christos 	return test_no_reentrancy(test_json_stats_mutexes,
    506  1.1  christos 	    test_hpa_shard_json_ndirty_huge,
    507  1.1  christos 	    test_hpa_shard_json_contains_sec_stats,
    508  1.1  christos 	    test_hpa_shard_json_contains_retained_stats);
    509  1.1  christos }
    510