初始化内容

This commit is contained in:
2026-09-16 14:07:40 +08:00
parent 6934b390bf
commit 4f643c1e7c
18350 changed files with 6088489 additions and 305 deletions
+56
View File
@@ -0,0 +1,56 @@
cmake_minimum_required(VERSION 3.18)
project(mimalloc-test C CXX)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
# Set default build type
if (NOT CMAKE_BUILD_TYPE)
if ("${CMAKE_BINARY_DIR}" MATCHES ".*(D|d)ebug$")
message(STATUS "No build type selected, default to *** Debug ***")
set(CMAKE_BUILD_TYPE "Debug")
else()
message(STATUS "No build type selected, default to *** Release ***")
set(CMAKE_BUILD_TYPE "Release")
endif()
endif()
# Import mimalloc (if installed)
find_package(mimalloc CONFIG REQUIRED)
message(STATUS "Found mimalloc installed at: ${MIMALLOC_LIBRARY_DIR} (${MIMALLOC_VERSION_DIR})")
# link with a dynamic shared library
# use `LD_PRELOAD` to actually override malloc/free at runtime with mimalloc
add_executable(dynamic-override main-override.c)
target_link_libraries(dynamic-override PUBLIC mimalloc)
add_executable(dynamic-override-cxx main-override.cpp)
target_link_libraries(dynamic-override-cxx PUBLIC mimalloc)
# overriding with a static object file works reliable as the symbols in the
# object file have priority over those in library files
add_executable(static-override-obj main-override.c ${MIMALLOC_OBJECT_DIR}/mimalloc${CMAKE_C_OUTPUT_EXTENSION})
target_include_directories(static-override-obj PUBLIC ${MIMALLOC_INCLUDE_DIR})
target_link_libraries(static-override-obj PUBLIC mimalloc-static)
# overriding with a static library works too if using the `mimalloc-override.h`
# header to redefine malloc/free. (the library already overrides new/delete)
add_executable(static-override-static main-override-static.c)
target_link_libraries(static-override-static PUBLIC mimalloc-static)
# overriding with a static library: this may not work if the library is linked too late
# on the command line after the C runtime library; but we cannot control that well in CMake
add_executable(static-override main-override.c)
target_link_libraries(static-override PUBLIC mimalloc-static)
add_executable(static-override-cxx main-override.cpp)
target_link_libraries(static-override-cxx PUBLIC mimalloc-static)
## test memory errors
add_executable(test-wrong test-wrong.c)
target_link_libraries(test-wrong PUBLIC mimalloc)
+60
View File
@@ -0,0 +1,60 @@
// Issue #981: test overriding allocation in a DLL that is compiled independent of mimalloc.
// This is imported by the `mimalloc-test-override` project.
#include <string>
#include <iostream>
#include "main-override-dep.h"
std::string TestAllocInDll::GetString()
{
char* test = new char[128];
memset(test, 0, 128);
const char* t = "test";
memcpy(test, t, 4);
std::string r = test;
std::cout << "override-dep: GetString: " << r << "\n";
delete[] test;
return r;
}
#include <windows.h>
void TestAllocInDll::TestHeapAlloc()
{
HANDLE theap = GetProcessHeap();
int* p = (int*)HeapAlloc(theap, 0, sizeof(int));
*p = 42;
HeapFree(theap, 0, p);
}
class Static {
private:
void* p;
public:
Static() {
printf("override-dep: static constructor\n");
p = malloc(64);
return;
}
~Static() {
free(p);
printf("override-dep: static destructor\n");
return;
}
};
static Static s = Static();
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID reserved) {
(void)(reserved);
(void)(module);
if (reason==DLL_PROCESS_ATTACH) {
printf("override-dep: dll attach\n");
}
else if (reason==DLL_PROCESS_DETACH) {
printf("override-dep: dll detach\n");
}
return TRUE;
}
+12
View File
@@ -0,0 +1,12 @@
#pragma once
// Issue #981: test overriding allocation in a DLL that is compiled independent of mimalloc.
// This is imported by the `mimalloc-test-override` project.
#include <string>
class TestAllocInDll
{
public:
__declspec(dllexport) std::string GetString();
__declspec(dllexport) void TestHeapAlloc();
};
+553
View File
@@ -0,0 +1,553 @@
#if _WIN32
#include <windows.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <mimalloc.h>
#include <mimalloc-override.h> // redefines malloc etc.
static void mi_bins(void);
static void double_free1();
static void double_free2();
static void corrupt_free();
static void block_overflow1();
static void block_overflow2();
static void invalid_free();
static void test_aslr(void);
static void test_process_info(void);
static void test_reserved(void);
static void negative_stat(void);
static void alloc_huge(void);
static void test_heap_walk(void);
static void test_canary_leak(void);
static void test_manage_os_memory(void);
// static void test_large_pages(void);
#if _WIN32
#include "main-static-dep.h"
static void test_dep(); // test static mimalloc in a separate DLL
#else
static void test_dep() {};
#endif
int main() {
mi_version();
mi_stats_reset();
test_dep();
// mi_bins();
// test_manage_os_memory();
// test_large_pages();
// detect double frees and theap corruption
// double_free1();
// double_free2();
// corrupt_free();
// block_overflow1();
// block_overflow2();
test_canary_leak();
// test_aslr();
// invalid_free();
// test_reserved();
// negative_stat();
// test_theap_walk();
// alloc_huge();
void* p1 = malloc(78);
void* p2 = malloc(24);
free(p1);
p1 = mi_malloc(8);
char* s = strdup("hello\n");
free(p2);
// mi_theap_t* h = mi_theap_new();
// mi_theap_set_default(h);
p2 = malloc(16);
p1 = realloc(p1, 32);
free(p1);
free(p2);
free(s);
/* now test if override worked by allocating/freeing across the api's*/
//p1 = mi_malloc(32);
//free(p1);
//p2 = malloc(32);
//mi_free(p2);
//mi_collect(true);
//mi_stats_print(NULL);
// test_process_info();
return 0;
}
static void invalid_free() {
free((void*)0xBADBEEF);
realloc((void*)0xBADBEEF, 10);
}
static void block_overflow1() {
uint8_t* p = (uint8_t*)mi_malloc(17);
p[18] = 0;
free(p);
}
static void block_overflow2() {
uint8_t* p = (uint8_t*)mi_malloc(16);
p[17] = 0;
free(p);
}
// The double free samples come ArcHeap [1] by Insu Yun (issue #161)
// [1]: https://arxiv.org/pdf/1903.00503.pdf
static void double_free1() {
void* p[256];
//uintptr_t buf[256];
p[0] = mi_malloc(622616);
p[1] = mi_malloc(655362);
p[2] = mi_malloc(786432);
mi_free(p[2]);
// [VULN] Double free
mi_free(p[2]);
p[3] = mi_malloc(786456);
// [BUG] Found overlap
// p[3]=0x429b2ea2000 (size=917504), p[1]=0x429b2e42000 (size=786432)
fprintf(stderr, "p3: %p-%p, p1: %p-%p, p2: %p\n", p[3], (uint8_t*)(p[3]) + 786456, p[1], (uint8_t*)(p[1]) + 655362, p[2]);
}
static void double_free2() {
void* p[256];
//uintptr_t buf[256];
// [INFO] Command buffer: 0x327b2000
// [INFO] Input size: 182
p[0] = malloc(712352);
p[1] = malloc(786432);
free(p[0]);
// [VULN] Double free
free(p[0]);
p[2] = malloc(786440);
p[3] = malloc(917504);
p[4] = malloc(786440);
// [BUG] Found overlap
// p[4]=0x433f1402000 (size=917504), p[1]=0x433f14c2000 (size=786432)
fprintf(stderr, "p1: %p-%p, p2: %p-%p\n", p[4], (uint8_t*)(p[4]) + 917504, p[1], (uint8_t*)(p[1]) + 786432);
}
// Try to corrupt the theap through buffer overflow
#define N 256
#define SZ 64
static void corrupt_free() {
void* p[N];
// allocate
for (int i = 0; i < N; i++) {
p[i] = malloc(SZ);
}
// free some
for (int i = 0; i < N; i += (N/10)) {
free(p[i]);
p[i] = NULL;
}
// try to corrupt the free list
for (int i = 0; i < N; i++) {
if (p[i] != NULL) {
memset(p[i], 0, SZ+8);
}
}
// allocate more.. trying to trigger an allocation from a corrupted entry
// this may need many allocations to get there (if at all)
for (int i = 0; i < 4096; i++) {
malloc(SZ);
}
}
static void test_aslr(void) {
void* p[256];
p[0] = malloc(378200);
p[1] = malloc(1134626);
printf("p1: %p, p2: %p\n", p[0], p[1]);
}
static void test_process_info(void) {
size_t elapsed = 0;
size_t user_msecs = 0;
size_t system_msecs = 0;
size_t current_rss = 0;
size_t peak_rss = 0;
size_t current_commit = 0;
size_t peak_commit = 0;
size_t page_faults = 0;
for (int i = 0; i < 100000; i++) {
void* p = calloc(100, 10);
free(p);
}
mi_process_info(&elapsed, &user_msecs, &system_msecs, &current_rss, &peak_rss, &current_commit, &peak_commit, &page_faults);
printf("\n\n*** process info: elapsed %3zd.%03zd s, user: %3zd.%03zd s, rss: %zd b, commit: %zd b\n\n", elapsed/1000, elapsed%1000, user_msecs/1000, user_msecs%1000, peak_rss, peak_commit);
}
static void test_reserved(void) {
#define KiB 1024UL
#define MiB (KiB*KiB)
#define GiB (MiB*KiB)
mi_reserve_os_memory(3500*MiB, false, true);
void* p1 = malloc(100);
void* p2 = malloc(100000);
void* p3 = malloc(2*GiB);
void* p4 = malloc(1*GiB + 100000);
free(p1);
free(p2);
free(p3);
p3 = malloc(1*GiB);
free(p4);
}
static void negative_stat(void) {
int* p = mi_malloc(60000);
mi_stats_print_out(NULL, NULL);
*p = 100;
mi_free(p);
mi_stats_print_out(NULL, NULL);
}
static void alloc_huge(void) {
void* p = mi_malloc(67108872);
mi_free(p);
}
static bool test_visit(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg) {
if (block == NULL) {
printf("visiting an area with blocks of size %zu (including padding)\n", area->full_block_size);
}
else {
printf(" block of size %zu (allocated size is %zu)\n", block_size, mi_usable_size(block));
}
return true;
}
static void test_heap_walk(void) {
mi_heap_t* heap = mi_heap_new();
mi_heap_malloc(heap, 16*2097152);
mi_heap_malloc(heap, 2067152);
mi_heap_malloc(heap, 2097160);
mi_heap_malloc(heap, 24576);
mi_heap_visit_blocks(heap, true, &test_visit, NULL);
}
static void test_canary_leak(void) {
char* p = mi_mallocn_tp(char, 22);
for (int i = 0; i < 22; i++) {
p[i] = '0'+i;
}
puts(p);
free(p);
}
#if _WIN32
static void test_manage_os_memory(void) {
size_t size = 256 * 1024 * 1024;
void* ptr = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
mi_arena_id_t arena_id;
mi_manage_os_memory_ex(ptr, size, true /* committed */, true /* pinned */, false /* is zero */, -1 /* numa node */, true /* exclusive */, &arena_id);
mi_heap_t* cuda_theap = mi_heap_new_in_arena(arena_id); // you can do this in any thread
// now allocate only in the cuda arena
void* p1 = mi_heap_malloc(cuda_theap, 8);
int* p2 = mi_heap_malloc_tp(int,cuda_theap);
*p2 = 42;
// and maybe set the cuda theap as the default theap? (but careful as now `malloc` will allocate in the cuda theap as well)
{
mi_theap_t* prev_default_theap = mi_theap_set_default(mi_heap_theap(cuda_theap));
void* p3 = mi_malloc(8); // allocate in the cuda theap
mi_free(p3);
}
mi_free(p1);
mi_free(p2);
}
#else
static void test_manage_os_memory(void) {
// empty
}
#endif
#if _WIN32
static void call_library(void) {
HMODULE dll = LoadLibraryA("mimalloc-test-static-dep.dll");
if (dll != NULL) {
TestFun fun = (TestFun)GetProcAddress(dll, "Test");
if (fun != NULL) {
fun();
}
bool ok = FreeLibrary(dll);
if (!ok) printf("unable to free library: %i\n", ok);
}
}
static void test_dep(void) {
call_library();
call_library();
}
#endif
// Experiment with huge OS pages
#if 0
#include <mimalloc/types.h>
#include <mimalloc/internal.h>
#include <unistd.h>
#include <sys/mman.h>
static void test_large_pages(void) {
mi_memid_t memid;
#if 0
size_t pages_reserved;
size_t page_size;
uint8_t* p = (uint8_t*)_mi_os_alloc_huge_os_pages(1, -1, 30000, &pages_reserved, &page_size, &memid);
const size_t req_size = pages_reserved * page_size;
#else
const size_t req_size = 64*MI_MiB;
uint8_t* p = (uint8_t*)_mi_os_alloc(req_size, &memid, NULL);
#endif
p[0] = 1;
//_mi_os_protect(p, _mi_os_page_size());
//_mi_os_unprotect(p, _mi_os_page_size());
//_mi_os_decommit(p, _mi_os_page_size(), NULL);
if (madvise(p, req_size, MADV_HUGEPAGE) == 0) {
printf("advised huge pages\n");
_mi_os_decommit(p, _mi_os_page_size(), NULL);
};
_mi_os_free(p, req_size, memid, NULL);
}
#endif
// ----------------------------
// bin size experiments
// ------------------------------
#if 0
#include <stdint.h>
#include <stdbool.h>
#include <mimalloc/bits.h>
#define MI_LARGE_WSIZE_MAX (4*1024*1024 / MI_INTPTR_SIZE)
#define MI_BIN_HUGE 100
//#define MI_ALIGN2W
// Bit scan reverse: return the index of the highest bit.
static inline uint8_t mi_bsr32(uint32_t x);
#if defined(_MSC_VER)
//#include <Windows.h>
#include <intrin.h>
static inline uint8_t mi_bsr32(uint32_t x) {
uint32_t idx;
_BitScanReverse(&idx, x);
return idx;
}
#elif defined(__GNUC__) || defined(__clang__)
static inline uint8_t mi_bsr32(uint32_t x) {
return (31 - __builtin_clz(x));
}
#else
static inline uint8_t mi_bsr32(uint32_t x) {
// de Bruijn multiplication, see <http://supertech.csail.mit.edu/papers/debruijn.pdf>
static const uint8_t debruijn[32] = {
31, 0, 22, 1, 28, 23, 18, 2, 29, 26, 24, 10, 19, 7, 3, 12,
30, 21, 27, 17, 25, 9, 6, 11, 20, 16, 8, 5, 15, 4, 14, 13,
};
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
x++;
return debruijn[(x*0x076be629) >> 27];
}
#endif
// Bit scan reverse: return the index of the highest bit.
uint8_t _mi_bsr(uintptr_t x) {
if (x == 0) return 0;
#if MI_INTPTR_SIZE==8
uint32_t hi = (x >> 32);
return (hi == 0 ? mi_bsr32((uint32_t)x) : 32 + mi_bsr32(hi));
#elif MI_INTPTR_SIZE==4
return mi_bsr32(x);
#else
# error "define bsr for non-32 or 64-bit platforms"
#endif
}
static inline size_t _mi_wsize_from_size(size_t size) {
return (size + sizeof(uintptr_t) - 1) / sizeof(uintptr_t);
}
// #define MI_ALIGN2W
// Return the bin for a given field size.
// Returns MI_BIN_HUGE if the size is too large.
// We use `wsize` for the size in "machine word sizes",
// i.e. byte size == `wsize*sizeof(void*)`.
static inline size_t mi_bin(size_t wsize) {
// size_t wsize = _mi_wsize_from_size(size);
// size_t bin;
/*if (wsize <= 1) {
bin = 1;
}
*/
#if defined(MI_ALIGN4W)
if (wsize <= 4) {
return (wsize <= 1 ? 1 : (wsize+1)&~1); // round to double word sizes
}
#elif defined(MI_ALIGN2W)
if (wsize <= 8) {
return (wsize <= 1 ? 1 : (wsize+1)&~1); // round to double word sizes
}
#else
if (wsize <= 8) {
return (wsize == 0 ? 1 : wsize);
}
#endif
else if (wsize > MI_LARGE_WSIZE_MAX) {
return MI_BIN_HUGE;
}
else {
#if defined(MI_ALIGN4W)
if (wsize <= 16) { wsize = (wsize+3)&~3; } // round to 4x word sizes
#endif
wsize--;
// find the highest bit
size_t idx;
mi_bsr(wsize, &idx);
uint8_t b = (uint8_t)idx;
// and use the top 3 bits to determine the bin (~12.5% worst internal fragmentation).
// - adjust with 3 because we use do not round the first 8 sizes
// which each get an exact bin
const size_t bin = ((b << 2) + ((wsize >> (b - 2)) & 0x03)) - 3;
assert(bin > 0 && bin < MI_BIN_HUGE);
return bin;
}
}
static inline uint8_t _mi_bin4(size_t size) {
size_t wsize = _mi_wsize_from_size(size);
uint8_t bin;
if (wsize <= 1) {
bin = 1;
}
#if defined(MI_ALIGN4W)
else if (wsize <= 4) {
bin = (uint8_t)((wsize+1)&~1); // round to double word sizes
}
#elif defined(MI_ALIGN2W)
else if (wsize <= 8) {
bin = (uint8_t)((wsize+1)&~1); // round to double word sizes
}
#else
else if (wsize <= 8) {
bin = (uint8_t)wsize;
}
#endif
else if (wsize > MI_LARGE_WSIZE_MAX) {
bin = MI_BIN_HUGE;
}
else {
size_t idx;
mi_bsr(wsize, &idx);
uint8_t b = (uint8_t)idx;
bin = ((b << 1) + (uint8_t)((wsize >> (b - 1)) & 0x01)) + 3;
}
return bin;
}
static size_t _mi_binx4(size_t wsize) {
size_t bin;
if (wsize <= 1) {
bin = 1;
}
else if (wsize <= 8) {
// bin = (wsize+1)&~1; // round to double word sizes
bin = (uint8_t)wsize;
}
else {
size_t idx;
mi_bsr(wsize, &idx);
uint8_t b = (uint8_t)idx;
if (b <= 1) return wsize;
bin = ((b << 1) | (wsize >> (b - 1))&0x01) + 3;
}
return bin;
}
static size_t _mi_binx8(size_t bsize) {
if (bsize<=1) return bsize;
size_t idx;
mi_bsr(bsize, &idx);
uint8_t b = (uint8_t)idx;
if (b <= 2) return bsize;
size_t bin = ((b << 2) | (bsize >> (b - 2))&0x03) - 5;
return bin;
}
static inline size_t mi_binx(size_t wsize) {
uint8_t bin;
if (wsize <= 1) {
bin = 1;
}
else if (wsize <= 8) {
// bin = (wsize+1)&~1; // round to double word sizes
bin = (uint8_t)wsize;
}
else {
wsize--;
assert(wsize>0);
// find the highest bit
uint8_t b = (uint8_t)(MI_SIZE_BITS - 1 - mi_clz(wsize));
// and use the top 3 bits to determine the bin (~12.5% worst internal fragmentation).
// - adjust with 3 because we use do not round the first 8 sizes
// which each get an exact bin
bin = ((b << 2) + (uint8_t)((wsize >> (b - 2)) & 0x03)) - 3;
}
return bin;
}
static void mi_bins(void) {
//printf(" QNULL(1), /* 0 */ \\\n ");
size_t last_bin = 0;
for (size_t wsize = 1; wsize <= (4*1024*1024) / 8 + 1024; wsize++) {
size_t bin = mi_bin(wsize);
if (bin != last_bin) {
//printf("min bsize: %6zd, max bsize: %6zd, bin: %6zd\n", min_wsize, last_wsize, last_bin);
printf("QNULL(%6zd), ", wsize-1);
if (last_bin%8 == 0) printf("/* %zu */ \\\n ", last_bin);
last_bin = bin;
}
}
}
#endif
+49
View File
@@ -0,0 +1,49 @@
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <mimalloc.h>
// #include <mimalloc-override.h>
int main() {
mi_version(); // ensure mimalloc library is linked
void* p1 = malloc(78);
_expand(p1, 100);
if (!mi_is_in_heap_region(p1)) {
printf("p1: malloc failed to allocate in heap region\n");
return 1;
}
void* p2 = malloc(24);
if (!mi_is_in_heap_region(p2)) {
printf("p2: malloc failed to allocate in heap region\n");
return 1;
}
free(p1);
p1 = malloc(8);
char* s = strdup("hello\n");
free(p2);
p2 = malloc(16);
void* p3 = realloc(p1, 32); if (p3!=NULL) { p1 = p3; }
free(p1);
free(p2);
free(s);
//mi_collect(true);
/* now test if override worked by allocating/freeing across the api's*/
p1 = mi_malloc(32);
free(p1);
p2 = malloc(32);
mi_free(p2);
//p1 = malloc(24);
//p2 = reallocarray(p1, 16, 16);
//free(p2);
//p1 = malloc(24);
//assert(reallocarr(&p1, 16, 16) == 0);
//free(p1);
mi_stats_print(NULL);
return 0;
}
+635
View File
@@ -0,0 +1,635 @@
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <mimalloc.h>
#include <new>
#include <vector>
#include <future>
#include <iostream>
#include <thread>
#include <random>
#include <chrono>
#include <assert.h>
#ifdef _WIN32
#include <mimalloc-new-delete.h>
#include <windows.h>
static void msleep(unsigned long msecs) { Sleep(msecs); }
#else
#include <unistd.h>
static void msleep(unsigned long msecs) { usleep(msecs * 1000UL); }
#endif
static void theap_thread_free_large(); // issue #221
static void theap_no_delete(); // issue #202
static void theap_late_free(); // issue #204
static void padding_shrink(); // issue #209
static void various_tests();
static void test_mt_shutdown();
static void fail_aslr(); // issue #372
static void tsan_numa_test(); // issue #414
static void strdup_test(); // issue #445
static void theap_thread_free_huge();
static void test_std_string(); // issue #697
static void test_thread_local(); // issue #944
// static void test_mixed0(); // issue #942
static void test_mixed1(); // issue #942
static void test_stl_allocators();
static void test_join(); // issue #1177
static void test_thread_leak(void); // issue #1104
static void test_perf(void); // issue #1104
static void test_perf2(void); // issue #1104
static void test_perf3(void); // issue #1104
static void test_perf4(void); // issue #1104
static void test_perf5(void); // issue #1104
#if _WIN32
#include "main-override-dep.h"
static void test_dep(); // issue #981: test overriding in another DLL
#else
static void test_dep() { };
#endif
int main() {
mi_stats_reset(); // ignore earlier allocations
//various_tests();
//test_mixed1();
// test_dep();
// test_join();
// test_thread_leak();
// test_perf();
// test_perf2();
// test_perf3();
// test_perf4();
test_perf5();
//test_std_string();
//test_thread_local();
// theap_thread_free_huge();
/*
theap_thread_free_large();
theap_no_delete();
theap_late_free();
padding_shrink();
tsan_numa_test();
*/
/*
strdup_test();
test_stl_allocators();
test_mt_shutdown();
*/
//fail_aslr();
mi_stats_print(NULL);
return 0;
}
static void* p = malloc(8);
void free_p() {
free(p);
return;
}
class Test {
private:
int i;
public:
Test(int x) { i = x; }
~Test() { }
};
static void various_tests() {
atexit(free_p);
void* p1 = malloc(78);
void* p2 = mi_malloc_aligned(24, 16);
free(p1);
p1 = malloc(8);
char* s = mi_strdup("hello\n");
mi_free(p2);
p2 = malloc(16);
p1 = realloc(p1, 32);
free(p1);
free(p2);
mi_free(s);
Test* t = new Test(42);
delete t;
t = new (std::nothrow) Test(42);
delete t;
auto tbuf = new unsigned char[sizeof(Test)];
t = new (tbuf) Test(42);
t->~Test();
delete[] tbuf;
#if _WIN32
const char* ptr = ::_Getdays(); // test _base overrid
free((void*)ptr);
#endif
}
class Static {
private:
void* p;
public:
Static() {
p = malloc(64);
return;
}
~Static() {
free(p);
return;
}
};
static Static s = Static();
static bool test_stl_allocator1() {
std::vector<int, mi_stl_allocator<int> > vec;
vec.push_back(1);
vec.pop_back();
return vec.size() == 0;
}
struct some_struct { int i; int j; double z; };
#if _WIN32
static void test_dep()
{
TestAllocInDll t;
std::string s = t.GetString();
std::cout << "test_dep GetString: " << s << "\n";
t.TestHeapAlloc();
}
#endif
static bool test_stl_allocator2() {
std::vector<some_struct, mi_stl_allocator<some_struct> > vec;
vec.push_back(some_struct());
vec.pop_back();
return vec.size() == 0;
}
#if MI_HAS_HEAP_STL_ALLOCATOR
static bool test_stl_allocator3() {
std::vector<int, mi_heap_stl_allocator<int> > vec;
vec.push_back(1);
vec.pop_back();
return vec.size() == 0;
}
static bool test_stl_allocator4() {
std::vector<some_struct, mi_heap_stl_allocator<some_struct> > vec;
vec.push_back(some_struct());
vec.pop_back();
return vec.size() == 0;
}
static bool test_stl_allocator5() {
std::vector<int, mi_heap_destroy_stl_allocator<int> > vec;
vec.push_back(1);
vec.pop_back();
return vec.size() == 0;
}
static bool test_stl_allocator6() {
std::vector<some_struct, mi_heap_destroy_stl_allocator<some_struct> > vec;
vec.push_back(some_struct());
vec.pop_back();
return vec.size() == 0;
}
#endif
static void test_stl_allocators() {
test_stl_allocator1();
test_stl_allocator2();
#if MI_HAS_HEAP_STL_ALLOCATOR
test_stl_allocator3();
test_stl_allocator4();
test_stl_allocator5();
test_stl_allocator6();
#endif
}
#if 0
#include <algorithm>
#include <chrono>
#include <functional>
#include <iostream>
#include <thread>
#include <vector>
static void test_mixed0() {
std::vector<std::unique_ptr<std::size_t>> numbers(1024 * 1024 * 100);
std::vector<std::thread> threads(1);
std::atomic<std::size_t> index{};
auto start = std::chrono::system_clock::now();
for (auto& thread : threads) {
thread = std::thread{[&index, &numbers]() {
while (true) {
auto i = index.fetch_add(1, std::memory_order_relaxed);
if (i >= numbers.size()) return;
numbers[i] = std::make_unique<std::size_t>(i);
}
}};
}
for (auto& thread : threads) thread.join();
auto end = std::chrono::system_clock::now();
auto duration =
std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "Running on " << threads.size() << " threads took " << duration
<< std::endl;
}
#endif
void asd() {
void* p = malloc(128);
free(p);
}
static void test_mixed1() {
std::thread thread(asd);
thread.join();
}
#if 0
// issue #691
static char* cptr;
static void* thread1_allocate()
{
cptr = mi_calloc_tp(char,22085632);
return NULL;
}
static void* thread2_free()
{
assert(cptr);
mi_free(cptr);
cptr = NULL;
return NULL;
}
static void test_large_migrate(void) {
auto t1 = std::thread(thread1_allocate);
t1.join();
auto t2 = std::thread(thread2_free);
t2.join();
/*
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, &thread1_allocate, NULL);
pthread_join(thread1, NULL);
pthread_create(&thread2, NULL, &thread2_free, NULL);
pthread_join(thread2, NULL);
*/
return;
}
#endif
// issue 445
static void strdup_test() {
#ifdef _MSC_VER
char* s = _strdup("hello\n");
char* buf = NULL;
size_t len;
_dupenv_s(&buf, &len, "MIMALLOC_VERBOSE");
mi_free(buf);
mi_free(s);
#endif
}
// Issue #202
static void heap_no_delete_worker() {
mi_heap_t* heap = mi_heap_new();
void* q = mi_heap_malloc(heap, 1024); (void)(q);
// mi_heap_delete(heap); // uncomment to prevent assertion
}
static void heap_no_delete() {
auto t1 = std::thread(heap_no_delete_worker);
t1.join();
}
// Issue #697
static void test_std_string() {
std::string path = "/Users/xxxx/Library/Developer/Xcode/DerivedData/xxxxxxxxxx/Build/Intermediates.noindex/xxxxxxxxxxx/arm64/XX_lto.o/0.arm64.lto.o";
std::string path1 = "/Users/xxxx/Library/Developer/Xcode/DerivedData/xxxxxxxxxx/Build/Intermediates.noindex/xxxxxxxxxxx/arm64/XX_lto.o/1.arm64.lto.o";
std::cout << path + "\n>>> " + path1 + "\n>>> " << std::endl;
}
// Issue #204
static volatile void* global_p;
static void t1main() {
mi_heap_t* heap = mi_heap_new();
global_p = mi_heap_malloc(heap, 1024);
mi_heap_delete(heap);
}
static void theap_late_free() {
auto t1 = std::thread(t1main);
msleep(2000);
assert(global_p);
mi_free((void*)global_p);
t1.join();
}
// issue #209
static void* shared_p;
static void alloc0(/* void* arg */)
{
shared_p = mi_malloc(8);
}
static void padding_shrink(void)
{
auto t1 = std::thread(alloc0);
t1.join();
mi_free(shared_p);
}
// Issue #221
static void theap_thread_free_large_worker() {
mi_free(shared_p);
}
static void theap_thread_free_large() {
for (int i = 0; i < 100; i++) {
shared_p = mi_malloc_aligned(2*1024*1024 + 1, 8);
auto t1 = std::thread(theap_thread_free_large_worker);
t1.join();
}
}
static void theap_thread_free_huge_worker() {
mi_free(shared_p);
}
static void theap_thread_free_huge() {
for (int i = 0; i < 10; i++) {
shared_p = mi_malloc(1024 * 1024 * 1024);
auto t1 = std::thread(theap_thread_free_huge_worker);
t1.join();
}
}
static std::atomic<long> xgsum;
static void local_alloc() {
long sum = 0;
for(int i = 0; i < 1000000; i++) {
const int n = 1 + std::rand() % 1000;
uint8_t* p = (uint8_t*)calloc(n, 1);
p[0] = 1;
sum += p[std::rand() % n];
if ((std::rand() % 100) > 24) {
free(p);
}
}
xgsum += sum;
}
static void test_thread_leak() {
std::vector<std::thread> threads;
for (int i=1; i<=100; ++i) {
threads.emplace_back(std::thread(&local_alloc));
}
for (auto& th : threads) {
th.join();
}
}
static void test_mt_shutdown()
{
const int threads = 5;
std::vector< std::future< std::vector< char* > > > ts;
auto fn = [&]()
{
std::vector< char* > ps;
ps.reserve(1000);
for (int i = 0; i < 1000; i++)
ps.emplace_back(new char[1]);
return ps;
};
for (int i = 0; i < threads; i++)
ts.emplace_back(std::async(std::launch::async, fn));
for (auto& f : ts)
for (auto& p : f.get())
delete[] p;
std::cout << "done" << std::endl;
}
// issue #372
static void fail_aslr() {
size_t sz = (size_t)(4ULL << 40); // 4TiB
void* p = malloc(sz);
printf("pointer p: %p: area up to %p\n", p, (uint8_t*)p + sz);
*(int*)0x5FFFFFFF000 = 0; // should segfault
}
// issues #414
static void dummy_worker() {
void* p = mi_malloc(0);
mi_free(p);
}
static void tsan_numa_test() {
auto t1 = std::thread(dummy_worker);
dummy_worker();
t1.join();
}
class MTest
{
char *data;
public:
MTest() { data = (char*)malloc(1024); }
~MTest() { free(data); };
};
thread_local MTest tlVariable;
void threadFun( int i )
{
printf( "Thread %d\n", i );
std::this_thread::sleep_for( std::chrono::milliseconds(100) );
}
void test_thread_local()
{
for( int i=1; i < 100; ++i )
{
std::thread t( threadFun, i );
t.join();
mi_stats_print(NULL);
}
return;
}
// issue #1177
thread_local void* s_ptr = mi_malloc(1);
void test_join() {
std::thread thread([]() { mi_free(s_ptr); });
thread.join();
mi_free(s_ptr);
}
static std::atomic<long> gsum;
const int LEN[] = { 1000, 5000, 10000, 50000 };
// adapted from example in
// https://github.com/microsoft/mimalloc/issues/1104
static void test_perf_local_alloc()
{
// thread-local random number generator
std::minstd_rand rng(std::random_device{}());
long sum = 0;
for (int i = 0; i < 1000000; i++)
{
int len = LEN[rng() % 4];
int* p = (int*)mi_zalloc_aligned(len * sizeof(int), alignof(int));
p[0] = 1;
sum += p[rng() % len];
free(p);
}
std::cout << ".";
gsum += sum;
}
static void test_perf_run()
{
std::vector<std::thread> threads;
for (int i = 0; i < 24; ++i)
{
threads.emplace_back(std::thread(&test_perf_local_alloc));
}
for (auto& th : threads)
{
th.join();
}
std::cout << "\n";
}
void test_perf(void)
{
test_perf_run();
std::cout << "gsum: " << gsum.load() << "\n";
}
static int sum2;
static void escape(uint8_t* p, size_t n) {
if (n==0) return;
p[std::rand() % n] = 42;
sum2 += p[std::rand() % n];
}
void test_perf2(void) {
for (size_t i = 0; i < 100000000; i++) {
const size_t n = 1000;
uint8_t* p = (uint8_t*)calloc(1, n);
escape(p,n);
free(p);
}
}
void test_perf3(void) {
for (size_t i = 0; i < 5; i++) {
const size_t n = (size_t)1*1024*1024*1024;
uint8_t* p = (uint8_t*)calloc(1, n);
escape(p, n);
free(p);
}
}
static void local_alloc4() {
for (int i = 0; i < 1000000; i++) {
const size_t n = i%1000;
uint8_t* p = (uint8_t*)calloc(1,n);
escape(p,n);
if (i % 4 > 0) {
free(p);
}
}
}
static void test_perf4(void) {
std::vector<std::thread> threads;
for (int i = 1; i <= 100; ++i) {
threads.emplace_back(std::thread(&local_alloc4));
}
for (auto& th : threads) {
th.join();
}
}
void escape5(uint8_t* p, size_t n) {
if (n==0) return;
for (size_t i = 0; i < n; i++) {
p[i] = (uint8_t)(i & 0xFF);
}
p[rand() % n] = (uint8_t)(n&0xFF);
// asm volatile("" : : "g"(p) : "memory");
}
static long gsum5;
static void local_alloc5() {
long sum = 0;
for (int i = 0; i < 500000; i++) {
const size_t n = i % 1000;
uint8_t* p = (uint8_t*)mi_malloc(n);
escape5(p, n);
if (i % 4 > 0) {
if (n>0) { sum += p[n-1]; }
mi_free(p);
}
}
gsum5 += sum;
}
static void test_perf5(void) {
std::vector<std::thread> threads;
for (int i = 1; i <= 100; ++i) {
threads.emplace_back(std::thread(&local_alloc5));
}
for (auto& th : threads) {
th.join();
}
printf("gsum5: %li\n", gsum5);
}
+51
View File
@@ -0,0 +1,51 @@
// test allocation in a DLL that is statically linked to mimalloc
#include <string>
#include <iostream>
#include "main-static-dep.h"
#include <mimalloc.h>
class Static {
private:
void* p;
public:
Static() {
printf("static-dep: static constructor\n");
p = mi_malloc(64);
return;
}
~Static() {
mi_free(p);
printf("static-dep: static destructor\n");
return;
}
};
static Static s = Static();
void Test(void) {
char* s = mi_mallocn_tp(char, 128);
#ifdef _WIN32
strcpy_s(s, 128, "hello world!");
#else
strlcpy(s, "hello world!", 128);
#endif
printf("message from static dll: %s\n", s);
mi_free(s);
}
#ifdef WIN32
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID reserved) {
(void)(reserved);
(void)(module);
if (reason==DLL_PROCESS_ATTACH) {
printf("static-dep: dll attach\n");
}
else if (reason==DLL_PROCESS_DETACH) {
mi_option_enable(mi_option_destroy_on_exit);
printf("static-dep: dll detach\n");
}
return TRUE;
}
#endif
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#if __cplusplus
extern "C" {
#endif
#ifdef WIN32
typedef void (__cdecl *TestFun)(void);
__declspec(dllexport) void __cdecl Test(void);
#else
typedef void (*TestFun)(void);
void Test(void);
#endif
#if __cplusplus
}
#endif
+44
View File
@@ -0,0 +1,44 @@
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <mimalloc.h>
#include <new>
#include <vector>
#include <future>
#include <iostream>
#include <thread>
#include <random>
#include <chrono>
#include <assert.h>
#include <dlfcn.h>
#include "main-static-dep.h"
TestFun fun;
void* so;
void testso() {
fun();
}
void loadso() {
so = dlopen("./libstatic.so", RTLD_LAZY);
fun = (TestFun)dlsym(so,"Test");
testso();
}
static void test_static(void) {
auto t1 = std::thread(&loadso);
t1.join();
auto t2 = std::thread(&testso);
t2.join();
}
int main(int argc, char** argv) {
test_static();
return 0;
}
+46
View File
@@ -0,0 +1,46 @@
#include <stdio.h>
#include <assert.h>
#include <mimalloc.h>
void test_heap(void* p_out) {
mi_heap_t* heap = mi_heap_new();
void* p1 = mi_heap_malloc(heap,32);
void* p2 = mi_heap_malloc(heap,48);
mi_free(p_out);
mi_heap_destroy(heap);
//mi_theap_delete(theap); mi_free(p1); mi_free(p2);
}
void test_large() {
const size_t N = 1000;
for (size_t i = 0; i < N; ++i) {
size_t sz = 1ull << 21;
char* a = mi_mallocn_tp(char,sz);
for (size_t k = 0; k < sz; k++) { a[k] = 'x'; }
mi_free(a);
}
}
int main() {
void* p1 = mi_malloc(16);
void* p2 = mi_malloc(1000000);
mi_free(p1);
mi_free(p2);
p1 = mi_malloc(16);
p2 = mi_malloc(16);
mi_free(p1);
mi_free(p2);
test_heap(mi_malloc(32));
p1 = mi_malloc_aligned(64, 16);
p2 = mi_malloc_aligned(160,24);
mi_free(p2);
mi_free(p1);
//test_large();
mi_collect(true);
mi_stats_print(NULL);
return 0;
}
+16
View File
@@ -0,0 +1,16 @@
Testing allocators is difficult as bugs may only surface after particular
allocation patterns. The main approach to testing _mimalloc_ is therefore
to have extensive internal invariant checking (see `page_is_valid` in `page.c`
for example), which is enabled in debug mode with `-DMI_DEBUG_FULL=ON`.
The main testing strategy is then to run [`mimalloc-bench`][bench] using full
invariant checking to catch any potential problems over a wide range of intensive
allocation benchmarks and programs.
However, this does not test well for the entire API surface and this is tested
with `test-api.c` when using `make test` (from `out/debug` etc). (This is
not complete yet, please add to it.)
The `main.c` and `main-override.c` are there to test if building and overriding
from a local install works and therefore these build a separate `test/CMakeLists.txt`.
[bench]: https://github.com/daanx/mimalloc-bench
+343
View File
@@ -0,0 +1,343 @@
/* ----------------------------------------------------------------------------
Copyright (c) 2018-2020, Microsoft Research, Daan Leijen
This is free software; you can redistribute it and/or modify it under the
terms of the MIT license. A copy of the license can be found in the file
"LICENSE" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "mimalloc.h"
#include "mimalloc/types.h"
#include "testhelper.h"
// ---------------------------------------------------------------------------
// Helper functions
// ---------------------------------------------------------------------------
bool check_zero_init(uint8_t* p, size_t size);
#if MI_DEBUG >= 2
bool check_debug_fill_uninit(uint8_t* p, size_t size);
bool check_debug_fill_freed(uint8_t* p, size_t size);
#endif
// ---------------------------------------------------------------------------
// Main testing
// ---------------------------------------------------------------------------
int main(void) {
mi_option_disable(mi_option_verbose);
// ---------------------------------------------------
// Zeroing allocation
// ---------------------------------------------------
CHECK_BODY("zeroinit-zalloc-small") {
size_t zalloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_zalloc(zalloc_size);
result = check_zero_init(p, zalloc_size);
mi_free(p);
};
CHECK_BODY("zeroinit-zalloc-large") {
size_t zalloc_size = MI_SMALL_SIZE_MAX * 2;
uint8_t* p = (uint8_t*)mi_zalloc(zalloc_size);
result = check_zero_init(p, zalloc_size);
mi_free(p);
};
CHECK_BODY("zeroinit-zalloc_small") {
size_t zalloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_zalloc_small(zalloc_size);
result = check_zero_init(p, zalloc_size);
mi_free(p);
};
CHECK_BODY("zeroinit-calloc-small") {
size_t calloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_calloc(calloc_size, 1);
result = check_zero_init(p, calloc_size);
mi_free(p);
};
CHECK_BODY("zeroinit-calloc-large") {
size_t calloc_size = MI_SMALL_SIZE_MAX * 2;
uint8_t* p = (uint8_t*)mi_calloc(calloc_size, 1);
result = check_zero_init(p, calloc_size);
mi_free(p);
};
CHECK_BODY("zeroinit-rezalloc-small") {
size_t zalloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_zalloc(zalloc_size);
result = check_zero_init(p, zalloc_size);
zalloc_size *= 3;
p = (uint8_t*)mi_rezalloc(p, zalloc_size);
result &= check_zero_init(p, zalloc_size);
mi_free(p);
};
CHECK_BODY("zeroinit-rezalloc-large") {
size_t zalloc_size = MI_SMALL_SIZE_MAX * 2;
uint8_t* p = (uint8_t*)mi_zalloc(zalloc_size);
result = check_zero_init(p, zalloc_size);
zalloc_size *= 3;
p = (uint8_t*)mi_rezalloc(p, zalloc_size);
result &= check_zero_init(p, zalloc_size);
mi_free(p);
};
CHECK_BODY("zeroinit-recalloc-small") {
size_t calloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_calloc(calloc_size, 1);
result = check_zero_init(p, calloc_size);
calloc_size *= 3;
p = (uint8_t*)mi_recalloc(p, calloc_size, 1);
result &= check_zero_init(p, calloc_size);
mi_free(p);
};
CHECK_BODY("zeroinit-recalloc-large") {
size_t calloc_size = MI_SMALL_SIZE_MAX * 2;
uint8_t* p = (uint8_t*)mi_calloc(calloc_size, 1);
result = check_zero_init(p, calloc_size);
calloc_size *= 3;
p = (uint8_t*)mi_recalloc(p, calloc_size, 1);
result &= check_zero_init(p, calloc_size);
mi_free(p);
};
// ---------------------------------------------------
// Zeroing in aligned API
// ---------------------------------------------------
CHECK_BODY("zeroinit-zalloc_aligned-small") {
size_t zalloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_zalloc_aligned(zalloc_size, MI_MAX_ALIGN_SIZE * 2);
result = check_zero_init(p, zalloc_size);
mi_free(p);
};
CHECK_BODY("zeroinit-zalloc_aligned-large") {
size_t zalloc_size = MI_SMALL_SIZE_MAX * 2;
uint8_t* p = (uint8_t*)mi_zalloc_aligned(zalloc_size, MI_MAX_ALIGN_SIZE * 2);
result = check_zero_init(p, zalloc_size);
mi_free(p);
};
CHECK_BODY("zeroinit-calloc_aligned-small") {
size_t calloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_calloc_aligned(calloc_size, 1, MI_MAX_ALIGN_SIZE * 2);
result = check_zero_init(p, calloc_size);
mi_free(p);
};
CHECK_BODY("zeroinit-calloc_aligned-large") {
size_t calloc_size = MI_SMALL_SIZE_MAX * 2;
uint8_t* p = (uint8_t*)mi_calloc_aligned(calloc_size, 1, MI_MAX_ALIGN_SIZE * 2);
result = check_zero_init(p, calloc_size);
mi_free(p);
};
CHECK_BODY("zeroinit-rezalloc_aligned-small") {
size_t zalloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_zalloc_aligned(zalloc_size, MI_MAX_ALIGN_SIZE * 2);
result = check_zero_init(p, zalloc_size);
zalloc_size *= 3;
p = (uint8_t*)mi_rezalloc_aligned(p, zalloc_size, MI_MAX_ALIGN_SIZE * 2);
result &= check_zero_init(p, zalloc_size);
mi_free(p);
};
CHECK_BODY("zeroinit-rezalloc_aligned-large") {
size_t zalloc_size = MI_SMALL_SIZE_MAX * 2;
uint8_t* p = (uint8_t*)mi_zalloc_aligned(zalloc_size, MI_MAX_ALIGN_SIZE * 2);
result = check_zero_init(p, zalloc_size);
zalloc_size *= 3;
p = (uint8_t*)mi_rezalloc_aligned(p, zalloc_size, MI_MAX_ALIGN_SIZE * 2);
result &= check_zero_init(p, zalloc_size);
mi_free(p);
};
CHECK_BODY("zeroinit-recalloc_aligned-small") {
size_t calloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_calloc_aligned(calloc_size, 1, MI_MAX_ALIGN_SIZE * 2);
result = check_zero_init(p, calloc_size);
calloc_size *= 3;
p = (uint8_t*)mi_recalloc_aligned(p, calloc_size, 1, MI_MAX_ALIGN_SIZE * 2);
result &= check_zero_init(p, calloc_size);
mi_free(p);
};
CHECK_BODY("zeroinit-recalloc_aligned-large") {
size_t calloc_size = MI_SMALL_SIZE_MAX * 2;
uint8_t* p = (uint8_t*)mi_calloc_aligned(calloc_size, 1, MI_MAX_ALIGN_SIZE * 2);
result = check_zero_init(p, calloc_size);
calloc_size *= 3;
p = (uint8_t*)mi_recalloc_aligned(p, calloc_size, 1, MI_MAX_ALIGN_SIZE * 2);
result &= check_zero_init(p, calloc_size);
mi_free(p);
};
#if (MI_DEBUG >= 2) && !MI_TSAN
// ---------------------------------------------------
// Debug filling
// ---------------------------------------------------
CHECK_BODY("uninit-malloc-small") {
size_t malloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_malloc(malloc_size);
result = check_debug_fill_uninit(p, malloc_size);
mi_free(p);
};
CHECK_BODY("uninit-malloc-large") {
size_t malloc_size = MI_SMALL_SIZE_MAX * 2;
uint8_t* p = (uint8_t*)mi_malloc(malloc_size);
result = check_debug_fill_uninit(p, malloc_size);
mi_free(p);
};
CHECK_BODY("uninit-malloc_small") {
size_t malloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_malloc_small(malloc_size);
result = check_debug_fill_uninit(p, malloc_size);
mi_free(p);
};
CHECK_BODY("uninit-realloc-small") {
size_t malloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_malloc(malloc_size);
result = check_debug_fill_uninit(p, malloc_size);
malloc_size *= 3;
p = (uint8_t*)mi_realloc(p, malloc_size);
result &= check_debug_fill_uninit(p, malloc_size);
mi_free(p);
};
CHECK_BODY("uninit-realloc-large") {
size_t malloc_size = MI_SMALL_SIZE_MAX * 2;
uint8_t* p = (uint8_t*)mi_malloc(malloc_size);
result = check_debug_fill_uninit(p, malloc_size);
malloc_size *= 3;
p = (uint8_t*)mi_realloc(p, malloc_size);
result &= check_debug_fill_uninit(p, malloc_size);
mi_free(p);
};
CHECK_BODY("uninit-mallocn-small") {
size_t malloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_mallocn(malloc_size, 1);
result = check_debug_fill_uninit(p, malloc_size);
mi_free(p);
};
CHECK_BODY("uninit-mallocn-large") {
size_t malloc_size = MI_SMALL_SIZE_MAX * 2;
uint8_t* p = (uint8_t*)mi_mallocn(malloc_size, 1);
result = check_debug_fill_uninit(p, malloc_size);
mi_free(p);
};
CHECK_BODY("uninit-reallocn-small") {
size_t malloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_mallocn(malloc_size, 1);
result = check_debug_fill_uninit(p, malloc_size);
malloc_size *= 3;
p = (uint8_t*)mi_reallocn(p, malloc_size, 1);
result &= check_debug_fill_uninit(p, malloc_size);
mi_free(p);
};
CHECK_BODY("uninit-reallocn-large") {
size_t malloc_size = MI_SMALL_SIZE_MAX * 2;
uint8_t* p = (uint8_t*)mi_mallocn(malloc_size, 1);
result = check_debug_fill_uninit(p, malloc_size);
malloc_size *= 3;
p = (uint8_t*)mi_reallocn(p, malloc_size, 1);
result &= check_debug_fill_uninit(p, malloc_size);
mi_free(p);
};
CHECK_BODY("uninit-malloc_aligned-small") {
size_t malloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_malloc_aligned(malloc_size, MI_MAX_ALIGN_SIZE * 2);
result = check_debug_fill_uninit(p, malloc_size);
mi_free(p);
};
CHECK_BODY("uninit-malloc_aligned-large") {
size_t malloc_size = MI_SMALL_SIZE_MAX * 2;
uint8_t* p = (uint8_t*)mi_malloc_aligned(malloc_size, MI_MAX_ALIGN_SIZE * 2);
result = check_debug_fill_uninit(p, malloc_size);
mi_free(p);
};
CHECK_BODY("uninit-realloc_aligned-small") {
size_t malloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_malloc_aligned(malloc_size, MI_MAX_ALIGN_SIZE * 2);
result = check_debug_fill_uninit(p, malloc_size);
malloc_size *= 3;
p = (uint8_t*)mi_realloc_aligned(p, malloc_size, MI_MAX_ALIGN_SIZE * 2);
result &= check_debug_fill_uninit(p, malloc_size);
mi_free(p);
};
CHECK_BODY("uninit-realloc_aligned-large") {
size_t malloc_size = MI_SMALL_SIZE_MAX * 2;
uint8_t* p = (uint8_t*)mi_malloc_aligned(malloc_size, MI_MAX_ALIGN_SIZE * 2);
result = check_debug_fill_uninit(p, malloc_size);
malloc_size *= 3;
p = (uint8_t*)mi_realloc_aligned(p, malloc_size, MI_MAX_ALIGN_SIZE * 2);
result &= check_debug_fill_uninit(p, malloc_size);
mi_free(p);
};
#if !(MI_TRACK_VALGRIND || MI_TRACK_ASAN || MI_GUARDED)
CHECK_BODY("fill-freed-small") {
size_t malloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_malloc(malloc_size);
mi_free(p);
// First sizeof(void*) bytes will contain housekeeping data, skip these
result = check_debug_fill_freed(p + sizeof(void*), malloc_size - sizeof(void*));
};
CHECK_BODY("fill-freed-large") {
size_t malloc_size = MI_SMALL_SIZE_MAX * 2;
uint8_t* p = (uint8_t*)mi_malloc(malloc_size);
mi_free(p);
// First sizeof(void*) bytes will contain housekeeping data, skip these
result = check_debug_fill_freed(p + sizeof(void*), malloc_size - sizeof(void*));
};
#endif
#endif
// ---------------------------------------------------
// Done
// ---------------------------------------------------[]
return print_test_summary();
}
// ---------------------------------------------------------------------------
// Helper functions
// ---------------------------------------------------------------------------
bool check_zero_init(uint8_t* p, size_t size) {
if(!p)
return false;
bool result = true;
for (size_t i = 0; i < size; ++i) {
result &= p[i] == 0;
}
return result;
}
#if MI_DEBUG >= 2
bool check_debug_fill_uninit(uint8_t* p, size_t size) {
#if MI_TRACK_VALGRIND || MI_TRACK_ASAN || MI_GUARDED
(void)p; (void)size;
return true; // when compiled with valgrind we don't init on purpose
#else
if(!p)
return false;
bool result = true;
for (size_t i = 0; i < size; ++i) {
result &= p[i] == MI_DEBUG_UNINIT;
}
return result;
#endif
}
bool check_debug_fill_freed(uint8_t* p, size_t size) {
#if MI_TRACK_VALGRIND || MI_GUARDED
(void)p; (void)size;
return true; // when compiled with valgrind we don't fill on purpose
#else
if(!p)
return false;
bool result = true;
for (size_t i = 0; i < size; ++i) {
result &= p[i] == MI_DEBUG_FREED;
}
return result;
#endif
}
#endif
+692
View File
@@ -0,0 +1,692 @@
/* ----------------------------------------------------------------------------
Copyright (c) 2018-2026, Microsoft Research, Daan Leijen
This is free software; you can redistribute it and/or modify it under the
terms of the MIT license. A copy of the license can be found in the file
"LICENSE" at the root of this distribution.
-----------------------------------------------------------------------------*/
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic ignored "-Walloc-size-larger-than="
#endif
/*
Testing allocators is difficult as bugs may only surface after particular
allocation patterns. The main approach to testing _mimalloc_ is therefore
to have extensive internal invariant checking (see `page_is_valid` in `page.c`
for example), which is enabled in debug mode with `-DMI_DEBUG_FULL=ON`.
The main testing is then to run `mimalloc-bench` [1] using full invariant checking
to catch any potential problems over a wide range of intensive allocation bench
marks.
However, this does not test well for the entire API surface. In this test file
we therefore test the API over various inputs. Please add more tests :-)
[1] https://github.com/daanx/mimalloc-bench
*/
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <errno.h>
#ifdef __cplusplus
#include <vector>
#endif
#include "mimalloc.h"
// #include "mimalloc/internal.h"
#include "mimalloc/types.h" // for MI_DEBUG and MI_PAGE_MAX_OVERALLOC_ALIGN
#include "testhelper.h"
// ---------------------------------------------------------------------------
// Test functions
// ---------------------------------------------------------------------------
bool test_theap1(void);
bool test_theap2(void);
bool test_theap_arena_destroy(void);
bool test_theap_arena_delete(void);
bool test_stl_allocator1(void);
bool test_stl_allocator2(void);
bool test_stl_theap_allocator1(void);
bool test_stl_theap_allocator2(void);
bool test_stl_theap_allocator3(void);
bool test_stl_theap_allocator4(void);
static bool test_zero_aligned_first(void);
static bool mem_has_vals(const uint8_t* p, size_t size, uint8_t val) {
if (p==NULL) return false;
for (size_t i = 0; i < size; ++i) {
if (p[i] != val) return false;
}
return true;
}
static bool mem_is_zero(const void* p, size_t size) {
return mem_has_vals((const uint8_t*)p,size,0);
}
// ---------------------------------------------------------------------------
// Main testing
// ---------------------------------------------------------------------------
int main(void) {
mi_option_disable(mi_option_verbose);
#if 1
#if defined(__cplusplus) && !defined(_MSC_VER)
CHECK_BODY("c++ new-handler") {
std::set_new_handler([]{ throw std::bad_alloc(); });
void* p = mi_new_nothrow(SIZE_MAX/2);
result = (p==NULL);
}
CHECK_BODY("c++ new handler2") {
try {
void* p = mi_new_n(SIZE_MAX/2, 4);
(void)(p);
result = false;
}
catch(std::bad_alloc) {
result = true;
}
}
#endif
#endif
// ---------------------------------------------------
// Malloc
// ---------------------------------------------------
CHECK_BODY("malloc-zero") {
void* p = mi_malloc(0);
result = (p != NULL);
mi_free(p);
};
CHECK_BODY("malloc-nomem1") {
result = (mi_malloc((size_t)PTRDIFF_MAX + (size_t)1) == NULL);
};
CHECK_BODY("malloc-free-null") {
mi_free(NULL);
};
#if MI_INTPTR_BITS > 32
CHECK_BODY("malloc-free-invalid-low") {
mi_cfree((void*)(MI_ZU(0x0000000003990080))); // issue #1087
};
#endif
CHECK_BODY("calloc-overflow") {
// use (size_t)&mi_calloc to get some number without triggering compiler warnings
result = (mi_calloc((size_t)&mi_calloc,SIZE_MAX/1000) == NULL);
};
CHECK_BODY("malloc-large") { // see PR #544.
void* p = mi_malloc(67108872);
mi_free(p);
};
CHECK_BODY("calloc0") {
void* p = mi_calloc(0,1000);
const size_t usable = mi_usable_size(p);
result = (usable <= 16);
mi_free(p);
};
CHECK_BODY("mi_urealloc_invalid") {
void* p = mi_malloc(64);
size_t pre, post;
void* q = mi_urealloc((char*)p + 3, 32, &pre, &post);
mi_free(p);
result = (q==NULL || q==(uint8_t*)p+3);
}
// ---------------------------------------------------
// Extended
// ---------------------------------------------------
CHECK_BODY("posix_memalign1") {
void* p = &p;
int err = mi_posix_memalign(&p, sizeof(void*), 32);
result = ((err==0 && (uintptr_t)p % sizeof(void*) == 0) || p==&p);
mi_free(p);
};
CHECK_BODY("posix_memalign_no_align") {
void* p = &p;
int err = mi_posix_memalign(&p, 3, 32);
result = (err==EINVAL && p==&p);
};
CHECK_BODY("posix_memalign_zero") {
void* p = &p;
int err = mi_posix_memalign(&p, sizeof(void*), 0);
mi_free(p);
result = (err==0);
};
CHECK_BODY("posix_memalign_nopow2") {
void* p = &p;
int err = mi_posix_memalign(&p, 3*sizeof(void*), 32);
result = (err==EINVAL && p==&p);
};
CHECK_BODY("posix_memalign_nomem") {
void* p = &p;
int err = mi_posix_memalign(&p, sizeof(void*), SIZE_MAX);
result = (err==ENOMEM && p==&p);
};
// ---------------------------------------------------
// Aligned API
// ---------------------------------------------------
CHECK_BODY("malloc-aligned1") {
void* p = mi_malloc_aligned(32,32); result = (p != NULL && (uintptr_t)(p) % 32 == 0); mi_free(p);
};
CHECK_BODY("malloc-aligned2") {
void* p = mi_malloc_aligned(48,32); result = (p != NULL && (uintptr_t)(p) % 32 == 0); mi_free(p);
};
CHECK_BODY("malloc-aligned3") {
void* p1 = mi_malloc_aligned(48,32); bool result1 = (p1 != NULL && (uintptr_t)(p1) % 32 == 0);
void* p2 = mi_malloc_aligned(48,32); bool result2 = (p2 != NULL && (uintptr_t)(p2) % 32 == 0);
mi_free(p2);
mi_free(p1);
result = (result1&&result2);
};
CHECK_BODY("malloc-aligned4") {
void* p;
bool ok = true;
for (int i = 0; i < 8 && ok; i++) {
p = mi_malloc_aligned(8, 16);
ok = (p != NULL && (uintptr_t)(p) % 16 == 0); mi_free(p);
}
result = ok;
};
CHECK_BODY("malloc-aligned5") {
void* p = mi_malloc_aligned(4097,4096);
size_t usable = mi_usable_size(p);
result = (usable >= 4097 && usable < 16000);
fprintf(stderr, "malloc_aligned5: usable size: %zi. ", usable);
mi_free(p);
};
/*
CHECK_BODY("malloc-aligned6") {
bool ok = true;
for (size_t align = 1; align <= MI_PAGE_MAX_OVERALLOC_ALIGN && ok; align *= 2) {
void* ps[8];
for (int i = 0; i < 8 && ok; i++) {
ps[i] = mi_malloc_aligned(align*13 // size
, align);
if (ps[i] == NULL || (uintptr_t)(ps[i]) % align != 0) {
ok = false;
}
}
for (int i = 0; i < 8 && ok; i++) {
mi_free(ps[i]);
}
}
result = ok;
};
*/
CHECK_BODY("malloc-aligned7") {
void* p = mi_malloc_aligned(1024,MI_PAGE_MAX_OVERALLOC_ALIGN);
mi_free(p);
result = ((uintptr_t)p % MI_PAGE_MAX_OVERALLOC_ALIGN) == 0;
};
CHECK_BODY("malloc-aligned8") {
bool ok = true;
for (int i = 0; i < 5 && ok; i++) {
int n = (1 << i);
void* p = mi_malloc_aligned(1024, n * MI_PAGE_MAX_OVERALLOC_ALIGN);
ok = ((uintptr_t)p % (n*MI_PAGE_MAX_OVERALLOC_ALIGN)) == 0;
mi_free(p);
}
result = ok;
};
CHECK_BODY("malloc-aligned9") { // test large alignments
bool ok = true;
void* p[8];
const int max_align_shift =
#if SIZE_MAX > UINT32_MAX
28 /* up to 64 MiB alignment */
#else
20
#endif
;
size_t sizes[8] = { 8, 512, 1024 * 1024, MI_PAGE_MAX_OVERALLOC_ALIGN, MI_PAGE_MAX_OVERALLOC_ALIGN + 1, 2 * MI_PAGE_MAX_OVERALLOC_ALIGN, 8 * MI_PAGE_MAX_OVERALLOC_ALIGN, 0 };
for (int i = 0; i < max_align_shift && ok; i++) {
int align = (1 << i);
for (int j = 0; j < 8 && ok; j++) {
p[j] = mi_zalloc_aligned(sizes[j], align);
ok = ((uintptr_t)p[j] % align) == 0;
}
for (int j = 0; j < 8; j++) {
mi_free(p[j]);
}
}
result = ok;
};
CHECK_BODY("malloc-aligned9a") { // test large alignments
void* p = mi_zalloc_aligned(1024 * 1024, 2);
mi_free(p);
p = mi_zalloc_aligned(1024 * 1024, 2);
mi_free(p);
result = true;
};
CHECK_BODY("malloc-aligned10") {
bool ok = true;
void* p[10+1];
int align;
int j;
for(j = 0, align = 1; j <= 10 && ok; align *= 2, j++ ) {
p[j] = mi_malloc_aligned(43 + align, align);
ok = ((uintptr_t)p[j] % align) == 0;
}
for ( ; j > 0; j--) {
mi_free(p[j-1]);
}
result = ok;
}
//CHECK_BODY("malloc_aligned11") {
// mi_theap_t* theap = mi_theap_new();
// void* p = mi_theap_malloc_aligned(theap, 33554426, 8);
// result = mi_theap_contains_block(theap, p);
// mi_theap_destroy(theap);
//}
CHECK_BODY("mimalloc-aligned12") {
void* p = mi_malloc_aligned(0x100, 0x100);
result = (((uintptr_t)p % 0x100) == 0); // #602
mi_free(p);
}
CHECK_BODY("mimalloc-aligned13") {
bool ok = true;
for( size_t size = 1; size <= (MI_SMALL_SIZE_MAX * 2) && ok; size++ ) {
for(size_t align = 1; align <= size && ok; align *= 2 ) {
void* p[10];
for(int i = 0; i < 10 && ok; i++) {
p[i] = mi_malloc_aligned(size,align);;
ok = (p[i] != NULL && ((uintptr_t)(p[i]) % align) == 0);
}
for(int i = 0; i < 10 && ok; i++) {
mi_free(p[i]);
}
/*
if (ok && align <= size && ((size + MI_PADDING_SIZE) & (align-1)) == 0) {
size_t bsize = mi_good_size(size);
ok = (align <= bsize && (bsize & (align-1)) == 0);
}
*/
}
}
result = ok;
}
CHECK_BODY("malloc-aligned-at1") {
void* p = mi_malloc_aligned_at(48,32,0); result = (p != NULL && ((uintptr_t)(p) + 0) % 32 == 0); mi_free(p);
};
CHECK_BODY("malloc-aligned-at2") {
void* p = mi_malloc_aligned_at(50,32,8); result = (p != NULL && ((uintptr_t)(p) + 8) % 32 == 0); mi_free(p);
};
CHECK_BODY("memalign1") {
void* p;
bool ok = true;
for (int i = 0; i < 8 && ok; i++) {
p = mi_memalign(16,8);
ok = (p != NULL && (uintptr_t)(p) % 16 == 0); mi_free(p);
}
result = ok;
};
CHECK_BODY("zalloc-aligned-small1") {
size_t zalloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_zalloc_aligned(zalloc_size, MI_MAX_ALIGN_SIZE * 2);
result = mem_is_zero(p, zalloc_size);
mi_free(p);
};
CHECK_BODY("rezalloc_aligned-small1") {
size_t zalloc_size = MI_SMALL_SIZE_MAX / 2;
uint8_t* p = (uint8_t*)mi_zalloc_aligned(zalloc_size, MI_MAX_ALIGN_SIZE * 2);
result = mem_is_zero(p, zalloc_size);
zalloc_size *= 3;
p = (uint8_t*)mi_rezalloc_aligned(p, zalloc_size, MI_MAX_ALIGN_SIZE * 2);
result = result && mem_is_zero(p, zalloc_size);
mi_free(p);
};
CHECK_BODY("rezalloc_aligned_zeros") { // issue #763
size_t alignment = 1024;
size_t n = 1024 * 6;
void* ptr = mi_zalloc_aligned(n, alignment);
assert(mem_is_zero(ptr,n));
memset(ptr,123,n/2);
ptr = mi_rezalloc_aligned(ptr, n/2, alignment);
assert(mem_has_vals((uint8_t*)ptr,n/2,123));
ptr = mi_rezalloc_aligned(ptr, n, alignment);
assert(mem_has_vals((uint8_t*)ptr,n/2,123));
result = mem_is_zero((uint8_t*)ptr + n/2, n/2);
}
// ---------------------------------------------------
// Reallocation
// ---------------------------------------------------
CHECK_BODY("realloc-null") {
void* p = mi_realloc(NULL,4);
result = (p != NULL);
mi_free(p);
};
CHECK_BODY("realloc-null-sizezero") {
void* p = mi_realloc(NULL,0); // <https://en.cppreference.com/w/c/memory/realloc> "If ptr is NULL, the behavior is the same as calling malloc(new_size)."
result = (p != NULL);
mi_free(p);
};
CHECK_BODY("realloc-sizezero") {
void* p = mi_malloc(4);
void* q = mi_realloc(p, 0);
result = (q != NULL);
mi_free(q);
};
CHECK_BODY("reallocarray-null-sizezero") {
void* p = mi_reallocarray(NULL,0,16); // issue #574
result = (p != NULL && errno == 0);
mi_free(p);
};
CHECK_BODY("realloc-guarded") { // issue #1304
void* shared_ptr = NULL;
for (int iterations = 0; iterations < 64; ++iterations) {
for (int i = 0; i < 1024; ++i) {
shared_ptr = mi_realloc(shared_ptr, i * 64);
}
}
}
// ---------------------------------------------------
// Small allocations
// ---------------------------------------------------
CHECK_BODY("free_small1") {
for(size_t n = 1; n < MI_SMALL_SIZE_MAX; n *=2) {
const size_t size = n*sizeof(int);
int* p = (int*)mi_zalloc(size);
p[n-1] = 42;
mi_free_size(p,size);
}
}
CHECK_BODY("free_small2") {
for(size_t n = 1; n < MI_SMALL_SIZE_MAX; n *=2) {
const size_t size = n*sizeof(int);
int* p = (int*)mi_zalloc(size);
p[n-1] = 42;
p = (int*)mi_rezalloc(p, size + MI_SMALL_SIZE_MAX);
mi_free_size(p,size + MI_SMALL_SIZE_MAX);
}
}
// ---------------------------------------------------
// Returned block sizes
// ---------------------------------------------------
CHECK_BODY("umalloc1") {
for(size_t size = 1; size <= 32*MI_MiB; size *= 2 ) {
size_t bsize;
void* p = mi_umalloc(size,&bsize);
assert(bsize >= size);
size_t pre_size;
size_t post_size;
p = mi_urealloc(p, size + 1024, &pre_size, &post_size);
assert(pre_size == bsize);
assert(post_size >= size + 1024);
size_t fsize;
mi_ufree(p,&fsize);
assert(fsize == post_size);
}
}
#if (MI_INTPTR_SIZE > 4)
CHECK_BODY("arena_reserve") {
result = (0==mi_reserve_os_memory(16*MI_GiB,false,true));
}
#endif
// ---------------------------------------------------
// Heaps
// ---------------------------------------------------
CHECK_BODY("heap-os1") {
// @zoxc opus bug #2.
mi_heap_t* h = mi_heap_new();
void* p = mi_heap_malloc_aligned(h, 1<<20, 2<<20); // forced OS allocation
mi_heap_delete(h);
mi_free(p); // SIGSEGV
}
CHECK_BODY("heap-os2") {
// @zoxc opus bug #3.
mi_collect(true);
mi_stats_t_decl(stats0);
mi_stats_get(&stats0);
mi_heap_t* h = mi_heap_new();
long failed = 0;
for(int i = 0; i < 10; i++) {
int* p = (int*)mi_heap_malloc_aligned(h, 1<<20, 2<<20); // forced OS allocation
if (p==NULL) {
failed++;
}
else {
p[0] = 42;
}
}
mi_heap_destroy(h);
mi_collect(true);
mi_stats_t_decl(stats1);
mi_stats_get(&stats1);
result = (stats0.pages.current == stats1.pages.current);
if (!result) {
fprintf(stderr, "heap-os2: pages: %ld != %ld (failed: %ld)\n", (long)stats0.pages.current, (long)stats1.pages.current, failed);
}
}
#define NHEAPS (1000)
CHECK_BODY("heap-many") { // check creating many heaps and threadlocals, see issue #1358
mi_heap_t* heaps[NHEAPS];
for (size_t i = 0; i < NHEAPS; i++) {
heaps[i] = mi_heap_new();
if (heaps[i] == NULL) { result = false; break; };
if (mi_heap_malloc(heaps[i], 32) == NULL) { result = false; break; }
}
for (size_t i = 0; i < NHEAPS; i++) {
mi_heap_destroy(heaps[i]);
}
}
//CHECK("theap_destroy", test_theap1());
//CHECK("theap_delete", test_theap2());
//CHECK("theap_arena_destroy", test_theap_arena_destroy());
//CHECK("theap_arena_delete", test_theap_arena_delete());
// ---------------------------------------------------
// Threads
// ---------------------------------------------------
CHECK_BODY("zero_aligned_first") {
result = mi_run_on_thread(&test_zero_aligned_first);
}
//mi_stats_print(NULL);
// ---------------------------------------------------
// various
// ---------------------------------------------------
#if !defined(MI_TRACK_ASAN) // realpath may leak with ASAN enabled (as the ASAN allocator intercepts it)
CHECK_BODY("realpath") {
char* s = mi_realpath( ".", NULL );
// printf("realpath: %s\n",s);
mi_free(s);
};
#endif
CHECK("stl_allocator1", test_stl_allocator1());
CHECK("stl_allocator2", test_stl_allocator2());
//CHECK("stl_theap_allocator1", test_stl_theap_allocator1());
//CHECK("stl_theap_allocator2", test_stl_theap_allocator2());
//CHECK("stl_theap_allocator3", test_stl_theap_allocator3());
//CHECK("stl_theap_allocator4", test_stl_theap_allocator4());
// ---------------------------------------------------
// Done
// ---------------------------------------------------[]
return print_test_summary();
}
// ---------------------------------------------------
// Larger test functions
// ---------------------------------------------------
/*
bool test_theap1(void) {
mi_theap_t* theap = mi_theap_new();
int* p1 = mi_theap_malloc_tp(theap,int);
int* p2 = mi_theap_malloc_tp(theap,int);
*p1 = *p2 = 43;
mi_theap_destroy(theap);
return true;
}
bool test_theap2(void) {
mi_theap_t* theap = mi_theap_new();
int* p1 = mi_theap_malloc_tp(theap,int);
int* p2 = mi_theap_malloc_tp(theap,int);
mi_theap_delete(theap);
*p1 = 42;
mi_free(p1);
mi_free(p2);
return true;
}
bool test_theap_arena_destroy(void) {
mi_arena_id_t arena_id = NULL;
if (mi_reserve_os_memory_ex(64 * 1024 * 1024, true, false, true, &arena_id) != 0) {
return false;
}
mi_theap_t* theap = mi_theap_new_ex(0, true, arena_id);
if (theap == NULL) {
return false;
}
mi_theap_destroy(theap);
return true;
}
bool test_theap_arena_delete(void) {
mi_arena_id_t arena_id = NULL;
if (mi_reserve_os_memory_ex(64 * 1024 * 1024, true, false, true, &arena_id) != 0) {
return false;
}
mi_theap_t* theap = mi_theap_new_ex(0, true, arena_id);
if (theap == NULL) {
return false;
}
mi_theap_delete(theap);
return true;
}
*/
bool test_stl_allocator1(void) {
#ifdef __cplusplus
std::vector<int, mi_stl_allocator<int> > vec;
vec.push_back(1);
vec.pop_back();
return vec.size() == 0;
#else
return true;
#endif
}
struct some_struct { int i; int j; double z; };
bool test_stl_allocator2(void) {
#ifdef __cplusplus
std::vector<some_struct, mi_stl_allocator<some_struct> > vec;
vec.push_back(some_struct());
vec.pop_back();
return vec.size() == 0;
#else
return true;
#endif
}
/*
bool test_stl_theap_allocator1(void) {
#ifdef __cplusplus
std::vector<some_struct, mi_theap_stl_allocator<some_struct> > vec;
vec.push_back(some_struct());
vec.pop_back();
return vec.size() == 0;
#else
return true;
#endif
}
bool test_stl_theap_allocator2(void) {
#ifdef __cplusplus
std::vector<some_struct, mi_theap_destroy_stl_allocator<some_struct> > vec;
vec.push_back(some_struct());
vec.pop_back();
return vec.size() == 0;
#else
return true;
#endif
}
bool test_stl_theap_allocator3(void) {
#ifdef __cplusplus
mi_theap_t* theap = mi_theap_new();
bool good = false;
{
mi_theap_stl_allocator<some_struct> myAlloc(theap);
std::vector<some_struct, mi_theap_stl_allocator<some_struct> > vec(myAlloc);
vec.push_back(some_struct());
vec.pop_back();
good = vec.size() == 0;
}
mi_theap_delete(theap);
return good;
#else
return true;
#endif
}
bool test_stl_theap_allocator4(void) {
#ifdef __cplusplus
mi_theap_t* theap = mi_theap_new();
bool good = false;
{
mi_theap_destroy_stl_allocator<some_struct> myAlloc(theap);
std::vector<some_struct, mi_theap_destroy_stl_allocator<some_struct> > vec(myAlloc);
vec.push_back(some_struct());
vec.pop_back();
good = vec.size() == 0;
}
mi_theap_destroy(theap);
return good;
#else
return true;
#endif
}
*/
// ---------------------------------------------------------------------------
// Test a zero size aligned allocation as the very first allocation of a fresh thread.
// ---------------------------------------------------------------------------
static bool test_zero_aligned_first(void) {
void* p = mi_malloc_aligned(0, 16); // must be the first mimalloc call on this thread
bool res = (p != NULL && (uintptr_t)(p) % 16 == 0);
mi_free(p);
p = mi_zalloc_aligned(0, 32);
res = res && (p != NULL && (uintptr_t)(p) % 32 == 0);
mi_free(p);
return res;
}
+14
View File
@@ -0,0 +1,14 @@
/* ----------------------------------------------------------------------------
Copyright (c) 2018-2026 Microsoft Research, Daan Leijen
This is free software; you can redistribute it and/or modify it under the
terms of the MIT license.
-----------------------------------------------------------------------------*/
#define TEST_STRESS 1
#define MI_USE_HEAPS 4
#if !defined(MI_TEST_LIGHT) // too slow in test integration
#define ALLOW_LARGE 1
#endif
#include "test-stress.c"
+10
View File
@@ -0,0 +1,10 @@
/* ----------------------------------------------------------------------------
Copyright (c) 2018-2026 Microsoft Research, Daan Leijen
This is free software; you can redistribute it and/or modify it under the
terms of the MIT license.
-----------------------------------------------------------------------------*/
#define TEST_STRESS_SUBPROCS 1
#define NSUBPROCS 2
#define NTHREADS 16
#include "test-stress.c"
+578
View File
@@ -0,0 +1,578 @@
/* ----------------------------------------------------------------------------
Copyright (c) 2018-2026 Microsoft Research, Daan Leijen
This is free software; you can redistribute it and/or modify it under the
terms of the MIT license.
-----------------------------------------------------------------------------*/
/* This is a stress test for the allocator, using multiple threads and
transferring objects between threads. It tries to reflect real-world workloads:
- allocation size is distributed linearly in powers of two
- with some fraction extra large (and some very large)
- the allocations are initialized and read again at free
- pointers transfer between threads
- threads are terminated and recreated with some objects surviving in between
- uses deterministic "randomness", but execution can still depend on
(random) thread scheduling. Do not use this test as a benchmark!
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <mimalloc.h>
#include <mimalloc-stats.h>
// #define MI_GUARDED 1
// #define USE_STD_MALLOC 1
// #define MI_USE_HEAPS 1
// #define ALLOW_LARGE 1
// #define TEST_STRESS_SUBPROCS 1
// #define TEST_LEAK 1
#define TEST_STRESS 1
#ifndef NTHREADS
#define NTHREADS 32
#endif
// > mimalloc-test-stress [THREADS] [SCALE] [ITER]
//
// argument defaults
#if defined(MI_TSAN) && MI_TEST_LIGHT // with thread-sanitizer reduce the threads to test within the azure pipeline limits
static int THREADS = NTHREADS/4;
static int SCALE = 10;
static int ITER = 100;
#elif defined(MI_TSAN) // with thread-sanitizer reduce the threads to test within the azure pipeline limits
static int THREADS = NTHREADS/4;
static int SCALE = 25;
static int ITER = 500;
#elif defined(MI_UBSAN) // with undefined behavious sanitizer reduce parameters to stay within the azure pipeline limits
static int THREADS = NTHREADS/4;
static int SCALE = 25;
static int ITER = 20;
#elif defined(MI_GUARDED) // with debug guard pages reduce parameters to stay within the azure pipeline limits
static int THREADS = NTHREADS/4;
static int SCALE = 25;
static int ITER = 10;
#elif MI_DEBUG && MI_TEST_LIGHT
static int THREADS = NTHREADS/4;
static int SCALE = 25;
static int ITER = 10;
#elif MI_DEBUG
static int THREADS = NTHREADS;
static int SCALE = 25;
static int ITER = 25;
#else
static int THREADS = NTHREADS; // more repeatable if THREADS <= #processors
static int SCALE = 50; // scaling factor
static int ITER = 50; // N full iterations destructing and re-creating all threads
#endif
#ifndef ALLOW_LARGE
#define ALLOW_LARGE false
#endif
static bool allow_large_objects = ALLOW_LARGE; // allow very large objects? (set to `true` if SCALE>100)
static size_t use_one_size = 0; // use single object size of `N * sizeof(uintptr_t)`?
static bool main_participates = false; // main thread participates as a worker too
#ifdef USE_STD_MALLOC
#define custom_calloc(n,s) calloc(n,s)
#define custom_realloc(p,s) realloc(p,s)
#define custom_free(p) free(p)
#else
#ifdef MI_USE_HEAPS
#if TEST_STRESS_SUBPROCS
#error "cannot test rolling heaps with multiple subprocesses (for now)"
#endif
static mi_heap_t* current_heap;
#define custom_calloc(n,s) mi_heap_calloc(current_heap,n,s)
#define custom_realloc(p,s) mi_heap_realloc(current_heap,p,s)
#define custom_free(p) mi_free(p)
#else
#define custom_calloc(n,s) mi_calloc(n,s)
#define custom_realloc(p,s) mi_realloc(p,s)
#define custom_free(p) mi_free(p)
#endif
#ifndef NDEBUG
#define xMI_HEAP_WALK // walk the theap objects?
#endif
#endif
// transfer pointer between threads
#define TRANSFERS (1000)
// static volatile void* transfer[TRANSFERS];
#if (UINTPTR_MAX != UINT32_MAX)
const uintptr_t cookie = 0xbf58476d1ce4e5b9UL;
#else
const uintptr_t cookie = 0x1ce4e5b9UL;
#endif
static void* atomic_exchange_ptr(volatile void** p, void* newval);
typedef uintptr_t* random_t;
static uintptr_t pick(random_t r) {
uintptr_t x = *r;
#if (UINTPTR_MAX > UINT32_MAX)
// by Sebastiano Vigna, see: <http://xoshiro.di.unimi.it/splitmix64.c>
x ^= x >> 30;
x *= 0xbf58476d1ce4e5b9UL;
x ^= x >> 27;
x *= 0x94d049bb133111ebUL;
x ^= x >> 31;
#else
// by Chris Wellons, see: <https://nullprogram.com/blog/2018/07/31/>
x ^= x >> 16;
x *= 0x7feb352dUL;
x ^= x >> 15;
x *= 0x846ca68bUL;
x ^= x >> 16;
#endif
*r = x;
return x;
}
static bool chance(size_t perc, random_t r) {
return (pick(r) % 100 <= perc);
}
static void* alloc_items(size_t items, random_t r) {
if (chance(1, r)) {
if (chance(1, r) && allow_large_objects) items *= 10000; // 0.01% giant
else if (chance(10, r) && allow_large_objects) items *= 1000; // 0.1% huge
else items *= 100; // 1% large objects;
}
if (items>=32 && items<=40) items*=2; // pthreads uses 320b allocations (this shows that more clearly in the stats)
if (use_one_size > 0) items = (use_one_size / sizeof(uintptr_t));
if (items==0) items = 1;
uintptr_t* p = (uintptr_t*)custom_calloc(items,sizeof(uintptr_t));
if (p != NULL) {
for (uintptr_t i = 0; i < items; i++) {
assert(p[i] == 0);
p[i] = (items - i) ^ cookie;
}
}
return p;
}
static void free_items(void* p) {
if (p != NULL) {
uintptr_t* q = (uintptr_t*)p;
uintptr_t items = (q[0] ^ cookie);
for (uintptr_t i = 0; i < items; i++) {
if ((q[i] ^ cookie) != items - i) {
fprintf(stderr, "memory corruption at block %p at %zu\n", p, i);
abort();
}
}
}
custom_free(p);
}
#ifdef MI_HEAP_WALK
static bool visit_blocks(const mi_theap_t* theap, const mi_theap_area_t* area, void* block, size_t block_size, void* arg) {
(void)(theap); (void)(area);
size_t* total = (size_t*)arg;
if (block != NULL) {
*total += block_size;
}
return true;
}
#endif
static void stress(intptr_t tid, void* vtransfers) {
#ifndef USE_STD_MALLOC
// printf("test stress thread: subproc: %p, tid: %zi\n", mi_subproc_current()._mi_subproc_id, tid);
#endif
volatile void** transfers = (volatile void**)vtransfers;
//bench_start_thread();
uintptr_t r = ((tid + 1) * 43); // rand();
const size_t max_item_shift = 5; // 128
const size_t max_item_retained_shift = max_item_shift + 2;
size_t allocs = 100 * ((size_t)SCALE) * (tid % 8 + 1); // some threads do more
size_t retain = allocs / 2;
void** data = NULL;
size_t data_size = 0;
size_t data_top = 0;
void** retained = (void**)custom_calloc(retain,sizeof(void*));
size_t retain_top = 0;
while (allocs > 0 || retain > 0) {
if (retain == 0 || (chance(50, &r) && allocs > 0)) {
// 50%+ alloc
allocs--;
if (data_top >= data_size) {
data_size += 100000;
data = (void**)custom_realloc(data, data_size * sizeof(void*));
}
data[data_top++] = alloc_items(1ULL << (pick(&r) % max_item_shift), &r);
}
else {
// 25% retain
retained[retain_top++] = alloc_items( 1ULL << (pick(&r) % max_item_retained_shift), &r);
retain--;
}
if (chance(66, &r) && data_top > 0) {
// 66% free previous alloc
size_t idx = pick(&r) % data_top;
free_items(data[idx]);
data[idx] = NULL;
}
if (chance(25, &r) && data_top > 0) {
// 25% exchange a local pointer with the (shared) transfer buffer.
size_t data_idx = pick(&r) % data_top;
size_t transfer_idx = pick(&r) % TRANSFERS;
void* p = data[data_idx];
void* q = atomic_exchange_ptr(&transfers[transfer_idx], p);
data[data_idx] = q;
}
}
#ifdef MI_HEAP_WALK
// walk the theap
size_t total = 0;
mi_theap_visit_blocks(mi_theap_get_default(), true, visit_blocks, &total);
#endif
// free everything that is left
for (size_t i = 0; i < retain_top; i++) {
free_items(retained[i]);
}
for (size_t i = 0; i < data_top; i++) {
free_items(data[i]);
}
custom_free(retained);
custom_free(data);
//bench_end_thread();
}
static mi_subproc_id_t subproc_null = { NULL };
typedef void (thread_entry_fun_t)(intptr_t tid, void* arg);
static void run_os_threads(mi_subproc_id_t subproc, size_t nthreads, thread_entry_fun_t* fun, void* arg);
static void test_stress(mi_subproc_id_t subproc) {
// printf("test stress: subproc: %p\n", subproc._mi_subproc_id);
volatile void* transfers[TRANSFERS];
memset((void**)transfers,0,sizeof(transfers));
#ifdef MI_USE_HEAPS
mi_heap_t* prev_heaps[MI_USE_HEAPS] = { NULL };
#endif
uintptr_t r = rand();
for (int n = 0; n < ITER; n++) {
#ifdef MI_USE_HEAPS
// new heap for each iteration
if (prev_heaps[MI_USE_HEAPS-1] != NULL) {
mi_heap_delete(prev_heaps[MI_USE_HEAPS-1]); // delete from N iterations ago
}
for(int i = MI_USE_HEAPS-1; i > 0; i--) {
prev_heaps[i] = prev_heaps[i-1];
}
prev_heaps[0] = current_heap;
current_heap = mi_heap_new();
#endif
run_os_threads(subproc, THREADS, &stress, (void**)transfers);
#if !defined(NDEBUG) && !defined(USE_STD_MALLOC)
// switch between arena and OS allocation for testing
// mi_option_set_enabled(mi_option_disallow_arena_alloc, (n%2)==1);
#endif
#if defined(MI_HEAP_WALK) && defined(MI_USE_HEAPS)
size_t total = 0;
// mi_abandoned_visit_blocks(mi_subproc_main(), -1, true, visit_blocks, &total);
mi_heap_visit_blocks(heap, true, visit_blocks, &total);
#endif
for (int i = 0; i < TRANSFERS; i++) {
if (chance(50, &r) || n + 1 == ITER) { // free all on last run, otherwise free half of the transfers
void* p = atomic_exchange_ptr(&transfers[i], NULL);
free_items(p);
}
}
#if !defined(NDEBUG) || defined(MI_TSAN)
if ((n + 1) % 10 == 0) {
printf("- iterations left: %3d\n", ITER - (n + 1));
#ifndef USE_STD_MALLOC
mi_debug_show_arenas();
#endif
//mi_collect(true);
//mi_debug_show_arenas();
}
#endif
}
#ifndef USE_STD_MALLOC
#ifdef MI_USE_HEAPS
mi_subproc_heap_stats_print_out(mi_subproc_current(),NULL,NULL);
#else
mi_stats_print(NULL);
#endif
#endif
// clean up (a bit too early in order to test if the final `free_items` still works correctly)
#ifdef MI_USE_HEAPS
for (int i = 0; i < MI_USE_HEAPS; i++) {
mi_heap_delete(prev_heaps[i]); prev_heaps[i] = NULL;
}
mi_heap_delete(current_heap); current_heap = NULL;
#endif
for (int i = 0; i < TRANSFERS; i++) {
void* p = atomic_exchange_ptr(&transfers[i], NULL);
if (p != NULL) {
free_items(p);
}
}
}
#if TEST_STRESS_SUBPROCS && !defined(USE_STD_MALLOC)
static mi_subproc_id_t subprocs[NSUBPROCS];
static void test_stress_subproc( intptr_t i, void* arg ) {
(void)arg;
mi_subproc_id_t subproc = subprocs[i];
mi_subproc_add_current_thread(subproc);
test_stress(subproc);
}
static void test_stress_subprocs(void) {
printf(" (for %d subprocesses)\n", NSUBPROCS);
for(int i = 0; i < NSUBPROCS; i++) {
subprocs[i] = mi_subproc_new();
}
run_os_threads(subproc_null, NSUBPROCS, &test_stress_subproc, NULL);
for(int i = 0; i < NSUBPROCS; i++) {
mi_subproc_destroy(subprocs[i]);
}
}
#endif
#if TEST_LEAK
static void leak(intptr_t tid) {
uintptr_t r = rand();
void* p = alloc_items(1 /*pick(&r)%128*/, &r);
if (chance(50, &r)) {
intptr_t i = (pick(&r) % TRANSFERS);
void* q = atomic_exchange_ptr(&transfer[i], p);
free_items(q);
}
}
static void test_leak(void) {
for (int n = 0; n < ITER; n++) {
run_os_threads(subproc_null, THREADS, &leak, NULL);
mi_collect(false);
#ifndef NDEBUG
if ((n + 1) % 10 == 0) { printf("- iterations left: %3d\n", ITER - (n + 1)); }
#endif
}
}
#endif
#if defined(USE_STD_MALLOC) && defined(MI_LINK_VERSION)
#ifdef __cplusplus
extern "C"
#endif
int mi_version(void);
#endif
int main(int argc, char** argv) {
#ifdef MI_LINK_VERSION
mi_version();
#endif
#if !defined(NDEBUG) && !defined(USE_STD_MALLOC)
mi_option_set(mi_option_arena_reserve, (long)(mi_arena_min_size()/1024) /* in KiB ! */);
// mi_option_set(mi_option_purge_delay,1);
#endif
#if defined(NDEBUG) && !defined(USE_STD_MALLOC)
// mi_option_set(mi_option_purge_delay,-1);
mi_option_set(mi_option_page_reclaim_on_free, 0);
#endif
// > mimalloc-test-stress [THREADS] [SCALE] [ITER]
if (argc >= 2) {
char* end;
long n = strtol(argv[1], &end, 10);
if (n > 0) THREADS = n;
}
if (argc >= 3) {
char* end;
long n = (strtol(argv[2], &end, 10));
if (n > 0) SCALE = n;
}
if (argc >= 4) {
char* end;
long n = (strtol(argv[3], &end, 10));
if (n > 0) ITER = n;
}
if (SCALE > 100) {
allow_large_objects = true;
}
printf("Using %d threads with a %d%% load-per-thread and %d iterations%s", THREADS, SCALE, ITER, (allow_large_objects ? " (allow large objects)" : ""));
#if MI_USE_HEAPS
printf(" (using %d rolling heaps)", MI_USE_HEAPS);
#endif
printf("\n"); fflush(stdout);
#if !defined(NDEBUG) && !defined(USE_STD_MALLOC)
mi_stats_reset();
#endif
//mi_reserve_os_memory(1024*1024*1024ULL, false, true);
//int res = mi_reserve_huge_os_pages(4,1);
//printf("(reserve huge: %i\n)", res);
//bench_start_program();
// Run ITER full iterations where half the objects in the transfer buffer survive to the next round.
srand(0x7feb352d);
// mi_stats_reset();
#if TEST_STRESS_SUBPROCS && !defined(USE_STD_MALLOC)
test_stress_subprocs();
#elif TEST_STRESS
test_stress(subproc_null);
#elif TEST_LEAK
test_leak();
#endif
#ifndef USE_STD_MALLOC
#ifndef NDEBUG
mi_collect(true);
mi_debug_show_arenas();
//mi_collect(true);
//char* json = mi_stats_get_json(0, NULL);
//if (json != NULL) {
// fputs(json,stderr);
// mi_free(json);
//}
#endif
// mi_collect(true);
mi_stats_print(NULL);
#endif
//bench_end_program();
return 0;
}
typedef struct callback_s {
thread_entry_fun_t* fun;
intptr_t tid;
void* arg;
mi_subproc_id_t subproc;
} callback_t;
static void* thread_entry(void* param) {
callback_t* cb = (callback_t*)param;
#ifndef USE_STD_MALLOC
if (cb->subproc._mi_subproc_id != NULL) {
mi_subproc_add_current_thread(cb->subproc);
}
#endif
cb->fun(cb->tid,cb->arg);
return NULL;
}
#ifdef _WIN32
#include <windows.h>
static DWORD WINAPI win_thread_entry(LPVOID param) {
thread_entry(param);
return 0;
}
static void run_os_threads(mi_subproc_id_t subproc, size_t nthreads, thread_entry_fun_t* fun, void* arg) {
DWORD* tids = (DWORD*)custom_calloc(nthreads,sizeof(DWORD));
HANDLE* thandles = (HANDLE*)custom_calloc(nthreads,sizeof(HANDLE));
callback_t* callbacks = (callback_t*)custom_calloc(nthreads,sizeof(callback_t));
thandles[0] = GetCurrentThread(); // avoid lint warning
const size_t start = (main_participates ? 1 : 0);
for (size_t i = start; i < nthreads; i++) {
callbacks[i].fun = fun;
callbacks[i].tid = i;
callbacks[i].arg = arg;
callbacks[i].subproc = subproc;
thandles[i] = CreateThread(0, 8*1024L, &win_thread_entry, (void*)&callbacks[i], 0, &tids[i]);
}
if (main_participates) {
fun(0,arg); // run the main thread as well
}
for (size_t i = start; i < nthreads; i++) {
WaitForSingleObject(thandles[i], INFINITE);
}
for (size_t i = start; i < nthreads; i++) {
CloseHandle(thandles[i]);
}
custom_free(callbacks);
custom_free(tids);
custom_free(thandles);
}
static void* atomic_exchange_ptr(volatile void** p, void* newval) {
#if (INTPTR_MAX == INT32_MAX)
return (void*)InterlockedExchange((volatile LONG*)p, (LONG)newval);
#else
return (void*)InterlockedExchange64((volatile LONG64*)p, (LONG64)newval);
#endif
}
#else
#include <pthread.h>
static void run_os_threads(mi_subproc_id_t subproc, size_t nthreads, thread_entry_fun_t* fun, void* arg) {
pthread_t* threads = (pthread_t*)custom_calloc(nthreads,sizeof(pthread_t));
callback_t* callbacks = (callback_t*)custom_calloc(nthreads,sizeof(callback_t));
const size_t start = (main_participates ? 1 : 0);
//pthread_setconcurrency(nthreads);
for (size_t i = start; i < nthreads; i++) {
callbacks[i].fun = fun;
callbacks[i].tid = i;
callbacks[i].arg = arg;
callbacks[i].subproc = subproc;
pthread_create(&threads[i], NULL, &thread_entry, (void*)&callbacks[i]);
}
if (main_participates) {
fun(0,arg); // run the main thread as well
}
for (size_t i = start; i < nthreads; i++) {
pthread_join(threads[i], NULL);
}
custom_free(callbacks);
custom_free(threads);
}
#ifdef __cplusplus
#include <atomic>
static void* atomic_exchange_ptr(volatile void** p, void* newval) {
return std::atomic_exchange((volatile std::atomic<void*>*)p, newval);
}
#else
#include <stdatomic.h>
static void* atomic_exchange_ptr(volatile void** p, void* newval) {
return atomic_exchange((volatile _Atomic(void*)*)p, newval);
}
#endif
#endif
+99
View File
@@ -0,0 +1,99 @@
/* ----------------------------------------------------------------------------
Copyright (c) 2018-2020, Microsoft Research, Daan Leijen
This is free software; you can redistribute it and/or modify it under the
terms of the MIT license. A copy of the license can be found in the file
"LICENSE" at the root of this distribution.
-----------------------------------------------------------------------------*/
/* test file for valgrind/asan support.
VALGRIND:
----------
Compile in an "out/debug" folder:
> cd out/debug
> cmake ../.. -DMI_TRACK_VALGRIND=1
> make -j8
and then compile this file as:
> gcc -g -o test-wrong -I../../include ../../test/test-wrong.c libmimalloc-valgrind-debug.a -lpthread
and test as:
> valgrind ./test-wrong
ASAN
----------
Compile in an "out/debug" folder:
> cd out/debug
> cmake ../.. -DMI_TRACK_ASAN=1
> make -j8
and then compile this file as:
> clang -g -o test-wrong -I../../include ../../test/test-wrong.c libmimalloc-asan-debug.a -lpthread -fsanitize=address -fsanitize-recover=address
and test as:
> ASAN_OPTIONS=verbosity=1:halt_on_error=0 ./test-wrong
*/
#include <stdio.h>
#include <stdlib.h>
#include "mimalloc.h"
#ifdef USE_STD_MALLOC
# define mi(x) x
#else
# define mi(x) mi_##x
#endif
int main(int argc, char** argv) {
(void)(argc);
(void)(argv);
int* p = (int*)mi(malloc)(3*sizeof(int));
p[0] = 1;
int* r = (int*)mi_malloc_aligned(8,16);
mi_free(r);
// illegal byte wise read
char* c = (char*)mi(malloc)(3);
printf("invalid byte: over: %d, under: %d\n", c[4], c[-1]);
mi(free)(c);
// double free
mi(free)(c);
// undefined access
long* q = (long*)mi(malloc)(sizeof(long));
printf("undefined: %ld\n", *q);
// illegal int read
printf("invalid: over: %ld, under: %ld\n", q[1], q[-1]);
*q = 42;
// buffer overflow
q[1] = 43;
q[2] = 44;
// buffer underflow
q[-1] = 41;
mi(free)(q);
// double free
mi(free)(q);
// use after free
printf("use-after-free: %ld\n", *q);
// leak p
// mi_free(p)
return 0;
}
+98
View File
@@ -0,0 +1,98 @@
/* ----------------------------------------------------------------------------
Copyright (c) 2018-2020, Microsoft Research, Daan Leijen
This is free software; you can redistribute it and/or modify it under the
terms of the MIT license. A copy of the license can be found in the file
"LICENSE" at the root of this distribution.
-----------------------------------------------------------------------------*/
#ifndef TESTHELPER_H_
#define TESTHELPER_H_
#include <stdbool.h>
#include <stdio.h>
#include <errno.h>
// ---------------------------------------------------------------------------
// Test macros: CHECK(name,predicate) and CHECK_BODY(name,body)
// ---------------------------------------------------------------------------
static int ok = 0;
static int failed = 0;
static bool check_result(bool result, const char* testname, const char* fname, long lineno) {
if (!(result)) {
failed++;
fprintf(stderr,"\n FAILED: %s: %s:%ld\n", testname, fname, lineno);
/* exit(1); */
}
else {
ok++;
fprintf(stderr, "ok.\n");
}
return true;
}
#define CHECK_BODY(name) \
fprintf(stderr,"test: %s... ", name ); \
errno = 0; \
for(bool done = false, result = true; !done; done = check_result(result,name,__FILE__,__LINE__))
#define CHECK(name,expr) CHECK_BODY(name){ result = (expr); }
// Print summary of test. Return value can be directly use as a return value for main().
static inline int print_test_summary(void)
{
fprintf(stderr,"\n\n---------------------------------------------\n"
"succeeded: %i\n"
"failed : %i\n\n", ok, failed);
return failed;
}
#endif // TESTHELPER_H_
// ------------------------------------------------------
// helper to run on threads
// ------------------------------------------------------
typedef bool (*mi_thread_fun_t)(void);
bool mi_run_on_thread(mi_thread_fun_t fun);
typedef struct mi_thread_fun_args_s {
mi_thread_fun_t fun;
bool result;
} mi_thread_fun_args_t;
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
static DWORD WINAPI mi_win_thread_entry(LPVOID varg) {
mi_thread_fun_args_t* arg = (mi_thread_fun_args_t*)varg;
arg->result = arg->fun();
return 0;
}
bool mi_run_on_thread(mi_thread_fun_t fun) {
mi_thread_fun_args_t arg = { fun, false };
HANDLE thread = CreateThread(NULL, 0, &mi_win_thread_entry, &arg, 0, NULL);
if (thread == NULL) return false;
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
return arg.result;
}
#else
#include <pthread.h>
static void* mi_pthread_entry(void* varg) {
mi_thread_fun_args_t* arg = (mi_thread_fun_args_t*)varg;
arg->result = arg->fun();
return NULL;
}
bool mi_run_on_thread(mi_thread_fun_t fun) {
mi_thread_fun_args_t arg = { fun, false };
pthread_t thread;
if (pthread_create(&thread, NULL, &mi_pthread_entry, &arg) != 0) return false;
pthread_join(thread, NULL);
return arg.result;
}
#endif