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