#include "test/jemalloc_test.h" #include "jemalloc/internal/ctl.h" static void arena_mallctl(const char *mallctl_str, unsigned arena, void *oldp, size_t *oldlen, void *newp, size_t newlen) { int err; char buf[100]; malloc_snprintf(buf, sizeof(buf), mallctl_str, arena); err = mallctl(buf, oldp, oldlen, newp, newlen); expect_d_eq(0, err, "Mallctl failed; %s", buf); } TEST_BEGIN(test_oversize_threshold_get_set) { int err; size_t old_threshold; size_t new_threshold; size_t threshold_sz = sizeof(old_threshold); unsigned arena; size_t arena_sz = sizeof(arena); err = mallctl("arenas.create", (void *)&arena, &arena_sz, NULL, 0); expect_d_eq(0, err, "Arena creation failed"); /* Just a write. */ new_threshold = 1024 * 1024; arena_mallctl("arena.%u.oversize_threshold", arena, NULL, NULL, &new_threshold, threshold_sz); /* Read and write */ new_threshold = 2 * 1024 * 1024; arena_mallctl("arena.%u.oversize_threshold", arena, &old_threshold, &threshold_sz, &new_threshold, threshold_sz); expect_zu_eq(1024 * 1024, old_threshold, "Should have read old value"); /* Just a read */ arena_mallctl("arena.%u.oversize_threshold", arena, &old_threshold, &threshold_sz, NULL, 0); expect_zu_eq(2 * 1024 * 1024, old_threshold, "Should have read old value"); } TEST_END static size_t max_purged = 0; static bool purge_forced_record_max(extent_hooks_t* hooks, void *addr, size_t sz, size_t offset, size_t length, unsigned arena_ind) { if (length > max_purged) { max_purged = length; } return false; } static bool dalloc_record_max(extent_hooks_t *extent_hooks, void *addr, size_t sz, bool comitted, unsigned arena_ind) { if (sz > max_purged) { max_purged = sz; } return false; } extent_hooks_t max_recording_extent_hooks; TEST_BEGIN(test_oversize_threshold) { max_recording_extent_hooks = ehooks_default_extent_hooks; max_recording_extent_hooks.purge_forced = &purge_forced_record_max; max_recording_extent_hooks.dalloc = &dalloc_record_max; extent_hooks_t *extent_hooks = &max_recording_extent_hooks; int err; unsigned arena; size_t arena_sz = sizeof(arena); err = mallctl("arenas.create", (void *)&arena, &arena_sz, NULL, 0); expect_d_eq(0, err, "Arena creation failed"); arena_mallctl("arena.%u.extent_hooks", arena, NULL, NULL, &extent_hooks, sizeof(extent_hooks)); /* * This test will fundamentally race with purging, since we're going to * check the dirty stats to see if our oversized allocation got purged. * We don't want other purging to happen accidentally. We can't just * disable purging entirely, though, since that will also disable * oversize purging. Just set purging intervals to be very large. */ ssize_t decay_ms = 100 * 1000; ssize_t decay_ms_sz = sizeof(decay_ms); arena_mallctl("arena.%u.dirty_decay_ms", arena, NULL, NULL, &decay_ms, decay_ms_sz); arena_mallctl("arena.%u.muzzy_decay_ms", arena, NULL, NULL, &decay_ms, decay_ms_sz); /* Clean everything out. */ arena_mallctl("arena.%u.purge", arena, NULL, NULL, NULL, 0); max_purged = 0; /* Set threshold to 1MB. */ size_t threshold = 1024 * 1024; size_t threshold_sz = sizeof(threshold); arena_mallctl("arena.%u.oversize_threshold", arena, NULL, NULL, &threshold, threshold_sz); /* Allocating and freeing half a megabyte should leave them dirty. */ void *ptr = mallocx(512 * 1024, MALLOCX_ARENA(arena)); dallocx(ptr, MALLOCX_TCACHE_NONE); if (!is_background_thread_enabled()) { expect_zu_lt(max_purged, 512 * 1024, "Expected no 512k purge"); } /* Purge again to reset everything out. */ arena_mallctl("arena.%u.purge", arena, NULL, NULL, NULL, 0); max_purged = 0; /* * Allocating and freeing 2 megabytes should have them purged because of * the oversize threshold. */ ptr = mallocx(2 * 1024 * 1024, MALLOCX_ARENA(arena)); dallocx(ptr, MALLOCX_TCACHE_NONE); expect_zu_ge(max_purged, 2 * 1024 * 1024, "Expected a 2MB purge"); } TEST_END int main(void) { return test_no_reentrancy( test_oversize_threshold_get_set, test_oversize_threshold); }