From cab741eac844cf6b042df509c0c1929617b501b2 Mon Sep 17 00:00:00 2001 From: Madan Musuvathi Date: Fri, 27 Mar 2026 14:50:53 -0700 Subject: [PATCH] Add 204 coverage tests and 8 bug-triggering tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Merge 204 filtered coverage tests into test/test-api.c (63% → 83% line coverage) --- test/test-api.c | 1946 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1943 insertions(+), 3 deletions(-) diff --git a/test/test-api.c b/test/test-api.c index 763e4cef2..036be13df 100644 --- a/test/test-api.c +++ b/test/test-api.c @@ -27,6 +27,16 @@ we therefore test the API over various inputs. Please add more tests :-) #include #include #include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif #ifdef __cplusplus #include @@ -59,6 +69,221 @@ bool mem_is_zero(uint8_t* p, size_t size) { return true; } +// --------------------------------------------------------------------------- +// Extended coverage helpers (DeepTest) +// --------------------------------------------------------------------------- + +// Output callback for mi_register_output / mi_stats_print_out +static volatile int dt_output_called = 0; +static void dt_output_fun(const char* msg, void* arg) { + (void)arg; + if (msg != NULL) { + dt_output_called++; + } +} + +// Error callback for mi_register_error +static volatile int dt_error_called = 0; +static int dt_last_error_code = 0; +static void dt_error_fun(int err, void* arg) { + (void)arg; + dt_error_called++; + dt_last_error_code = err; +} + +// Deferred free callback +static volatile int dt_deferred_free_called = 0; +static void dt_deferred_free_fun(bool force, unsigned long long heartbeat, void* arg) { + (void)force; (void)heartbeat; (void)arg; + dt_deferred_free_called++; +} + +// Block visitor callback for mi_heap_visit_blocks +static volatile int dt_blocks_visited = 0; +static bool dt_block_visitor(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg) { + (void)heap; (void)area; (void)block_size; (void)arg; + if (block != NULL) { + dt_blocks_visited++; + } + return true; // continue visiting +} + +// Block visitor that stops early +static bool dt_block_visitor_stop(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg) { + (void)heap; (void)area; (void)block_size; (void)arg; + if (block != NULL) { + dt_blocks_visited++; + return false; // stop visiting + } + return true; +} + +// Output callback (uses InterlockedIncrement on Windows) +#ifdef _WIN32 +static volatile long dt2_output_count = 0; +static void dt2_output_fn(const char* msg, void* arg) { + (void)arg; + if (msg != NULL && msg[0] != '\0') { + InterlockedIncrement(&dt2_output_count); + } +} + +// Error callback +static volatile long dt2_error_count = 0; +static volatile int dt2_last_err = 0; +static void dt2_error_fn(int err, void* arg) { + (void)arg; + InterlockedIncrement(&dt2_error_count); + dt2_last_err = err; +} + +// Double-free detection error callback +static volatile long dt2_double_free_error_count = 0; +static void dt2_double_free_error_handler(int err, void* arg) { + (void)arg; + if (err == EAGAIN) { + InterlockedIncrement(&dt2_double_free_error_count); + } +} + +// Block visitor callback +static volatile long dt2_visit_count = 0; +static bool dt2_block_visitor_fn(const mi_heap_t* heap, const mi_heap_area_t* area, + void* block, size_t block_size, void* arg) { + (void)heap; (void)area; (void)block_size; (void)arg; + if (block != NULL) { + InterlockedIncrement(&dt2_visit_count); + } + return true; +} +#endif + +// Thread functions for multi-threaded tests (Windows only) +#ifdef _WIN32 +static DWORD WINAPI dt_thread_alloc_func(LPVOID arg) { + (void)arg; + mi_thread_init(); + void* ptrs[100]; + for (int i = 0; i < 100; i++) { + ptrs[i] = mi_malloc((size_t)(i + 1) * 8); + } + for (int i = 0; i < 100; i++) { + mi_free(ptrs[i]); + } + mi_thread_done(); + return 0; +} + +// Thread that allocates in a heap and exits WITHOUT freeing -> abandonment +static DWORD WINAPI dt2_thread_abandon_allocs(LPVOID arg) { + (void)arg; + mi_thread_init(); + for (int i = 0; i < 500; i++) { + size_t sz = (size_t)((i % 20) + 1) * 16; + void* p = mi_malloc(sz); + (void)p; + } + mi_thread_done(); + return 0; +} + +// Thread that allocates medium and exits without freeing +static DWORD WINAPI dt2_thread_abandon_medium(LPVOID arg) { + (void)arg; + mi_thread_init(); + for (int i = 0; i < 200; i++) { + void* p = mi_malloc(2048 + (size_t)(i * 64)); + (void)p; + } + mi_thread_done(); + return 0; +} + +// Cross-thread free data +typedef struct { + void* ptrs[200]; + int count; + volatile LONG ready; +} dt2_cross_thread_data_t; + +static DWORD WINAPI dt2_thread_alloc_for_cross_free(LPVOID arg) { + dt2_cross_thread_data_t* data = (dt2_cross_thread_data_t*)arg; + mi_thread_init(); + for (int i = 0; i < 200; i++) { + data->ptrs[i] = mi_malloc((size_t)(i + 1) * 8); + } + data->count = 200; + InterlockedExchange(&data->ready, 1); + mi_thread_done(); + return 0; +} + +// Arena thread data +typedef struct { + mi_arena_id_t arena_id; + volatile LONG done; +} dt2_arena_thread_data_t; + +static DWORD WINAPI dt2_thread_arena_alloc(LPVOID arg) { + dt2_arena_thread_data_t* data = (dt2_arena_thread_data_t*)arg; + (void)data; + mi_thread_init(); + for (int i = 0; i < 50; i++) { + void* p = mi_malloc((size_t)(i + 1) * 128); + (void)p; + } + InterlockedExchange(&data->done, 1); + mi_thread_done(); + return 0; +} + +// Thread for aligned allocations +static DWORD WINAPI dt2_thread_aligned_allocs(LPVOID arg) { + (void)arg; + mi_thread_init(); + for (int i = 0; i < 50; i++) { + void* p = mi_malloc_aligned(256, 64); + mi_free(p); + } + mi_thread_done(); + return 0; +} + +// Thread for threadpool mode +static DWORD WINAPI dt2_thread_threadpool_mode(LPVOID arg) { + (void)arg; + mi_thread_init(); + mi_thread_set_in_threadpool(); + void* p = mi_malloc(128); + mi_free(p); + mi_thread_done(); + return 0; +} + +// Reclaim thread data +typedef struct { + void* ptrs[500]; + int count; + volatile LONG ready; + volatile LONG can_exit; +} dt2_reclaim_thread_data_t; + +static DWORD WINAPI dt2_thread_alloc_wait_reclaim(LPVOID arg) { + dt2_reclaim_thread_data_t* data = (dt2_reclaim_thread_data_t*)arg; + mi_thread_init(); + for (int i = 0; i < 500; i++) { + data->ptrs[i] = mi_malloc(32); + } + data->count = 500; + InterlockedExchange(&data->ready, 1); + while (InterlockedCompareExchange(&data->can_exit, 0, 0) == 0) { + Sleep(1); + } + mi_thread_done(); + return 0; +} +#endif + // --------------------------------------------------------------------------- // Main testing // --------------------------------------------------------------------------- @@ -372,9 +597,1724 @@ int main(void) { CHECK("stl_heap_allocator3", test_stl_heap_allocator3()); CHECK("stl_heap_allocator4", test_stl_heap_allocator4()); - // --------------------------------------------------- - // Done - // ---------------------------------------------------[] + + // --------------------------------- + // Extended API coverage (DeepTest) + // --------------------------------- + + CHECK_BODY("cfree") { + void* p = mi_malloc(64); + mi_cfree(p); + result = true; + }; + + CHECK_BODY("cfree-null") { + mi_cfree(NULL); + result = true; + }; + + CHECK_BODY("valloc") { + void* p = mi_valloc(128); + result = (p != NULL); + #ifdef _WIN32 + SYSTEM_INFO si; + GetSystemInfo(&si); + result = result && ((uintptr_t)p % si.dwPageSize == 0); + #endif + mi_free(p); + }; + + CHECK_BODY("valloc-zero") { + void* p = mi_valloc(0); + result = (p != NULL); + mi_free(p); + }; + + CHECK_BODY("pvalloc") { + void* p = mi_pvalloc(64); + result = (p != NULL); + mi_free(p); + }; + + CHECK_BODY("pvalloc-zero") { + void* p = mi_pvalloc(0); + result = (p != NULL); + mi_free(p); + }; + + CHECK_BODY("aligned_alloc") { + void* p = mi_aligned_alloc(64, 128); + result = (p != NULL && (uintptr_t)p % 64 == 0); + mi_free(p); + }; + + CHECK_BODY("aligned_alloc-16") { + void* p = mi_aligned_alloc(16, 48); + result = (p != NULL && (uintptr_t)p % 16 == 0); + mi_free(p); + }; + + CHECK_BODY("reallocarray-overflow") { + void* p = mi_malloc(16); + void* q = mi_reallocarray(p, SIZE_MAX, SIZE_MAX); + result = (q == NULL); + mi_free(p); + }; + + CHECK_BODY("reallocarr") { + void* p = mi_malloc(16); + int err = mi_reallocarr(&p, 10, 16); + result = (err == 0 && p != NULL); + mi_free(p); + }; + + CHECK_BODY("reallocarr-null-ptr") { + void* p = NULL; + int err = mi_reallocarr(&p, 5, 32); + result = (err == 0 && p != NULL); + mi_free(p); + }; + + CHECK_BODY("reallocarr-overflow") { + void* p = mi_malloc(16); + void* orig = p; + int err = mi_reallocarr(&p, SIZE_MAX, SIZE_MAX); + result = (err != 0 && p == orig); + mi_free(p); + }; + + CHECK_BODY("reallocarr-zero") { + void* p = mi_malloc(64); + int err = mi_reallocarr(&p, 0, 0); + result = (err == 0); + mi_free(p); + }; + + CHECK_BODY("mi__expand") { + void* p = mi_malloc(32); + void* q = mi__expand(p, 32); + result = (q == NULL || q == p); + mi_free(p); + }; + + CHECK_BODY("mi__expand-null") { + void* q = mi__expand(NULL, 32); + result = (q == NULL); + }; + + CHECK_BODY("wcsdup") { + unsigned short wstr[] = { 'H', 'e', 'l', 'l', 'o', 0 }; + unsigned short* dup = mi_wcsdup(wstr); + result = (dup != NULL); + if (dup) { + bool match = true; + for (int i = 0; i <= 5; i++) { + if (dup[i] != wstr[i]) { match = false; break; } + } + result = result && match; + } + mi_free(dup); + }; + + CHECK_BODY("wcsdup-empty") { + unsigned short wstr[] = { 0 }; + unsigned short* dup = mi_wcsdup(wstr); + result = (dup != NULL && dup[0] == 0); + mi_free(dup); + }; + + CHECK_BODY("mbsdup") { + unsigned char mbstr[] = "Hello World"; + unsigned char* dup = mi_mbsdup(mbstr); + result = (dup != NULL && strcmp((char*)dup, (char*)mbstr) == 0); + mi_free(dup); + }; + + CHECK_BODY("mbsdup-empty") { + unsigned char mbstr[] = ""; + unsigned char* dup = mi_mbsdup(mbstr); + result = (dup != NULL && dup[0] == 0); + mi_free(dup); + }; + + CHECK_BODY("dupenv_s") { + char* buf = NULL; + size_t sz = 0; + int err = mi_dupenv_s(&buf, &sz, "PATH"); + result = (err == 0); + mi_free(buf); + }; + + CHECK_BODY("dupenv_s-nonexistent") { + char* buf = NULL; + size_t sz = 0; + int err = mi_dupenv_s(&buf, &sz, "MI_TEST_NONEXISTENT_VAR_XYZ_12345"); + result = (err == 0 && buf == NULL); + mi_free(buf); + }; + + CHECK_BODY("dupenv_s-null-args") { + int err = mi_dupenv_s(NULL, NULL, "PATH"); + result = (err != 0); + }; + + CHECK_BODY("wdupenv_s") { + unsigned short* buf = NULL; + size_t sz = 0; + unsigned short name[] = { 'P', 'A', 'T', 'H', 0 }; + int err = mi_wdupenv_s(&buf, &sz, name); + result = (err == 0); + mi_free(buf); + }; + + CHECK_BODY("wdupenv_s-null") { + int err = mi_wdupenv_s(NULL, NULL, NULL); + result = (err != 0); + }; + + CHECK_BODY("aligned_recalloc") { + void* p = mi_calloc(4, 16); + void* q = mi_aligned_recalloc(p, 8, 16, 32); + result = (q != NULL && (uintptr_t)q % 32 == 0); + if (q) result = result && mem_is_zero((uint8_t*)q + 64, 64); + mi_free(q); + }; + + CHECK_BODY("aligned_recalloc-null") { + void* q = mi_aligned_recalloc(NULL, 4, 16, 32); + result = (q != NULL && (uintptr_t)q % 32 == 0); + mi_free(q); + }; + + CHECK_BODY("aligned_offset_recalloc") { + void* p = mi_calloc(4, 16); + void* q = mi_aligned_offset_recalloc(p, 8, 16, 32, 0); + result = (q != NULL); + mi_free(q); + }; + + CHECK_BODY("aligned_offset_recalloc-null") { + void* q = mi_aligned_offset_recalloc(NULL, 4, 16, 32, 0); + result = (q != NULL); + mi_free(q); + }; + + CHECK_BODY("malloc_size") { + void* p = mi_malloc(42); + size_t sz = mi_malloc_size(p); + result = (sz >= 42); + mi_free(p); + }; + + CHECK_BODY("malloc_size-null") { + size_t sz = mi_malloc_size(NULL); + result = (sz == 0); + }; + + CHECK_BODY("malloc_usable_size") { + void* p = mi_malloc(100); + size_t sz = mi_malloc_usable_size(p); + result = (sz >= 100); + mi_free(p); + }; + + CHECK_BODY("malloc_usable_size-null") { + size_t sz = mi_malloc_usable_size(NULL); + result = (sz == 0); + }; + + CHECK_BODY("malloc_good_size") { + size_t gs = mi_malloc_good_size(42); + result = (gs >= 42); + }; + + CHECK_BODY("malloc_good_size-zero") { + size_t gs0 = mi_malloc_good_size(0); + result = (gs0 <= 64); + }; + + CHECK_BODY("malloc_good_size-large") { + size_t gs = mi_malloc_good_size(1024 * 1024); + result = (gs >= 1024 * 1024); + }; + + CHECK_BODY("mi_version") { + int v = mi_version(); + result = (v >= 100); + }; + + CHECK_BODY("option-get-size") { + size_t sz = mi_option_get_size(mi_option_reserve_os_memory); + result = true; + (void)sz; + }; + + CHECK_BODY("option-set-default") { + mi_option_set_default(mi_option_purge_delay, 20); + result = true; + }; + + CHECK_BODY("option-set-enabled-default") { + mi_option_set_enabled_default(mi_option_eager_commit, true); + result = true; + }; + + CHECK_BODY("register-output") { + dt_output_called = 0; + mi_register_output(dt_output_fun, NULL); + mi_stats_print_out(dt_output_fun, NULL); + result = (dt_output_called > 0); + mi_register_output(NULL, NULL); + }; + + CHECK_BODY("register-error") { + dt_error_called = 0; + mi_register_error(dt_error_fun, NULL); + void* p = mi_malloc((size_t)PTRDIFF_MAX + 1); + result = (p == NULL); + mi_register_error(NULL, NULL); + }; + + CHECK_BODY("options-print") { + mi_options_print(); + result = true; + }; + + CHECK_BODY("register-deferred-free") { + dt_deferred_free_called = 0; + mi_register_deferred_free(dt_deferred_free_fun, NULL); + void* p = mi_malloc(64); + mi_free(p); + for (int i = 0; i < 100; i++) { + p = mi_malloc(64); + mi_free(p); + } + result = true; + mi_register_deferred_free(NULL, NULL); + }; + + CHECK_BODY("option-arena-reserve") { + size_t sz = mi_option_get_size(mi_option_arena_reserve); + result = true; + (void)sz; + }; + + CHECK_BODY("thread-init-done") { + mi_thread_init(); + mi_thread_done(); + result = true; + }; + + CHECK_BODY("is-redirected") { + bool redir = mi_is_redirected(); + result = true; + (void)redir; + }; + + CHECK_BODY("subproc-new-delete") { + mi_subproc_id_t sp = mi_subproc_new(); + result = (sp != NULL); + mi_subproc_delete(sp); + }; + + CHECK_BODY("heap-guarded-set") { + mi_heap_t* heap = mi_heap_new(); + mi_heap_guarded_set_sample_rate(heap, 0, 0); + mi_heap_guarded_set_size_bound(heap, 0, 1024); + result = true; + mi_heap_destroy(heap); + }; + + CHECK_BODY("thread-set-in-threadpool") { + mi_thread_set_in_threadpool(); + result = true; + }; + + CHECK_BODY("collect-reduce") { + mi_collect_reduce(0); + result = true; + }; + + CHECK_BODY("heap-visit-blocks-no-visit") { + mi_heap_t* heap = mi_heap_new(); + void* p = mi_heap_malloc(heap, 64); + dt_blocks_visited = 0; + mi_heap_visit_blocks(heap, false, dt_block_visitor, NULL); + result = true; + mi_free(p); + mi_heap_destroy(heap); + }; + + CHECK_BODY("heap-visit-blocks-stop") { + mi_heap_t* heap = mi_heap_new(); + void* ptrs[20]; + for (int i = 0; i < 20; i++) { + ptrs[i] = mi_heap_malloc(heap, 64); + } + dt_blocks_visited = 0; + mi_heap_visit_blocks(heap, true, dt_block_visitor_stop, NULL); + result = (dt_blocks_visited >= 1); + for (int i = 0; i < 20; i++) { + mi_free(ptrs[i]); + } + mi_heap_destroy(heap); + }; + + CHECK_BODY("check-owned") { + void* p = mi_malloc(64); + bool owned = mi_check_owned(p); + result = owned; + mi_free(p); + }; + + CHECK_BODY("check-owned-false") { + int stack_var = 42; + bool owned = mi_check_owned(&stack_var); + result = !owned; + }; + + CHECK_BODY("heap-check-owned") { + mi_heap_t* heap = mi_heap_new(); + void* p = mi_heap_malloc(heap, 64); + bool owned = mi_heap_check_owned(heap, p); + result = owned; + mi_free(p); + mi_heap_destroy(heap); + }; + + CHECK_BODY("heap-check-owned-false") { + mi_heap_t* heap = mi_heap_new(); + void* p = mi_malloc(64); + bool owned = mi_heap_check_owned(heap, p); + result = !owned; + mi_free(p); + mi_heap_destroy(heap); + }; + + CHECK_BODY("heap-new-ex-tag") { + mi_heap_t* heap = mi_heap_new_ex(42, false, 0); + result = (heap != NULL); + void* p = mi_heap_malloc(heap, 64); + mi_free(p); + mi_heap_delete(heap); + }; + + CHECK_BODY("heap-set-get-default") { + (void)mi_heap_get_default(); + mi_heap_t* heap = mi_heap_new(); + mi_heap_t* prev = mi_heap_set_default(heap); + mi_heap_t* cur = mi_heap_get_default(); + result = (cur == heap); + mi_heap_set_default(prev); + mi_heap_delete(heap); + }; + + CHECK_BODY("heap-reallocf") { + mi_heap_t* heap = mi_heap_new(); + void* p = mi_heap_malloc(heap, 64); + void* q = mi_heap_reallocf(heap, p, 128); + result = (q != NULL); + mi_free(q); + mi_heap_destroy(heap); + }; + + CHECK_BODY("heap-strdup") { + mi_heap_t* heap = mi_heap_new(); + char* s = mi_heap_strdup(heap, "hello world"); + result = (s != NULL && strcmp(s, "hello world") == 0); + mi_free(s); + mi_heap_destroy(heap); + }; + + CHECK_BODY("heap-new-delete") { + mi_heap_t* heap = mi_heap_new(); + int* p = (int*)mi_heap_malloc(heap, sizeof(int)); + *p = 42; + mi_heap_delete(heap); + result = (*p == 42); + mi_free(p); + }; + + CHECK_BODY("expand") { + void* p = mi_malloc(32); + void* q = mi_expand(p, 64); + result = (q == NULL || q == p); + mi_free(p); + }; + + CHECK_BODY("expand-null") { + void* q = mi_expand(NULL, 64); + result = (q == NULL); + }; + + CHECK_BODY("expand-zero") { + void* p = mi_malloc(32); + void* q = mi_expand(p, 0); + result = (q == NULL || q == p); + mi_free(p); + }; + + CHECK_BODY("mallocn-overflow") { + void* p = mi_mallocn(SIZE_MAX / 2, 3); + result = (p == NULL); + }; + + CHECK_BODY("reallocn-overflow") { + void* p = mi_malloc(64); + void* q = mi_reallocn(p, SIZE_MAX / 2, 3); + result = (q == NULL); + mi_free(p); + }; + + CHECK_BODY("reallocf") { + void* p = mi_malloc(64); + void* q = mi_reallocf(p, 128); + result = (q != NULL); + mi_free(q); + }; + + CHECK_BODY("reallocf-fail") { + void* p = mi_malloc(64); + void* q = mi_reallocf(p, (size_t)PTRDIFF_MAX + 1); + result = (q == NULL); + }; + + CHECK_BODY("strdup") { + char* s = mi_strdup("test string"); + result = (s != NULL && strcmp(s, "test string") == 0); + mi_free(s); + }; + + CHECK_BODY("strdup-empty") { + char* s = mi_strdup(""); + result = (s != NULL && s[0] == '\0'); + mi_free(s); + }; + + CHECK_BODY("strndup") { + char* s = mi_strndup("hello world", 5); + result = (s != NULL && strncmp(s, "hello", 5) == 0 && s[5] == '\0'); + mi_free(s); + }; + + CHECK_BODY("strndup-longer") { + char* s = mi_strndup("hi", 100); + result = (s != NULL && strcmp(s, "hi") == 0); + mi_free(s); + }; + + CHECK_BODY("usable-size-null") { + size_t us = mi_usable_size(NULL); + result = (us == 0); + }; + + CHECK_BODY("free-size") { + void* p = mi_malloc(64); + mi_free_size(p, 64); + result = true; + }; + + CHECK_BODY("free-size-null") { + mi_free_size(NULL, 0); + result = true; + }; + + CHECK_BODY("free-size-aligned") { + void* p = mi_malloc_aligned(128, 64); + mi_free_size_aligned(p, 128, 64); + result = true; + }; + + CHECK_BODY("free-size-aligned-null") { + mi_free_size_aligned(NULL, 0, 16); + result = true; + }; + + CHECK_BODY("free-aligned") { + void* p = mi_malloc_aligned(64, 32); + mi_free_aligned(p, 32); + result = true; + }; + + CHECK_BODY("free-aligned-null") { + mi_free_aligned(NULL, 32); + result = true; + }; + + CHECK_BODY("new-nothrow") { + void* p = mi_new_nothrow(64); + result = (p != NULL); + mi_free(p); + }; + + CHECK_BODY("new-aligned-nothrow") { + void* p = mi_new_aligned_nothrow(64, 32); + result = (p != NULL && (uintptr_t)p % 32 == 0); + mi_free(p); + }; + + CHECK_BODY("new-n") { + void* p = mi_new_n(10, 16); + result = (p != NULL); + mi_free(p); + }; + + CHECK_BODY("new-realloc") { + void* p = mi_malloc(64); + void* q = mi_new_realloc(p, 128); + result = (q != NULL); + mi_free(q); + }; + + CHECK_BODY("new-reallocn") { + void* p = mi_malloc(64); + void* q = mi_new_reallocn(p, 10, 16); + result = (q != NULL); + mi_free(q); + }; + + CHECK_BODY("new") { + void* p = mi_new(64); + result = (p != NULL); + mi_free(p); + }; + + CHECK_BODY("new-aligned") { + void* p = mi_new_aligned(64, 32); + result = (p != NULL && (uintptr_t)p % 32 == 0); + mi_free(p); + }; + + CHECK_BODY("heap-alloc-new") { + mi_heap_t* heap = mi_heap_new(); + void* p = mi_heap_alloc_new(heap, 64); + result = (p != NULL); + mi_free(p); + mi_heap_destroy(heap); + }; + + CHECK_BODY("heap-alloc-new-n") { + mi_heap_t* heap = mi_heap_new(); + void* p = mi_heap_alloc_new_n(heap, 10, 16); + result = (p != NULL); + mi_free(p); + mi_heap_destroy(heap); + }; + + CHECK_BODY("stats-merge") { + mi_stats_merge(); + result = true; + }; + + CHECK_BODY("stats-print") { + mi_stats_print(NULL); + result = true; + }; + + CHECK_BODY("stats-print-out") { + dt_output_called = 0; + mi_stats_print_out(dt_output_fun, NULL); + result = (dt_output_called > 0); + }; + + CHECK_BODY("thread-stats-print-out") { + dt_output_called = 0; + mi_thread_stats_print_out(dt_output_fun, NULL); + result = (dt_output_called > 0); + }; + + CHECK_BODY("reserve-os-memory") { + int res = mi_reserve_os_memory(4 * 1024 * 1024, true, false); + result = (res == 0); + }; + + CHECK_BODY("reserve-os-memory-ex") { + mi_arena_id_t arena_id = 0; + int res = mi_reserve_os_memory_ex(4 * 1024 * 1024, true, false, false, &arena_id); + result = (res == 0); + }; + + CHECK_BODY("arena-area") { + mi_arena_id_t arena_id = 0; + int res = mi_reserve_os_memory_ex(2 * 1024 * 1024, true, false, true, &arena_id); + if (res == 0) { + size_t sz = 0; + void* area = mi_arena_area(arena_id, &sz); + result = (area != NULL && sz > 0); + } else { + result = true; + } + }; + + CHECK_BODY("arenas-print") { + mi_arenas_print(); + result = true; + }; + + CHECK_BODY("is-in-heap-region-false") { + int stack_var = 42; + bool in_region = mi_is_in_heap_region(&stack_var); + result = !in_region; + }; + + CHECK_BODY("heap-new-in-arena") { + mi_arena_id_t arena_id = 0; + size_t sz = 0; + void* area = mi_arena_area(arena_id, &sz); + result = true; + (void)area; + }; + + CHECK_BODY("manage-os-memory") { + #ifdef _WIN32 + void* mem = VirtualAlloc(NULL, 1024 * 1024, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (mem) { + bool ok = mi_manage_os_memory(mem, 1024 * 1024, true, false, true, -1); + result = true; + (void)ok; + } else { + result = true; + } + #else + result = true; + #endif + }; + + CHECK_BODY("realloc-shrink-chain") { + void* p = mi_malloc(1024 * 1024); + bool ok = (p != NULL); + for (size_t sz = 512 * 1024; sz >= 8 && ok; sz /= 2) { + void* q = mi_realloc(p, sz); + ok = (q != NULL); + p = q; + } + mi_free(p); + result = ok; + }; + + CHECK_BODY("zalloc-aligned-at") { + uint8_t* p = (uint8_t*)mi_zalloc_aligned_at(128, 64, 0); + result = (p != NULL && mem_is_zero(p, 128)); + mi_free(p); + }; + + CHECK_BODY("calloc-aligned-at") { + uint8_t* p = (uint8_t*)mi_calloc_aligned_at(4, 32, 64, 0); + result = (p != NULL && mem_is_zero(p, 128)); + mi_free(p); + }; + + CHECK_BODY("realloc-aligned-at") { + void* p = mi_malloc_aligned_at(64, 32, 0); + void* q = mi_realloc_aligned_at(p, 128, 32, 0); + result = (q != NULL); + mi_free(q); + }; + + CHECK_BODY("rezalloc-aligned-at") { + uint8_t* p = (uint8_t*)mi_zalloc_aligned_at(64, 32, 0); + uint8_t* q = (uint8_t*)mi_rezalloc_aligned_at(p, 128, 32, 0); + result = (q != NULL && mem_is_zero(q, 128)); + mi_free(q); + }; + + CHECK_BODY("recalloc-aligned-at") { + uint8_t* p = (uint8_t*)mi_calloc_aligned_at(4, 16, 32, 0); + uint8_t* q = (uint8_t*)mi_recalloc_aligned_at(p, 8, 16, 32, 0); + result = (q != NULL && mem_is_zero(q, 128)); + mi_free(q); + }; + + CHECK_BODY("heap-realloc-aligned-at") { + mi_heap_t* heap = mi_heap_new(); + void* p = mi_heap_malloc_aligned_at(heap, 64, 32, 0); + void* q = mi_heap_realloc_aligned_at(heap, p, 128, 32, 0); + result = (q != NULL); + mi_free(q); + mi_heap_destroy(heap); + }; + + CHECK_BODY("heap-rezalloc-aligned-at") { + mi_heap_t* heap = mi_heap_new(); + uint8_t* p = (uint8_t*)mi_heap_zalloc_aligned_at(heap, 64, 32, 0); + uint8_t* q = (uint8_t*)mi_heap_rezalloc_aligned_at(heap, p, 128, 32, 0); + result = (q != NULL && mem_is_zero(q, 128)); + mi_free(q); + mi_heap_destroy(heap); + }; + + CHECK_BODY("heap-recalloc-aligned-at") { + mi_heap_t* heap = mi_heap_new(); + uint8_t* p = (uint8_t*)mi_heap_calloc_aligned_at(heap, 4, 16, 32, 0); + uint8_t* q = (uint8_t*)mi_heap_recalloc_aligned_at(heap, p, 8, 16, 32, 0); + result = (q != NULL && mem_is_zero(q, 128)); + mi_free(q); + mi_heap_destroy(heap); + }; + + CHECK_BODY("ucalloc") { + size_t bsize = 0; + uint8_t* p = (uint8_t*)mi_ucalloc(4, 16, &bsize); + result = (p != NULL && bsize >= 64 && mem_is_zero(p, 64)); + mi_free(p); + }; + + CHECK_BODY("umalloc-aligned") { + size_t bsize = 0; + void* p = mi_umalloc_aligned(64, 32, &bsize); + result = (p != NULL && bsize >= 64 && (uintptr_t)p % 32 == 0); + mi_free(p); + }; + + CHECK_BODY("uzalloc-aligned") { + size_t bsize = 0; + uint8_t* p = (uint8_t*)mi_uzalloc_aligned(64, 32, &bsize); + result = (p != NULL && bsize >= 64 && (uintptr_t)p % 32 == 0 && mem_is_zero(p, 64)); + mi_free(p); + }; + + CHECK_BODY("umalloc-small") { + size_t bsize = 0; + void* p = mi_umalloc_small(16, &bsize); + result = (p != NULL && bsize >= 16); + mi_free(p); + }; + + CHECK_BODY("uzalloc-small-via-uzalloc-aligned") { + size_t bsize = 0; + uint8_t* p = (uint8_t*)mi_uzalloc_aligned(16, sizeof(void*), &bsize); + result = (p != NULL && bsize >= 16 && mem_is_zero(p, 16)); + mi_free(p); + }; + + CHECK_BODY("libc-strdup-patterns") { + bool ok = true; + char buf[2048]; + for (int len = 0; len < 256; len++) { + for (int j = 0; j < len; j++) buf[j] = 'A' + (j % 26); + buf[len] = '\0'; + char* s = mi_strdup(buf); + ok = ok && (s != NULL) && (strcmp(s, buf) == 0); + mi_free(s); + } + result = ok; + }; + + #ifdef _WIN32 + CHECK_BODY("mt-basic-alloc") { + HANDLE threads[4]; + for (int i = 0; i < 4; i++) { + threads[i] = CreateThread(NULL, 0, dt_thread_alloc_func, NULL, 0, NULL); + } + WaitForMultipleObjects(4, threads, TRUE, 10000); + for (int i = 0; i < 4; i++) { + CloseHandle(threads[i]); + } + result = true; + }; + #endif + + CHECK_BODY("heap-delete-with-data") { + mi_heap_t* heap = mi_heap_new(); + void* ptrs[50]; + for (int i = 0; i < 50; i++) { + ptrs[i] = mi_heap_malloc(heap, 64); + } + mi_heap_delete(heap); + for (int i = 0; i < 50; i++) { + mi_free(ptrs[i]); + } + result = true; + }; + + CHECK_BODY("many-heaps") { + bool ok = true; + mi_heap_t* heaps[20]; + for (int i = 0; i < 20; i++) { + heaps[i] = mi_heap_new(); + ok = ok && (heaps[i] != NULL); + void* p = mi_heap_malloc(heaps[i], 64); + mi_free(p); + } + for (int i = 0; i < 20; i++) { + mi_heap_destroy(heaps[i]); + } + result = ok; + }; + + CHECK_BODY("option-get-clamp-min") { + mi_option_set(mi_option_purge_delay, -1); + long v = mi_option_get_clamp(mi_option_purge_delay, 0, 1000); + result = (v >= 0); + mi_option_set(mi_option_purge_delay, 10); + }; + + CHECK_BODY("option-guarded-set") { + long old_min = mi_option_get(mi_option_guarded_min); + long old_max = mi_option_get(mi_option_guarded_max); + mi_option_set(mi_option_guarded_min, 1000); + long new_max = mi_option_get(mi_option_guarded_max); + result = (new_max >= 1000); + mi_option_set(mi_option_guarded_max, 100); + long new_min = mi_option_get(mi_option_guarded_min); + result = result && (new_min <= 100); + mi_option_set(mi_option_guarded_min, old_min); + mi_option_set(mi_option_guarded_max, old_max); + }; + + CHECK_BODY("deferred-free-callback") { + mi_register_deferred_free(NULL, NULL); + result = true; + }; + + CHECK_BODY("subproc-new-delete2") { + mi_subproc_id_t sp = mi_subproc_new(); + result = (sp != NULL); + if (sp != NULL && sp != mi_subproc_main()) { + mi_subproc_delete(sp); + } + }; + + CHECK_BODY("version") { + int v = mi_version(); + result = (v > 0); + }; + + CHECK_BODY("mallocn-various") { + bool ok = true; + void* p = mi_mallocn(100, 16); + ok = ok && (p != NULL); + mi_free(p); + p = mi_mallocn(0, 16); + ok = ok && (p != NULL); + mi_free(p); + p = mi_mallocn(SIZE_MAX / 2, SIZE_MAX / 2); + ok = ok && (p == NULL); + result = ok; + }; + + CHECK_BODY("expand-null2") { + void* p = mi_expand(NULL, 64); + result = (p == NULL); + }; + + CHECK_BODY("expand-small") { + void* p = mi_malloc(64); + void* q = mi_expand(p, 32); + if (q != NULL) { + mi_free(q); + } else { + mi_free(p); + } + result = true; + }; + + CHECK_BODY("heap-contains-block-false") { + mi_heap_t* heap1 = mi_heap_new(); + mi_heap_t* heap2 = mi_heap_new(); + void* p = mi_heap_malloc(heap1, 64); + bool contains = mi_heap_contains_block(heap2, p); + result = !contains; + mi_free(p); + mi_heap_destroy(heap1); + mi_heap_destroy(heap2); + }; + + CHECK_BODY("heap-check-owned2") { + mi_heap_t* heap = mi_heap_new(); + void* p = mi_heap_malloc(heap, 64); + bool owned = mi_heap_check_owned(heap, p); + result = owned; + mi_free(p); + mi_heap_destroy(heap); + }; + + CHECK_BODY("check-owned2") { + void* p = mi_malloc(64); + bool owned = mi_check_owned(p); + result = owned; + mi_free(p); + }; + + CHECK_BODY("check-owned-false2") { + int stack_var = 42; + bool owned = mi_check_owned(&stack_var); + result = !owned; + }; + + CHECK_BODY("zalloc-aligned-at-various") { + bool ok = true; + size_t offsets[] = {0, 8, 16, 32, 0}; + for (int o = 0; offsets[o] != 0 || o == 0; o++) { + uint8_t* p = (uint8_t*)mi_zalloc_aligned_at(256, 64, offsets[o]); + ok = ok && (p != NULL); + if (p) ok = ok && mem_is_zero(p, 256); + mi_free(p); + if (offsets[o] == 0 && o > 0) break; + } + result = ok; + }; + + CHECK_BODY("calloc-aligned-at-various") { + bool ok = true; + uint8_t* p = (uint8_t*)mi_calloc_aligned_at(8, 32, 64, 16); + ok = ok && (p != NULL); + if (p) ok = ok && mem_is_zero(p, 256); + mi_free(p); + result = ok; + }; + + CHECK_BODY("mi-expand") { + void* p = mi_malloc(64); + void* q = mi__expand(p, 32); + if (q != NULL) { + mi_free(q); + } else { + mi_free(p); + } + result = true; + }; + + CHECK_BODY("malloc-size2") { + void* p = mi_malloc(42); + size_t sz = mi_malloc_size(p); + result = (sz >= 42); + mi_free(p); + }; + + CHECK_BODY("malloc-good-size2") { + size_t sz = mi_malloc_good_size(42); + result = (sz >= 42); + }; + + CHECK_BODY("malloc-usable-size2") { + void* p = mi_malloc(42); + size_t sz = mi_malloc_usable_size(p); + result = (sz >= 42); + mi_free(p); + }; + + CHECK_BODY("wcsdup2") { + unsigned short str[] = {'H', 'e', 'l', 'l', 'o', 0}; + unsigned short* dup = mi_wcsdup(str); + bool ok = (dup != NULL); + if (ok) { + for (int i = 0; i < 6; i++) { + ok = ok && (dup[i] == str[i]); + } + } + mi_free(dup); + result = ok; + }; + + CHECK_BODY("mbsdup2") { + unsigned char str[] = "test string"; + unsigned char* dup = mi_mbsdup(str); + bool ok = (dup != NULL); + if (ok) { + ok = (strcmp((char*)dup, (char*)str) == 0); + } + mi_free(dup); + result = ok; + }; + + CHECK_BODY("dupenv-s") { + char* buf = NULL; + size_t sz = 0; + int res = mi_dupenv_s(&buf, &sz, "PATH"); + result = (res == 0 && buf != NULL && sz > 0); + mi_free(buf); + }; + + CHECK_BODY("dupenv-s-nonexistent") { + char* buf = NULL; + size_t sz = 0; + int res = mi_dupenv_s(&buf, &sz, "MIMALLOC_NONEXISTENT_VAR_12345"); + result = (res == 0); + mi_free(buf); + }; + + CHECK_BODY("aligned-recalloc") { + void* p = mi_malloc_aligned(64, 32); + void* q = mi_aligned_recalloc(p, 4, 32, 32); + result = (q != NULL && (uintptr_t)q % 32 == 0); + mi_free(q); + }; + + CHECK_BODY("aligned-offset-recalloc") { + void* p = mi_malloc_aligned_at(64, 32, 0); + void* q = mi_aligned_offset_recalloc(p, 4, 32, 32, 0); + result = (q != NULL); + mi_free(q); + }; + + CHECK_BODY("reallocarr2") { + void* p = mi_malloc(64); + int err = mi_reallocarr(&p, 8, 32); + result = (err == 0 && p != NULL); + mi_free(p); + }; + + CHECK_BODY("reallocarr-overflow2") { + void* p = mi_malloc(64); + int err = mi_reallocarr(&p, SIZE_MAX / 2, SIZE_MAX / 2); + result = (err != 0); + mi_free(p); + }; + + CHECK_BODY("new-nothrow2") { + void* p = mi_new_nothrow(64); + result = (p != NULL); + mi_free(p); + }; + + CHECK_BODY("new-aligned-nothrow2") { + void* p = mi_new_aligned_nothrow(64, 32); + result = (p != NULL && (uintptr_t)p % 32 == 0); + mi_free(p); + }; + + CHECK_BODY("new-n2") { + void* p = mi_new_n(10, 16); + result = (p != NULL); + mi_free(p); + }; + + CHECK_BODY("new-realloc2") { + void* p = mi_new(64); + void* q = mi_new_realloc(p, 128); + result = (q != NULL); + mi_free(q); + }; + + CHECK_BODY("new-reallocn2") { + void* p = mi_new(64); + void* q = mi_new_reallocn(p, 8, 16); + result = (q != NULL); + mi_free(q); + }; + + CHECK_BODY("reserve-os-large") { + int res = mi_reserve_os_memory(64 * 1024 * 1024, true, false); + result = (res == 0); + }; + + CHECK_BODY("reserve-os-no-commit") { + int res = mi_reserve_os_memory(16 * 1024 * 1024, false, false); + result = (res == 0); + }; + + CHECK_BODY("heap-guarded-sample-rate") { + mi_heap_t* heap = mi_heap_new(); + mi_heap_guarded_set_sample_rate(heap, 100, 42); + void* p = mi_heap_malloc(heap, 64); + result = (p != NULL); + mi_free(p); + mi_heap_destroy(heap); + }; + + CHECK_BODY("heap-guarded-size-bound") { + mi_heap_t* heap = mi_heap_new(); + mi_heap_guarded_set_size_bound(heap, 16, 256); + void* p = mi_heap_malloc(heap, 64); + result = (p != NULL); + mi_free(p); + mi_heap_destroy(heap); + }; + + CHECK_BODY("stress-heap-alloc-delete") { + bool ok = true; + for (int round = 0; round < 10; round++) { + mi_heap_t* heap = mi_heap_new(); + ok = ok && (heap != NULL); + if (heap) { + for (int i = 0; i < 200; i++) { + void* p = mi_heap_malloc(heap, (size_t)(i + 1) * 4); + ok = ok && (p != NULL); + if (i % 3 == 0) mi_free(p); + } + if (round % 2 == 0) { + mi_heap_destroy(heap); + } else { + mi_heap_delete(heap); + } + } + } + mi_collect(true); + result = ok; + }; + + // ---------------------------------- + // Windows-specific tests (DeepTest) + // ---------------------------------- + #ifdef _WIN32 + CHECK_BODY("arena-reserve-exclusive") { + mi_arena_id_t arena_id = 0; + int res = mi_reserve_os_memory_ex(8 * 1024 * 1024, true, false, true, &arena_id); + if (res == 0) { + size_t sz = 0; + void* area = mi_arena_area(arena_id, &sz); + result = (area != NULL && sz >= 8 * 1024 * 1024); + } else { + result = true; + } + }; + + CHECK_BODY("arena-reserve-no-commit") { + mi_arena_id_t arena_id = 0; + int res = mi_reserve_os_memory_ex(4 * 1024 * 1024, false, false, false, &arena_id); + result = (res == 0); + }; + + CHECK_BODY("arena-reserve-exclusive-no-commit") { + mi_arena_id_t arena_id = 0; + int res = mi_reserve_os_memory_ex(4 * 1024 * 1024, false, false, true, &arena_id); + result = (res == 0); + }; + + CHECK_BODY("arena-heap-new-in-arena") { + mi_arena_id_t arena_id = 0; + int res = mi_reserve_os_memory_ex(128 * 1024 * 1024, false, false, false, &arena_id); + if (res == 0) { + mi_heap_t* heap = mi_heap_new_in_arena(arena_id); + if (heap != NULL) { + void* ptrs[100]; + bool ok = true; + for (int i = 0; i < 100; i++) { + ptrs[i] = mi_heap_malloc(heap, (size_t)(i + 1) * 16); + ok = ok && (ptrs[i] != NULL); + } + for (int i = 0; i < 100; i++) { + mi_free(ptrs[i]); + } + mi_heap_delete(heap); + result = ok; + } else { + result = true; + } + } else { + result = true; + } + }; + + CHECK_BODY("arena-heap-many-allocs") { + mi_arena_id_t arena_id = 0; + int res = mi_reserve_os_memory_ex(128 * 1024 * 1024, false, false, false, &arena_id); + if (res == 0) { + mi_heap_t* heap = mi_heap_new_in_arena(arena_id); + if (heap != NULL) { + void* ptrs[2000]; + bool ok = true; + for (int i = 0; i < 2000; i++) { + ptrs[i] = mi_heap_malloc(heap, 64); + ok = ok && (ptrs[i] != NULL); + } + for (int i = 1999; i >= 0; i--) { + mi_free(ptrs[i]); + } + mi_heap_collect(heap, true); + mi_heap_delete(heap); + result = ok; + } else { + result = true; + } + } else { + result = true; + } + }; + + CHECK_BODY("arena-manage-os-memory-ex") { + void* mem = VirtualAlloc(NULL, 4 * 1024 * 1024, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (mem != NULL) { + mi_arena_id_t arena_id = 0; + bool ok = mi_manage_os_memory_ex(mem, 4 * 1024 * 1024, true, false, true, -1, false, &arena_id); + if (ok) { + size_t sz = 0; + void* area = mi_arena_area(arena_id, &sz); + result = (area != NULL); + } else { + VirtualFree(mem, 0, MEM_RELEASE); + result = true; + } + } else { + result = true; + } + }; + + CHECK_BODY("arena-manage-exclusive") { + void* mem = VirtualAlloc(NULL, 128 * 1024 * 1024, MEM_RESERVE, PAGE_READWRITE); + if (mem != NULL) { + mi_arena_id_t arena_id = 0; + bool ok = mi_manage_os_memory_ex(mem, 128 * 1024 * 1024, false, false, false, -1, false, &arena_id); + if (ok) { + size_t sz = 0; + void* area = mi_arena_area(arena_id, &sz); + result = (area != NULL); + (void)area; + } else { + VirtualFree(mem, 0, MEM_RELEASE); + result = true; + } + } else { + result = true; + } + }; + + CHECK_BODY("arena-area-invalid-id") { + size_t sz = 0; + void* area = mi_arena_area(9999, &sz); + result = (area == NULL && sz == 0); + }; + + CHECK_BODY("arena-area-null-size") { + void* area = mi_arena_area(0, NULL); + result = true; + (void)area; + }; + + CHECK_BODY("mt-abandon-medium-reclaim") { + HANDLE threads[3]; + for (int i = 0; i < 3; i++) { + threads[i] = CreateThread(NULL, 0, dt2_thread_abandon_medium, NULL, 0, NULL); + } + WaitForMultipleObjects(3, threads, TRUE, 30000); + for (int i = 0; i < 3; i++) CloseHandle(threads[i]); + mi_collect(true); + for (int i = 0; i < 100; i++) { + void* p = mi_malloc(2048); + mi_free(p); + } + result = true; + }; + + CHECK_BODY("mt-cross-thread-free") { + dt2_cross_thread_data_t data; + memset(&data, 0, sizeof(data)); + HANDLE t = CreateThread(NULL, 0, dt2_thread_alloc_for_cross_free, &data, 0, NULL); + WaitForSingleObject(t, 30000); + CloseHandle(t); + if (data.ready) { + bool ok = true; + for (int i = 0; i < data.count; i++) { + ok = ok && (data.ptrs[i] != NULL); + mi_free(data.ptrs[i]); + } + result = ok; + } else { + result = true; + } + }; + + CHECK_BODY("mt-abandon-then-alloc-same-size") { + HANDLE t = CreateThread(NULL, 0, dt2_thread_abandon_allocs, NULL, 0, NULL); + WaitForSingleObject(t, 30000); + CloseHandle(t); + mi_collect(false); + void* ptrs[500]; + for (int i = 0; i < 500; i++) { + ptrs[i] = mi_malloc((size_t)(i % 20 + 1) * 16); + } + for (int i = 0; i < 500; i++) { + mi_free(ptrs[i]); + } + result = true; + }; + + CHECK_BODY("mt-reclaim-on-free") { + long old_reclaim = mi_option_get(mi_option_abandoned_reclaim_on_free); + mi_option_set(mi_option_abandoned_reclaim_on_free, 1); + dt2_reclaim_thread_data_t data; + memset(&data, 0, sizeof(data)); + HANDLE t = CreateThread(NULL, 0, dt2_thread_alloc_wait_reclaim, &data, 0, NULL); + while (InterlockedCompareExchange(&data.ready, 0, 0) == 0) { + Sleep(1); + } + InterlockedExchange(&data.can_exit, 1); + WaitForSingleObject(t, 30000); + CloseHandle(t); + Sleep(10); + for (int i = 0; i < data.count; i++) { + mi_free(data.ptrs[i]); + } + mi_option_set(mi_option_abandoned_reclaim_on_free, old_reclaim); + result = true; + }; + + CHECK_BODY("mt-many-threads-abandon") { + HANDLE threads[8]; + for (int i = 0; i < 8; i++) { + threads[i] = CreateThread(NULL, 0, dt2_thread_abandon_allocs, NULL, 0, NULL); + } + WaitForMultipleObjects(8, threads, TRUE, 30000); + for (int i = 0; i < 8; i++) CloseHandle(threads[i]); + mi_collect(true); + result = true; + }; + + CHECK_BODY("mt-aligned-threads") { + HANDLE threads[4]; + for (int i = 0; i < 4; i++) { + threads[i] = CreateThread(NULL, 0, dt2_thread_aligned_allocs, NULL, 0, NULL); + } + WaitForMultipleObjects(4, threads, TRUE, 30000); + for (int i = 0; i < 4; i++) CloseHandle(threads[i]); + result = true; + }; + + CHECK_BODY("mt-threadpool-mode") { + HANDLE threads[4]; + for (int i = 0; i < 4; i++) { + threads[i] = CreateThread(NULL, 0, dt2_thread_threadpool_mode, NULL, 0, NULL); + } + WaitForMultipleObjects(4, threads, TRUE, 30000); + for (int i = 0; i < 4; i++) CloseHandle(threads[i]); + result = true; + }; + + CHECK_BODY("arena-threaded-alloc-abandon") { + dt2_arena_thread_data_t data; + data.arena_id = 0; + data.done = 0; + HANDLE t = CreateThread(NULL, 0, dt2_thread_arena_alloc, &data, 0, NULL); + WaitForSingleObject(t, 30000); + CloseHandle(t); + mi_collect(true); + result = true; + }; + + CHECK_BODY("abandoned-visit-after-threads") { + HANDLE t = CreateThread(NULL, 0, dt2_thread_abandon_allocs, NULL, 0, NULL); + WaitForSingleObject(t, 30000); + CloseHandle(t); + mi_subproc_id_t sp = mi_subproc_main(); + long old_visit = mi_option_get(mi_option_visit_abandoned); + mi_option_set(mi_option_visit_abandoned, 1); + dt2_visit_count = 0; + bool ok = mi_abandoned_visit_blocks(sp, 0, true, dt2_block_visitor_fn, NULL); + result = true; + (void)ok; + mi_option_set(mi_option_visit_abandoned, old_visit); + }; + + CHECK_BODY("page-heap-set-default") { + mi_heap_t* heap = mi_heap_new(); + mi_heap_t* old = mi_heap_set_default(heap); + void* p = mi_malloc(128); + result = (p != NULL); + mi_free(p); + mi_heap_set_default(old); + mi_heap_destroy(heap); + }; + + CHECK_BODY("free-size-small") { + void* p = mi_malloc(16); + mi_free_size(p, 16); + result = true; + }; + + CHECK_BODY("free-size-medium") { + void* p = mi_malloc(512); + mi_free_size(p, 512); + result = true; + }; + + CHECK_BODY("free-size-large") { + void* p = mi_malloc(1024 * 1024); + mi_free_size(p, 1024 * 1024); + result = true; + }; + + CHECK_BODY("free-size-null2") { + mi_free_size(NULL, 0); + result = true; + }; + + CHECK_BODY("free-aligned-small") { + void* p = mi_malloc_aligned(64, 16); + mi_free_aligned(p, 16); + result = true; + }; + + CHECK_BODY("free-aligned-large-align") { + void* p = mi_malloc_aligned(256, 256); + mi_free_aligned(p, 256); + result = true; + }; + + CHECK_BODY("free-aligned-null2") { + mi_free_aligned(NULL, 16); + result = true; + }; + + CHECK_BODY("free-size-aligned2") { + void* p = mi_malloc_aligned(128, 64); + mi_free_size_aligned(p, 128, 64); + result = true; + }; + + CHECK_BODY("free-size-aligned-null2") { + mi_free_size_aligned(NULL, 0, 16); + result = true; + }; + + CHECK_BODY("free-size-aligned-various") { + bool ok = true; + size_t aligns[] = {8, 16, 32, 64, 128, 256, 512, 1024, 0}; + for (int a = 0; aligns[a] != 0; a++) { + size_t align = aligns[a]; + void* p = mi_malloc_aligned(align * 2, align); + ok = ok && (p != NULL); + mi_free_size_aligned(p, align * 2, align); + } + result = ok; + }; + + CHECK_BODY("options-print2") { + mi_options_print(); + result = true; + }; + + CHECK_BODY("option-get-size2") { + size_t sz1 = mi_option_get_size(mi_option_reserve_os_memory); + size_t sz2 = mi_option_get_size(mi_option_arena_reserve); + result = true; + (void)sz1; (void)sz2; + }; + + CHECK_BODY("option-set-default2") { + mi_option_set_default(mi_option_purge_delay, 20); + mi_option_set_default(mi_option_arena_purge_mult, 15); + result = true; + mi_option_set(mi_option_purge_delay, 10); + mi_option_set(mi_option_arena_purge_mult, 10); + }; + + CHECK_BODY("option-set-enabled-default2") { + mi_option_set_enabled_default(mi_option_eager_commit, true); + mi_option_set_enabled_default(mi_option_purge_decommits, true); + result = true; + }; + + CHECK_BODY("register-output2") { + dt2_output_count = 0; + mi_register_output(dt2_output_fn, NULL); + mi_stats_print_out(dt2_output_fn, NULL); + result = (dt2_output_count > 0); + mi_register_output(NULL, NULL); + }; + + CHECK_BODY("register-error2") { + mi_register_error(dt2_error_fn, NULL); + dt2_error_count = 0; + void* p = mi_malloc((size_t)PTRDIFF_MAX + 1); + result = true; + mi_free(p); + mi_register_error(NULL, NULL); + }; + + CHECK_BODY("stats-print-out-callback") { + dt2_output_count = 0; + mi_stats_print_out(dt2_output_fn, NULL); + result = (dt2_output_count > 0); + }; + + CHECK_BODY("thread-stats-print-callback") { + dt2_output_count = 0; + mi_thread_stats_print_out(dt2_output_fn, NULL); + result = (dt2_output_count > 0); + }; + + CHECK_BODY("option-verbose-trigger") { + mi_option_set(mi_option_verbose, 2); + void* p = mi_malloc(1024); + mi_free(p); + mi_collect(false); + mi_option_set(mi_option_verbose, 0); + result = true; + }; + + CHECK_BODY("option-purge-delay-zero") { + long old_delay = mi_option_get(mi_option_purge_delay); + long old_mult = mi_option_get(mi_option_arena_purge_mult); + mi_option_set(mi_option_purge_delay, 1); + mi_option_set(mi_option_arena_purge_mult, 1); + void* ptrs[50]; + for (int i = 0; i < 50; i++) { + ptrs[i] = mi_malloc(4096); + } + for (int i = 0; i < 50; i++) { + mi_free(ptrs[i]); + } + Sleep(5); + mi_collect(true); + mi_option_set(mi_option_purge_delay, old_delay); + mi_option_set(mi_option_arena_purge_mult, old_mult); + result = true; + }; + + CHECK_BODY("option-purge-delay-negative") { + long old = mi_option_get(mi_option_purge_delay); + mi_option_set(mi_option_purge_delay, -1); + void* p = mi_malloc(4096); + mi_free(p); + mi_collect(true); + mi_option_set(mi_option_purge_delay, old); + result = true; + }; + + CHECK_BODY("huge-free-size") { + void* p = mi_malloc(32 * 1024 * 1024); + mi_free_size(p, 32 * 1024 * 1024); + result = true; + }; + + CHECK_BODY("segment-heap-new-ex") { + mi_heap_t* h1 = mi_heap_new_ex(1, true, 0); + mi_heap_t* h2 = mi_heap_new_ex(2, false, 0); + bool ok = (h1 != NULL && h2 != NULL); + if (h1) { + void* p = mi_heap_malloc(h1, 128); + mi_free(p); + mi_heap_destroy(h1); + } + if (h2) { + void* p = mi_heap_malloc(h2, 256); + mi_free(p); + mi_heap_delete(h2); + } + result = ok; + }; + + CHECK_BODY("bitmap-many-arena-allocs") { + mi_arena_id_t arena_id = 0; + int res = mi_reserve_os_memory_ex(128 * 1024 * 1024, false, false, false, &arena_id); + if (res == 0) { + mi_heap_t* heap = mi_heap_new_in_arena(arena_id); + if (heap != NULL) { + void* ptrs[4000]; + bool ok = true; + for (int i = 0; i < 4000; i++) { + ptrs[i] = mi_heap_malloc(heap, (size_t)((i % 16) + 1) * 8); + ok = ok && (ptrs[i] != NULL); + } + for (int i = 0; i < 4000; i += 3) { + mi_free(ptrs[i]); + ptrs[i] = NULL; + } + for (int i = 0; i < 4000; i += 3) { + ptrs[i] = mi_heap_malloc(heap, 32); + } + for (int i = 0; i < 4000; i++) { + if (ptrs[i]) mi_free(ptrs[i]); + } + mi_heap_collect(heap, true); + mi_heap_delete(heap); + result = ok; + } else { + result = true; + } + } else { + result = true; + } + }; + + CHECK_BODY("is-redirected2") { + bool r = mi_is_redirected(); + result = true; + (void)r; + }; + + CHECK_BODY("heap-strdup2") { + mi_heap_t* heap = mi_heap_new(); + char* s = mi_heap_strdup(heap, "hello"); + result = (s != NULL && strcmp(s, "hello") == 0); + mi_free(s); + mi_heap_destroy(heap); + }; + + CHECK_BODY("heap-reallocf2") { + mi_heap_t* heap = mi_heap_new(); + void* p = mi_heap_malloc(heap, 64); + void* q = mi_heap_reallocf(heap, p, 256); + result = (q != NULL); + mi_free(q); + mi_heap_destroy(heap); + }; + + CHECK_BODY("arena-multi-thread-abandon") { + dt2_arena_thread_data_t data[4]; + HANDLE threads[4]; + for (int i = 0; i < 4; i++) { + data[i].arena_id = 0; + data[i].done = 0; + threads[i] = CreateThread(NULL, 0, dt2_thread_arena_alloc, &data[i], 0, NULL); + } + WaitForMultipleObjects(4, threads, TRUE, 30000); + for (int i = 0; i < 4; i++) CloseHandle(threads[i]); + mi_collect(true); + for (int i = 0; i < 200; i++) { + void* p = mi_malloc(128); + mi_free(p); + } + result = true; + }; + + CHECK_BODY("stats-reset-merge-cycle") { + mi_stats_reset(); + void* ptrs[100]; + for (int i = 0; i < 100; i++) { + ptrs[i] = mi_malloc(256); + } + for (int i = 0; i < 100; i++) { + mi_free(ptrs[i]); + } + mi_stats_merge(); + mi_stats_print_out(dt2_output_fn, NULL); + mi_stats_reset(); + result = true; + }; + + CHECK_BODY("heap-new-ex-with-arena") { + mi_arena_id_t arena_id = 0; + int res = mi_reserve_os_memory_ex(128 * 1024 * 1024, false, false, false, &arena_id); + if (res == 0) { + mi_heap_t* heap = mi_heap_new_ex(5, true, arena_id); + if (heap) { + void* p = mi_heap_malloc(heap, 256); + result = (p != NULL); + mi_free(p); + mi_heap_destroy(heap); + } else { + result = true; + } + } else { + result = true; + } + }; + + CHECK_BODY("debug-show-arenas-after-allocs") { + void* p = mi_malloc(64 * 1024 * 1024); + mi_debug_show_arenas(); + mi_arenas_print(); + mi_free(p); + result = true; + }; + + CHECK_BODY("double-free-detection") { + dt2_double_free_error_count = 0; + mi_register_error(dt2_double_free_error_handler, NULL); + void* p = mi_malloc(64); + mi_free(p); + mi_free(p); + result = (dt2_double_free_error_count >= 1); + mi_register_error(NULL, NULL); + }; + #endif + + // --------------------------------------------------- + // Done + // --------------------------------------------------- + return print_test_summary(); }