From ae47a2b13e2655106ab2b1bc11ef5714dadb4250 Mon Sep 17 00:00:00 2001 From: daan Date: Thu, 27 Jun 2019 13:36:19 -0700 Subject: [PATCH] Add initial testing file, test target, and readme about testing, issue #10, pr #57 --- CMakeLists.txt | 41 ++++-- ide/vs2017/mimalloc-test.vcxproj | 4 +- ide/vs2017/mimalloc-test.vcxproj.filters | 2 +- test/readme.md | 16 +++ test/test-api.c | 175 +++++++++++++++++++++++ 5 files changed, 224 insertions(+), 14 deletions(-) create mode 100644 test/readme.md create mode 100644 test/test-api.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 095fc2c..527f1f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,14 +106,15 @@ else() list(APPEND mi_libraries pthread) endif() +# ----------------------------------------------------------------------------- +# Main targets +# ----------------------------------------------------------------------------- + # shared library add_library(mimalloc SHARED ${mi_sources}) set_target_properties(mimalloc PROPERTIES VERSION ${mi_version} NO_SONAME "YES" OUTPUT_NAME ${mi_basename} ) target_compile_definitions(mimalloc PRIVATE ${mi_defines} MI_SHARED_LIB MI_SHARED_LIB_EXPORT) -if(MI_OVERRIDE MATCHES "ON") - target_compile_definitions(mimalloc PRIVATE MI_MALLOC_OVERRIDE) -endif() target_compile_options(mimalloc PRIVATE ${mi_cflags}) target_include_directories(mimalloc PRIVATE include PUBLIC $) target_link_libraries(mimalloc PUBLIC ${mi_libraries}) @@ -129,10 +130,6 @@ else() set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_basename}) endif() target_compile_definitions(mimalloc-static PRIVATE ${mi_defines} MI_STATIC_LIB) -if(NOT WIN32 AND MI_OVERRIDE MATCHES "ON") - # It is only possible to override malloc on Windows when building as a DLL. (src/alloc-override.c) - target_compile_definitions(mimalloc-static PRIVATE MI_MALLOC_OVERRIDE) -endif() target_compile_options(mimalloc-static PRIVATE ${mi_cflags}) target_include_directories(mimalloc-static PRIVATE include PUBLIC $) target_link_libraries(mimalloc-static PUBLIC ${mi_libraries}) @@ -149,13 +146,35 @@ install(FILES "$" DESTINATION lib) # duplicate the .so in # single object file for more predictable static overriding add_library(mimalloc-obj OBJECT src/static.c) target_compile_definitions(mimalloc-obj PRIVATE ${mi_defines}) -if(NOT WIN32 AND MI_OVERRIDE MATCHES "ON") - # It is only possible to override malloc on Windows when building as a DLL. (src/alloc-override.c) - target_compile_definitions(mimalloc-obj PRIVATE MI_MALLOC_OVERRIDE) -endif() target_compile_options(mimalloc-obj PRIVATE ${mi_cflags}) target_include_directories(mimalloc-obj PRIVATE include PUBLIC $) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/mimalloc-obj.dir/src/static.c${CMAKE_C_OUTPUT_EXTENSION} DESTINATION ${mi_install_dir} RENAME ${mi_basename}${CMAKE_C_OUTPUT_EXTENSION} ) + +# ----------------------------------------------------------------------------- +# API surface testing +# ----------------------------------------------------------------------------- +add_executable(mimalloc-test test/test-api.c) +target_compile_definitions(mimalloc-test PRIVATE ${mi_defines}) +target_compile_options(mimalloc-test PRIVATE ${mi_cflags}) +target_include_directories(mimalloc-test PRIVATE include) +target_link_libraries(mimalloc-test PRIVATE mimalloc-static) + +enable_testing() +add_test(test_api, mimalloc-test) + + +# ----------------------------------------------------------------------------- +# Set override properties +# ----------------------------------------------------------------------------- +if (MI_OVERRIDE MATCHES "ON") + target_compile_definitions(mimalloc PRIVATE MI_MALLOC_OVERRIDE) + if(NOT WIN32) + # It is only possible to override malloc on Windows when building as a DLL. (src/alloc-override.c) + target_compile_definitions(mimalloc-static PRIVATE MI_MALLOC_OVERRIDE) + target_compile_definitions(mimalloc-obj PRIVATE MI_MALLOC_OVERRIDE) + target_compile_definitions(mimalloc-test PRIVATE MI_MALLOC_OVERRIDE) + endif() +endif() diff --git a/ide/vs2017/mimalloc-test.vcxproj b/ide/vs2017/mimalloc-test.vcxproj index 5d33884..16d4dc1 100644 --- a/ide/vs2017/mimalloc-test.vcxproj +++ b/ide/vs2017/mimalloc-test.vcxproj @@ -145,7 +145,7 @@ - + @@ -155,4 +155,4 @@ - + \ No newline at end of file diff --git a/ide/vs2017/mimalloc-test.vcxproj.filters b/ide/vs2017/mimalloc-test.vcxproj.filters index 54163be..9254f6c 100644 --- a/ide/vs2017/mimalloc-test.vcxproj.filters +++ b/ide/vs2017/mimalloc-test.vcxproj.filters @@ -15,7 +15,7 @@ - + Source Files diff --git a/test/readme.md b/test/readme.md new file mode 100644 index 0000000..b74364f --- /dev/null +++ b/test/readme.md @@ -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_CHECK_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 diff --git a/test/test-api.c b/test/test-api.c new file mode 100644 index 0000000..710cb2a --- /dev/null +++ b/test/test-api.c @@ -0,0 +1,175 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2018, 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. +-----------------------------------------------------------------------------*/ + +/* +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_CHECK_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 +#include +#include +#include +#include +#include "mimalloc.h" +#include "mimalloc-internal.h" + + +// --------------------------------------------------------------------------- +// Test macros: CHECK(name,predicate) and CHECK_BODY(name,body) +// --------------------------------------------------------------------------- +static int ok = 0; +static int failed = 0; + +#define CHECK_BODY(name,body) \ + do { \ + fprintf(stderr,"test: %s... ", name ); \ + bool result = true; \ + do { body } while(false); \ + if (!(result)) { \ + failed++; \ + fprintf(stderr, \ + "\n FAILED: %s:%d:\n %s\n", \ + __FILE__, \ + __LINE__, \ + #body); \ + /* exit(1); */ \ + } \ + else { \ + ok++; \ + fprintf(stderr,"ok.\n"); \ + } \ + } while (false) + +#define CHECK(name,expr) CHECK_BODY(name,{ result = (expr); }) + +// --------------------------------------------------------------------------- +// Test functions +// --------------------------------------------------------------------------- +bool test_heap1(); +bool test_heap2(); + +// --------------------------------------------------------------------------- +// Main testing +// --------------------------------------------------------------------------- +int main() { + mi_option_enable(mi_option_verbose,false); + + // --------------------------------------------------- + // Malloc + // --------------------------------------------------- + + CHECK_BODY("malloc-zero",{ + void* p = mi_malloc(0); mi_free(p); + }); + CHECK_BODY("malloc-nomem1",{ + result = (mi_malloc(SIZE_MAX/2) == NULL); + }); + CHECK_BODY("malloc-null",{ + mi_free(NULL); + }); + + // --------------------------------------------------- + // Extended + // --------------------------------------------------- + #if defined(MI_MALLOC_OVERRIDE) && !defined(_WIN32) + CHECK_BODY("posix_memalign1", { + void* p = &main; + int err = posix_memalign(&p, sizeof(void*), 32); + mi_assert((err==0 && (uintptr_t)p % sizeof(void*) == 0) || p==&main); + mi_free(p); + result = (err==0); + }); + CHECK_BODY("posix_memalign_no_align", { + void* p = &main; + int err = posix_memalign(&p, 3, 32); + mi_assert(p==&main); + result = (err==EINVAL); + }); + CHECK_BODY("posix_memalign_zero", { + void* p = &main; + int err = posix_memalign(&p, sizeof(void*), 0); + mi_free(p); + result = (err==0); + }); + CHECK_BODY("posix_memalign_nopow2", { + void* p = &main; + int err = posix_memalign(&p, 3*sizeof(void*), 32); + result = (err==EINVAL && p==&main); + }); + CHECK_BODY("posix_memalign_nomem", { + void* p = &main; + int err = posix_memalign(&p, sizeof(void*), SIZE_MAX); + result = (err==ENOMEM && p==&main); + }); + #endif + + // --------------------------------------------------- + // Aligned API + // --------------------------------------------------- + CHECK_BODY("malloc-aligned1", { + void* p = mi_malloc_aligned(32,24); result = (p != NULL && (uintptr_t)(p) % 24 == 0); mi_free(p); + }); + CHECK_BODY("malloc-aligned2", { + void* p = mi_malloc_aligned(8,24); result = (p != NULL && (uintptr_t)(p) % 24 == 0); mi_free(p); + }); + CHECK_BODY("malloc-aligned-at1", { + void* p = mi_malloc_aligned_at(8,24,0); result = (p != NULL && ((uintptr_t)(p) + 0) % 24 == 0); mi_free(p); + }); + CHECK_BODY("malloc-aligned-at2", { + void* p = mi_malloc_aligned_at(5,24,8); result = (p != NULL && ((uintptr_t)(p) + 8) % 24 == 0); mi_free(p); + }); + + // --------------------------------------------------- + // Heaps + // --------------------------------------------------- + CHECK("heap_destroy", test_heap1()); + CHECK("heap_delete", test_heap2()); + + + // --------------------------------------------------- + // Done + // ---------------------------------------------------[] + fprintf(stderr,"\n\n---------------------------------------------\n" + "succeeded: %i\n" + "failed : %i\n\n", ok, failed); + return failed; +} + +// --------------------------------------------------- +// Larger test functions +// --------------------------------------------------- + +bool test_heap1() { + mi_heap_t* heap = mi_heap_new(); + int* p1 = mi_heap_malloc_tp(heap,int); + int* p2 = mi_heap_malloc_tp(heap,int); + *p1 = *p2 = 43; + mi_heap_destroy(heap); + return true; +} + +bool test_heap2() { + mi_heap_t* heap = mi_heap_new(); + int* p1 = mi_heap_malloc_tp(heap,int); + int* p2 = mi_heap_malloc_tp(heap,int); + mi_heap_delete(heap); + *p1 = 42; + mi_free(p1); + mi_free(p2); + return true; +}