初始化内容
This commit is contained in:
Vendored
+56
@@ -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
@@ -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
@@ -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
@@ -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, ¤t_rss, &peak_rss, ¤t_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
|
||||
Vendored
+49
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
Vendored
+44
@@ -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;
|
||||
}
|
||||
Vendored
+46
@@ -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;
|
||||
}
|
||||
Vendored
+16
@@ -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
|
||||
Vendored
+343
@@ -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
|
||||
Vendored
+692
@@ -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
@@ -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
@@ -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"
|
||||
Vendored
+578
@@ -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
|
||||
Vendored
+99
@@ -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;
|
||||
}
|
||||
Vendored
+98
@@ -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
|
||||
Reference in New Issue
Block a user