jemalloc/test/unit/eset.c
2026-05-27 14:48:38 -07:00

176 lines
6.2 KiB
C

#include "test/jemalloc_test.h"
#include "jemalloc/internal/eset.h"
#define ESET_TEST_ARENA_IND 111
#define ESET_TEST_ADDR_BASE ((uintptr_t)0x30000000U)
static void
test_edata_init(edata_t *edata, uintptr_t addr, size_t size, uint64_t sn,
extent_state_t state, bool pinned) {
memset(edata, 0, sizeof(*edata));
edata_init(edata, ESET_TEST_ARENA_IND, (void *)addr, size,
/* slab */ false, SC_NSIZES, sn, state, /* zeroed */ false,
/* committed */ true, EXTENT_PAI_PAC, EXTENT_NOT_HEAD);
edata_pinned_set(edata, pinned);
}
static void
test_eset_init(eset_t *eset, extent_state_t state) {
memset(eset, 0, sizeof(*eset));
eset_init(eset, state);
}
TEST_BEGIN(test_eset_insert_remove_fit) {
eset_t eset;
test_eset_init(&eset, extent_state_dirty);
edata_t a;
edata_t b;
edata_t c;
edata_t pinned;
test_edata_init(&a, ESET_TEST_ADDR_BASE, 2 * PAGE, 20,
extent_state_dirty, false);
test_edata_init(&b, ESET_TEST_ADDR_BASE + HUGEPAGE, 2 * PAGE, 10,
extent_state_dirty, false);
test_edata_init(&c, ESET_TEST_ADDR_BASE + 2 * HUGEPAGE, 4 * PAGE, 5,
extent_state_dirty, false);
test_edata_init(&pinned, ESET_TEST_ADDR_BASE + 3 * HUGEPAGE, PAGE, 1,
extent_state_dirty, true);
eset_insert(&eset, &a);
eset_insert(&eset, &b);
eset_insert(&eset, &c);
eset_insert(&eset, &pinned);
expect_zu_eq(9, eset_npages_get(&eset),
"Unexpected page count after inserts");
if (config_stats) {
pszind_t pind_2p = sz_psz2ind(
sz_psz_quantize_floor(2 * PAGE));
expect_zu_eq(2, eset_nextents_get(&eset, pind_2p),
"Unexpected extent count in 2-page bin");
expect_zu_eq(4 * PAGE, eset_nbytes_get(&eset, pind_2p),
"Unexpected byte count in 2-page bin");
}
expect_ptr_eq(&a, edata_list_inactive_first(&eset.lru),
"Non-pinned extents should keep insertion LRU order");
expect_ptr_eq(&b, edata_list_inactive_next(&eset.lru, &a),
"Non-pinned extents should keep insertion LRU order");
expect_ptr_eq(&c, edata_list_inactive_next(&eset.lru, &b),
"Pinned extents should be excluded from the LRU");
edata_t *fit = eset_fit(&eset, 2 * PAGE, PAGE,
/* exact_only */ false, SC_PTR_BITS, /* prefer_small */ false);
expect_ptr_eq(&c, fit,
"Default first-fit should choose the oldest fitting extent across "
"larger bins");
fit = eset_fit(&eset, 2 * PAGE, PAGE,
/* exact_only */ true, SC_PTR_BITS, /* prefer_small */ false);
expect_ptr_eq(&b, fit,
"Exact fit should choose the lowest serial number in the size bin");
eset_remove(&eset, &b);
expect_zu_eq(7, eset_npages_get(&eset),
"Unexpected page count after remove");
fit = eset_fit(&eset, 2 * PAGE, PAGE, /* exact_only */ true,
SC_PTR_BITS, /* prefer_small */ false);
expect_ptr_eq(&a, fit,
"Removing the heap min should refresh the bin summary");
eset_remove(&eset, &pinned);
expect_zu_eq(6, eset_npages_get(&eset),
"Pinned removal should still update page counts");
expect_ptr_eq(&a, edata_list_inactive_first(&eset.lru),
"Pinned removal should not disturb LRU contents");
eset_t prefer_eset;
test_eset_init(&prefer_eset, extent_state_dirty);
edata_t small_new;
edata_t large_old;
test_edata_init(&small_new, ESET_TEST_ADDR_BASE + 4 * HUGEPAGE,
4 * PAGE, 100, extent_state_dirty, false);
test_edata_init(&large_old, ESET_TEST_ADDR_BASE + 5 * HUGEPAGE,
8 * PAGE, 1, extent_state_dirty, false);
eset_insert(&prefer_eset, &small_new);
eset_insert(&prefer_eset, &large_old);
fit = eset_fit(&prefer_eset, 3 * PAGE, PAGE,
/* exact_only */ false, SC_PTR_BITS, /* prefer_small */ false);
expect_ptr_eq(&large_old, fit,
"Default first-fit should prefer the oldest suitable extent");
fit = eset_fit(&prefer_eset, 3 * PAGE, PAGE,
/* exact_only */ false, SC_PTR_BITS, /* prefer_small */ true);
expect_ptr_eq(&small_new, fit,
"prefer_small should stop at the smallest fitting bin");
}
TEST_END
TEST_BEGIN(test_eset_alignment_and_large_class_fallback) {
eset_t eset;
test_eset_init(&eset, extent_state_dirty);
edata_t aligned_candidate;
test_edata_init(&aligned_candidate,
ESET_TEST_ADDR_BASE + 2 * PAGE, 4 * PAGE, 1, extent_state_dirty,
false);
eset_insert(&eset, &aligned_candidate);
edata_t *fit = eset_fit(&eset, 2 * PAGE, 4 * PAGE,
/* exact_only */ false, SC_PTR_BITS, /* prefer_small */ false);
expect_ptr_eq(&aligned_candidate, fit,
"Alignment fallback should find a smaller extent that crosses the "
"requested alignment");
eset_t max_fit_eset;
test_eset_init(&max_fit_eset, extent_state_dirty);
edata_t too_large_old;
edata_t bounded_new;
test_edata_init(&too_large_old, ESET_TEST_ADDR_BASE + 6 * HUGEPAGE,
64 * PAGE, 1, extent_state_dirty, false);
test_edata_init(&bounded_new, ESET_TEST_ADDR_BASE + 7 * HUGEPAGE,
4 * PAGE, 100, extent_state_dirty, false);
eset_insert(&max_fit_eset, &too_large_old);
eset_insert(&max_fit_eset, &bounded_new);
fit = eset_fit(&max_fit_eset, 2 * PAGE, PAGE,
/* exact_only */ false, /* lg_max_fit */ 1,
/* prefer_small */ false);
expect_ptr_eq(&bounded_new, fit,
"lg_max_fit should reject excessively large older extents");
}
TEST_END
TEST_BEGIN(test_eset_exact_fit_large_class_disabled) {
test_skip_if(!sz_large_size_classes_disabled());
eset_t exact_eset;
test_eset_init(&exact_eset, extent_state_dirty);
size_t request = SC_LARGE_MINCLASS + PAGE;
edata_t exact;
edata_t larger;
test_edata_init(&exact, ESET_TEST_ADDR_BASE + 8 * HUGEPAGE,
request, 2, extent_state_dirty, false);
test_edata_init(&larger, ESET_TEST_ADDR_BASE + 9 * HUGEPAGE,
request + PAGE, 1, extent_state_dirty, false);
eset_insert(&exact_eset, &larger);
eset_insert(&exact_eset, &exact);
edata_t *fit = eset_fit(&exact_eset, request, PAGE,
/* exact_only */ true, SC_PTR_BITS, /* prefer_small */ false);
expect_ptr_eq(&exact, fit,
"Exact search should enumerate the floor bin when large size "
"classes are disabled");
fit = eset_fit(&exact_eset, request - PAGE, PAGE,
/* exact_only */ true, SC_PTR_BITS, /* prefer_small */ false);
expect_ptr_null(fit,
"Exact search should not return merely larger extents");
}
TEST_END
int
main(void) {
return test_no_reentrancy(test_eset_insert_remove_fit,
test_eset_alignment_and_large_class_fallback,
test_eset_exact_fit_large_class_disabled);
}