202 lines
5.7 KiB
C
202 lines
5.7 KiB
C
|
#include "test/jemalloc_test.h"
|
||
|
#include "test/arena_decay.h"
|
||
|
#include "test/guard.h"
|
||
|
|
||
|
#include "jemalloc/internal/guard.h"
|
||
|
|
||
|
static void
|
||
|
verify_extent_guarded(tsdn_t *tsdn, void *ptr) {
|
||
|
expect_true(extent_is_guarded(tsdn, ptr),
|
||
|
"All extents should be guarded.");
|
||
|
}
|
||
|
|
||
|
#define MAX_SMALL_ALLOCATIONS 4096
|
||
|
void *small_alloc[MAX_SMALL_ALLOCATIONS];
|
||
|
|
||
|
TEST_BEGIN(test_guarded_small) {
|
||
|
tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
|
||
|
unsigned npages = 16, pages_found = 0, ends_found = 0;
|
||
|
VARIABLE_ARRAY(uintptr_t, pages, npages);
|
||
|
|
||
|
/* Allocate to get sanitized pointers. */
|
||
|
size_t sz = PAGE / 8;
|
||
|
unsigned n_alloc = 0;
|
||
|
while (n_alloc < MAX_SMALL_ALLOCATIONS) {
|
||
|
void *ptr = malloc(sz);
|
||
|
expect_ptr_not_null(ptr, "Unexpected malloc() failure");
|
||
|
small_alloc[n_alloc] = ptr;
|
||
|
verify_extent_guarded(tsdn, ptr);
|
||
|
if ((uintptr_t)ptr % PAGE == 0) {
|
||
|
pages[pages_found++] = (uintptr_t)ptr;
|
||
|
}
|
||
|
if (((uintptr_t)ptr + (uintptr_t)sz) % PAGE == 0) {
|
||
|
ends_found++;
|
||
|
}
|
||
|
n_alloc++;
|
||
|
if (pages_found == npages && ends_found == npages) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
/* Should found the ptrs being checked for overflow and underflow. */
|
||
|
expect_u_eq(pages_found, npages, "Could not found the expected pages.");
|
||
|
expect_u_eq(ends_found, npages, "Could not found the expected pages.");
|
||
|
|
||
|
/* Verify the pages are not continuous, i.e. separated by guards. */
|
||
|
for (unsigned i = 0; i < npages - 1; i++) {
|
||
|
for (unsigned j = i + 1; j < npages; j++) {
|
||
|
uintptr_t ptr_diff = pages[i] > pages[j] ?
|
||
|
pages[i] - pages[j] : pages[j] - pages[i];
|
||
|
expect_zu_gt((size_t)ptr_diff, 2 * PAGE,
|
||
|
"Pages should not be next to each other.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (unsigned i = 0; i < n_alloc + 1; i++) {
|
||
|
free(small_alloc[i]);
|
||
|
}
|
||
|
}
|
||
|
TEST_END
|
||
|
|
||
|
TEST_BEGIN(test_guarded_large) {
|
||
|
tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
|
||
|
unsigned nlarge = 32;
|
||
|
VARIABLE_ARRAY(uintptr_t, large, nlarge);
|
||
|
|
||
|
/* Allocate to get sanitized pointers. */
|
||
|
size_t large_sz = SC_LARGE_MINCLASS;
|
||
|
for (unsigned i = 0; i < nlarge; i++) {
|
||
|
void *ptr = malloc(large_sz);
|
||
|
verify_extent_guarded(tsdn, ptr);
|
||
|
expect_ptr_not_null(ptr, "Unexpected malloc() failure");
|
||
|
large[i] = (uintptr_t)ptr;
|
||
|
}
|
||
|
|
||
|
/* Verify the pages are not continuous, i.e. separated by guards. */
|
||
|
uintptr_t min_diff = (uintptr_t)-1;
|
||
|
for (unsigned i = 0; i < nlarge; i++) {
|
||
|
for (unsigned j = i + 1; j < nlarge; j++) {
|
||
|
uintptr_t ptr_diff = large[i] > large[j] ?
|
||
|
large[i] - large[j] : large[j] - large[i];
|
||
|
expect_zu_ge((size_t)ptr_diff, large_sz + 2 * PAGE,
|
||
|
"Pages should not be next to each other.");
|
||
|
if (ptr_diff < min_diff) {
|
||
|
min_diff = ptr_diff;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
expect_zu_ge((size_t)min_diff, large_sz + 2 * PAGE,
|
||
|
"Pages should not be next to each other.");
|
||
|
|
||
|
for (unsigned i = 0; i < nlarge; i++) {
|
||
|
free((void *)large[i]);
|
||
|
}
|
||
|
}
|
||
|
TEST_END
|
||
|
|
||
|
static void
|
||
|
verify_pdirty(unsigned arena_ind, uint64_t expected) {
|
||
|
uint64_t pdirty = get_arena_pdirty(arena_ind);
|
||
|
expect_u64_eq(pdirty, expected / PAGE,
|
||
|
"Unexpected dirty page amount.");
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
verify_pmuzzy(unsigned arena_ind, uint64_t expected) {
|
||
|
uint64_t pmuzzy = get_arena_pmuzzy(arena_ind);
|
||
|
expect_u64_eq(pmuzzy, expected / PAGE,
|
||
|
"Unexpected muzzy page amount.");
|
||
|
}
|
||
|
|
||
|
TEST_BEGIN(test_guarded_decay) {
|
||
|
unsigned arena_ind = do_arena_create(-1, -1);
|
||
|
do_decay(arena_ind);
|
||
|
do_purge(arena_ind);
|
||
|
|
||
|
verify_pdirty(arena_ind, 0);
|
||
|
verify_pmuzzy(arena_ind, 0);
|
||
|
|
||
|
/* Verify that guarded extents as dirty. */
|
||
|
size_t sz1 = PAGE, sz2 = PAGE * 2;
|
||
|
/* W/o maps_coalesce, guarded extents are unguarded eagerly. */
|
||
|
size_t add_guard_size = maps_coalesce ? 0 : PAGE_GUARDS_SIZE;
|
||
|
generate_dirty(arena_ind, sz1);
|
||
|
verify_pdirty(arena_ind, sz1 + add_guard_size);
|
||
|
verify_pmuzzy(arena_ind, 0);
|
||
|
|
||
|
/* Should reuse the first extent. */
|
||
|
generate_dirty(arena_ind, sz1);
|
||
|
verify_pdirty(arena_ind, sz1 + add_guard_size);
|
||
|
verify_pmuzzy(arena_ind, 0);
|
||
|
|
||
|
/* Should not reuse; expect new dirty pages. */
|
||
|
generate_dirty(arena_ind, sz2);
|
||
|
verify_pdirty(arena_ind, sz1 + sz2 + 2 * add_guard_size);
|
||
|
verify_pmuzzy(arena_ind, 0);
|
||
|
|
||
|
tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
|
||
|
int flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
|
||
|
|
||
|
/* Should reuse dirty extents for the two mallocx. */
|
||
|
void *p1 = do_mallocx(sz1, flags);
|
||
|
verify_extent_guarded(tsdn, p1);
|
||
|
verify_pdirty(arena_ind, sz2 + add_guard_size);
|
||
|
|
||
|
void *p2 = do_mallocx(sz2, flags);
|
||
|
verify_extent_guarded(tsdn, p2);
|
||
|
verify_pdirty(arena_ind, 0);
|
||
|
verify_pmuzzy(arena_ind, 0);
|
||
|
|
||
|
dallocx(p1, flags);
|
||
|
verify_pdirty(arena_ind, sz1 + add_guard_size);
|
||
|
dallocx(p2, flags);
|
||
|
verify_pdirty(arena_ind, sz1 + sz2 + 2 * add_guard_size);
|
||
|
verify_pmuzzy(arena_ind, 0);
|
||
|
|
||
|
do_purge(arena_ind);
|
||
|
verify_pdirty(arena_ind, 0);
|
||
|
verify_pmuzzy(arena_ind, 0);
|
||
|
|
||
|
if (config_stats) {
|
||
|
expect_u64_eq(get_arena_npurge(arena_ind), 1,
|
||
|
"Expected purging to occur");
|
||
|
expect_u64_eq(get_arena_dirty_npurge(arena_ind), 1,
|
||
|
"Expected purging to occur");
|
||
|
expect_u64_eq(get_arena_dirty_purged(arena_ind),
|
||
|
(sz1 + sz2 + 2 * add_guard_size) / PAGE,
|
||
|
"Expected purging to occur");
|
||
|
expect_u64_eq(get_arena_muzzy_npurge(arena_ind), 0,
|
||
|
"Expected purging to occur");
|
||
|
}
|
||
|
|
||
|
if (opt_retain) {
|
||
|
/*
|
||
|
* With retain, guarded extents are not mergable and will be
|
||
|
* cached in ecache_retained. They should be reused.
|
||
|
*/
|
||
|
void *new_p1 = do_mallocx(sz1, flags);
|
||
|
verify_extent_guarded(tsdn, p1);
|
||
|
expect_ptr_eq(p1, new_p1, "Expect to reuse p1");
|
||
|
|
||
|
void *new_p2 = do_mallocx(sz2, flags);
|
||
|
verify_extent_guarded(tsdn, p2);
|
||
|
expect_ptr_eq(p2, new_p2, "Expect to reuse p2");
|
||
|
|
||
|
dallocx(new_p1, flags);
|
||
|
verify_pdirty(arena_ind, sz1 + add_guard_size);
|
||
|
dallocx(new_p2, flags);
|
||
|
verify_pdirty(arena_ind, sz1 + sz2 + 2 * add_guard_size);
|
||
|
verify_pmuzzy(arena_ind, 0);
|
||
|
}
|
||
|
|
||
|
do_arena_destroy(arena_ind);
|
||
|
}
|
||
|
TEST_END
|
||
|
|
||
|
int
|
||
|
main(void) {
|
||
|
return test(
|
||
|
test_guarded_small,
|
||
|
test_guarded_large,
|
||
|
test_guarded_decay);
|
||
|
}
|