Add test helper macros. (#465)
- Refactor the existing SNMALLOC_ASSERT and SNMALLOC_CHECK. These now use the FatalErrorBuilder to format the output if a format string is provided. - Extend the FatalErrorBuilder to print decimal integers for signed values. - Rename FatalErrorBuilder to MessageBuilder. - Rewrite the macros used in the jemalloc tests to use FatalErrorBuilder and move them into a header. - Refactor some of the tests to use the new macros.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
// 28 is FAST_FAIL_INVALID_BUFFER_ACCESS. Not using the symbolic constant to
|
||||
@@ -109,27 +110,40 @@ namespace snmalloc
|
||||
#define TOSTRING2(expr) #expr
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define SNMALLOC_ASSERT(expr) \
|
||||
# define SNMALLOC_ASSERT_MSG(...) \
|
||||
{}
|
||||
#else
|
||||
# define SNMALLOC_ASSERT(expr) \
|
||||
# define SNMALLOC_ASSERT_MSG(expr, fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (!(expr)) \
|
||||
{ \
|
||||
snmalloc::error("assert fail: " #expr " in " __FILE__ \
|
||||
" on " TOSTRING(__LINE__)); \
|
||||
snmalloc::report_fatal_error( \
|
||||
"assert fail: {} in {} on {} " fmt "\n", \
|
||||
#expr, \
|
||||
__FILE__, \
|
||||
TOSTRING(__LINE__), \
|
||||
##__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
} while (0)
|
||||
#endif
|
||||
#define SNMALLOC_ASSERT(expr) SNMALLOC_ASSERT_MSG(expr, "")
|
||||
|
||||
#define SNMALLOC_CHECK(expr) \
|
||||
#define SNMALLOC_CHECK_MSG(expr, fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (!(expr)) \
|
||||
{ \
|
||||
snmalloc::error("Check fail: " #expr " in " __FILE__ \
|
||||
" on " TOSTRING(__LINE__)); \
|
||||
snmalloc::report_fatal_error( \
|
||||
"Check fail: {} in {} on {} " fmt "\n", \
|
||||
#expr, \
|
||||
__FILE__, \
|
||||
TOSTRING(__LINE__), \
|
||||
##__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
} while (0)
|
||||
|
||||
#define SNMALLOC_CHECK(expr) SNMALLOC_CHECK_MSG(expr, "")
|
||||
|
||||
#ifndef NDEBUG
|
||||
# define SNMALLOC_ASSUME(x) SNMALLOC_ASSERT(x)
|
||||
@@ -184,6 +198,13 @@ namespace snmalloc
|
||||
#else
|
||||
static constexpr bool CHECK_CLIENT = false;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Forward declaration so that this can be called before the pal header is
|
||||
* included.
|
||||
*/
|
||||
template<size_t BufferSize = 1024, typename... Args>
|
||||
[[noreturn]] inline void report_fatal_error(Args... args);
|
||||
} // namespace snmalloc
|
||||
|
||||
#ifdef SNMALLOC_CHECK_CLIENT
|
||||
|
||||
@@ -256,7 +256,7 @@ namespace snmalloc
|
||||
* build an on-stack buffer containing the formatted string.
|
||||
*/
|
||||
template<size_t BufferSize>
|
||||
class FatalErrorBuilder
|
||||
class MessageBuilder
|
||||
{
|
||||
/**
|
||||
* The buffer that is used to store the formatted output.
|
||||
@@ -322,17 +322,51 @@ namespace snmalloc
|
||||
*/
|
||||
void append(void* ptr)
|
||||
{
|
||||
append(static_cast<size_t>(reinterpret_cast<uintptr_t>(ptr)));
|
||||
append(static_cast<unsigned long long>(reinterpret_cast<uintptr_t>(ptr)));
|
||||
// TODO: CHERI bits.
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a signed integer to the buffer, as a decimal string.
|
||||
*/
|
||||
void append(long long s)
|
||||
{
|
||||
if (s < 0)
|
||||
{
|
||||
append_char('-');
|
||||
s = 0 - s;
|
||||
}
|
||||
std::array<char, 20> buf;
|
||||
const char digits[] = "0123456789";
|
||||
for (long i = long(buf.size() - 1); i >= 0; i--)
|
||||
{
|
||||
buf[static_cast<size_t>(i)] = digits[s % 10];
|
||||
s /= 10;
|
||||
}
|
||||
bool skipZero = true;
|
||||
for (auto c : buf)
|
||||
{
|
||||
if (skipZero && (c == '0'))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
skipZero = false;
|
||||
append_char(c);
|
||||
}
|
||||
if (skipZero)
|
||||
{
|
||||
append_char('0');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a size to the buffer, as a hex string.
|
||||
*/
|
||||
void append(size_t s)
|
||||
void append(unsigned long long s)
|
||||
{
|
||||
append_char('0');
|
||||
append_char('x');
|
||||
std::array<char, sizeof(size_t) * 2> buf;
|
||||
std::array<char, 16> buf;
|
||||
const char hexdigits[] = "0123456789abcdef";
|
||||
// Length of string including null terminator
|
||||
static_assert(sizeof(hexdigits) == 0x11);
|
||||
@@ -357,12 +391,44 @@ namespace snmalloc
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload to force `long` to be promoted to `long long`.
|
||||
*/
|
||||
void append(long x)
|
||||
{
|
||||
append(static_cast<long long>(x));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload to force `unsigned long` to be promoted to `unsigned long long`.
|
||||
*/
|
||||
void append(unsigned long x)
|
||||
{
|
||||
append(static_cast<unsigned long long>(x));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload to force `int` to be promoted to `long long`.
|
||||
*/
|
||||
void append(int x)
|
||||
{
|
||||
append(static_cast<long long>(x));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload to force `unsigned int` to be promoted to `unsigned long long`.
|
||||
*/
|
||||
void append(unsigned int x)
|
||||
{
|
||||
append(static_cast<unsigned long long>(x));
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor. Takes a format string and the arguments to output.
|
||||
*/
|
||||
template<typename... Args>
|
||||
SNMALLOC_FAST_PATH FatalErrorBuilder(const char* fmt, Args... args)
|
||||
SNMALLOC_FAST_PATH MessageBuilder(const char* fmt, Args... args)
|
||||
{
|
||||
buffer[SafeLength] = 0;
|
||||
size_t arg = 0;
|
||||
@@ -382,6 +448,21 @@ namespace snmalloc
|
||||
append_char('\0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for trivial format strings (no arguments). This exists to
|
||||
* allow `MessageBuilder` to be used with macros without special casing
|
||||
* the single-argument version.
|
||||
*/
|
||||
SNMALLOC_FAST_PATH MessageBuilder(const char* fmt)
|
||||
{
|
||||
buffer[SafeLength] = 0;
|
||||
for (const char* s = fmt; *s != 0; ++s)
|
||||
{
|
||||
append_char(*s);
|
||||
}
|
||||
append_char('\0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the error buffer.
|
||||
*/
|
||||
|
||||
@@ -161,10 +161,10 @@ namespace snmalloc
|
||||
*
|
||||
* These types should be sufficient for allocator-related error messages.
|
||||
*/
|
||||
template<size_t BufferSize = 1024, typename... Args>
|
||||
template<size_t BufferSize, typename... Args>
|
||||
[[noreturn]] inline void report_fatal_error(Args... args)
|
||||
{
|
||||
FatalErrorBuilder<BufferSize> msg{std::forward<Args>(args)...};
|
||||
MessageBuilder<BufferSize> msg{std::forward<Args>(args)...};
|
||||
Pal::error(msg.get_message());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <functional>
|
||||
#include <stdio.h>
|
||||
#include <test/helpers.h>
|
||||
#include <test/setup.h>
|
||||
|
||||
#define SNMALLOC_NAME_MANGLE(a) our_##a
|
||||
@@ -59,10 +60,6 @@
|
||||
# define ALLOCM_ERR_NOT_MOVED OUR_ALLOCM_ERR_NOT_MOVED
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define __PRETTY_FUNCTION__ __FUNCSIG__
|
||||
#endif
|
||||
|
||||
using namespace snmalloc;
|
||||
using namespace snmalloc::bits;
|
||||
|
||||
@@ -91,37 +88,6 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the function under test. This is set in the START_TEST macro
|
||||
* and used for error reporting in EXPECT.
|
||||
*/
|
||||
const char* function = nullptr;
|
||||
|
||||
/**
|
||||
* Log that the test started.
|
||||
*/
|
||||
#define START_TEST(msg) \
|
||||
function = __PRETTY_FUNCTION__; \
|
||||
fprintf(stderr, "Starting test: " msg "\n");
|
||||
|
||||
/**
|
||||
* An assertion that fires even in debug builds. Uses the value set by
|
||||
* START_TEST.
|
||||
*/
|
||||
#define EXPECT(x, msg, ...) \
|
||||
if (!(x)) \
|
||||
{ \
|
||||
fprintf( \
|
||||
stderr, \
|
||||
"%s:%d in %s: " msg "\n", \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
function, \
|
||||
##__VA_ARGS__); \
|
||||
fflush(stderr); \
|
||||
abort(); \
|
||||
}
|
||||
|
||||
/**
|
||||
* The default maximum number of bits of address space to use for tests.
|
||||
* This is clamped on platforms without lazy commit because this much RAM
|
||||
@@ -142,7 +108,7 @@ namespace
|
||||
constexpr size_t low = 5;
|
||||
for (size_t base = low; base < Log2MaxSize; base++)
|
||||
{
|
||||
fprintf(stderr, "\tTrying 0x%zx-byte allocations\n", one_at_bit(base));
|
||||
INFO("\tTrying {}-byte allocations\n", one_at_bit(base));
|
||||
for (size_t i = 0; i < one_at_bit(low); i++)
|
||||
{
|
||||
for (int align = 1; align < 20; align++)
|
||||
@@ -171,13 +137,13 @@ namespace
|
||||
void* ptr = Mallocx(size, flags);
|
||||
EXPECT(
|
||||
ptr != nullptr,
|
||||
"Failed to allocate 0x%zx bytes with %d bit alignment",
|
||||
"Failed to allocate {} bytes with {}-bit alignment",
|
||||
size,
|
||||
align);
|
||||
size_t allocated = Sallocx(ptr, 0);
|
||||
EXPECT(
|
||||
allocated == expected,
|
||||
"Expected to have allocated 0x%zx bytes, got 0x%zx bytes",
|
||||
"Expected to have allocated {} bytes, got {} bytes",
|
||||
expected,
|
||||
allocated);
|
||||
Dallocx(ptr, 0);
|
||||
@@ -202,11 +168,11 @@ namespace
|
||||
ptr = static_cast<char*>(Rallocx(ptr, size * 2, flags));
|
||||
EXPECT(
|
||||
ptr != nullptr,
|
||||
"Failed to reallocate for 0x%zx byte allocation",
|
||||
"Failed to reallocate for {} byte allocation",
|
||||
size * 2);
|
||||
EXPECT(
|
||||
ptr[size] == 0,
|
||||
"Memory not zero initialised for 0x%zx byte reallocation from 0x%zx "
|
||||
"Memory not zero initialised for {} byte reallocation from {} "
|
||||
"byte allocation",
|
||||
size * 2,
|
||||
size);
|
||||
@@ -234,11 +200,9 @@ namespace
|
||||
auto test = [](size_t size, int align) {
|
||||
int flags = MALLOCX_LG_ALIGN(align);
|
||||
void* ptr = Mallocx(size, flags);
|
||||
EXPECT(
|
||||
ptr != nullptr, "Failed to allocate for 0x%zx byte allocation", size);
|
||||
EXPECT(ptr != nullptr, "Failed to allocate for zx byte allocation", size);
|
||||
size_t sz = Xallocx(ptr, size, 1024, flags);
|
||||
EXPECT(
|
||||
sz >= size, "xalloc returned 0x%zx, expected at least 0x%zx", sz, size);
|
||||
EXPECT(sz >= size, "xalloc returned {}, expected at least {}", sz, size);
|
||||
Dallocx(ptr, 0);
|
||||
};
|
||||
test_sizes_and_alignments(test);
|
||||
@@ -258,7 +222,7 @@ namespace
|
||||
int ret = Nallocm(&expected, size, flags);
|
||||
EXPECT(
|
||||
(ret == ALLOCM_SUCCESS),
|
||||
"nallocm(%zx, %d) failed with error %d",
|
||||
"nallocm({}, {}) failed with error {}",
|
||||
size,
|
||||
flags,
|
||||
ret);
|
||||
@@ -267,18 +231,18 @@ namespace
|
||||
ret = Allocm(&ptr, &allocated, size, flags);
|
||||
EXPECT(
|
||||
(ptr != nullptr) && (ret == ALLOCM_SUCCESS),
|
||||
"Failed to allocate 0x%zx bytes with %d bit alignment",
|
||||
"Failed to allocate {} bytes with {} bit alignment",
|
||||
size,
|
||||
align);
|
||||
EXPECT(
|
||||
allocated == expected,
|
||||
"Expected to have allocated 0x%zx bytes, got 0x%zx bytes",
|
||||
"Expected to have allocated {} bytes, got {} bytes",
|
||||
expected,
|
||||
allocated);
|
||||
ret = Sallocm(ptr, &expected, 0);
|
||||
EXPECT(
|
||||
(ret == ALLOCM_SUCCESS) && (allocated == expected),
|
||||
"Expected to have allocated 0x%zx bytes, got 0x%zx bytes",
|
||||
"Expected to have allocated {} bytes, got {} bytes",
|
||||
expected,
|
||||
allocated);
|
||||
|
||||
@@ -301,14 +265,14 @@ namespace
|
||||
void* orig = ptr;
|
||||
EXPECT(
|
||||
(ptr != nullptr) && (ret == ALLOCM_SUCCESS),
|
||||
"Failed to allocate 0x%zx bytes with %d bit alignment",
|
||||
"Failed to allocate {} bytes with {} bit alignment",
|
||||
size,
|
||||
align);
|
||||
ret = Rallocm(&ptr, nullptr, allocated + 1, 12, flags | ALLOCM_NO_MOVE);
|
||||
EXPECT(
|
||||
(ret == ALLOCM_ERR_NOT_MOVED) || (ptr == orig),
|
||||
"Expected rallocm not to be able to move or reallocate, but return was "
|
||||
"%d\n",
|
||||
"{}\n",
|
||||
ret);
|
||||
Dallocm(ptr, 0);
|
||||
});
|
||||
@@ -327,8 +291,8 @@ namespace
|
||||
int ret = Allocm(&ptr, nullptr, std::numeric_limits<size_t>::max() / 2, 0);
|
||||
EXPECT(
|
||||
(ptr == nullptr) && (ret == OUR_ALLOCM_ERR_OOM),
|
||||
"Expected massive allocation to fail with out of memory (%d), received "
|
||||
"allocation %p, return code %d",
|
||||
"Expected massive allocation to fail with out of memory ({}), received "
|
||||
"allocation {}, return code {}",
|
||||
OUR_ALLOCM_ERR_OOM,
|
||||
ptr,
|
||||
ret);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <stdio.h>
|
||||
#include <test/helpers.h>
|
||||
#include <test/setup.h>
|
||||
|
||||
#define SNMALLOC_NAME_MANGLE(a) our_##a
|
||||
@@ -14,26 +15,20 @@ constexpr int SUCCESS = 0;
|
||||
void check_result(size_t size, size_t align, void* p, int err, bool null)
|
||||
{
|
||||
bool failed = false;
|
||||
if (errno != err && err != SUCCESS)
|
||||
{
|
||||
// Note: successful calls are allowed to spuriously set errno
|
||||
printf("Expected error: %d but got %d\n", err, errno);
|
||||
abort();
|
||||
}
|
||||
|
||||
EXPECT(
|
||||
(errno == err) || (err == SUCCESS),
|
||||
"Expected error: {} but got {}",
|
||||
err,
|
||||
errno);
|
||||
if (null)
|
||||
{
|
||||
if (p != nullptr)
|
||||
{
|
||||
printf("Expected null, and got non-null return!\n");
|
||||
abort();
|
||||
}
|
||||
EXPECT(p == nullptr, "Expected null but got {}", p);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((p == nullptr) && (size != 0))
|
||||
{
|
||||
printf("Unexpected null returned.\n");
|
||||
INFO("Unexpected null returned.\n");
|
||||
failed = true;
|
||||
}
|
||||
const auto alloc_size = our_malloc_usable_size(p);
|
||||
@@ -57,8 +52,7 @@ void check_result(size_t size, size_t align, void* p, int err, bool null)
|
||||
const auto cheri_size = __builtin_cheri_length_get(p);
|
||||
if (cheri_size != alloc_size && (size != 0))
|
||||
{
|
||||
printf(
|
||||
"Cheri size is %zu, but required to be %zu.\n", cheri_size, alloc_size);
|
||||
INFO("Cheri size is {}, but required to be {}.", cheri_size, alloc_size);
|
||||
failed = true;
|
||||
}
|
||||
if (p != nullptr)
|
||||
@@ -82,16 +76,14 @@ void check_result(size_t size, size_t align, void* p, int err, bool null)
|
||||
#endif
|
||||
if (exact_size && (alloc_size != expected_size) && (size != 0))
|
||||
{
|
||||
printf(
|
||||
"Usable size is %zu, but required to be %zu.\n",
|
||||
alloc_size,
|
||||
expected_size);
|
||||
INFO(
|
||||
"Usable size is {}, but required to be {}.", alloc_size, expected_size);
|
||||
failed = true;
|
||||
}
|
||||
if ((!exact_size) && (alloc_size < expected_size))
|
||||
{
|
||||
printf(
|
||||
"Usable size is %zu, but required to be at least %zu.\n",
|
||||
INFO(
|
||||
"Usable size is {}, but required to be at least {}.",
|
||||
alloc_size,
|
||||
expected_size);
|
||||
failed = true;
|
||||
@@ -100,34 +92,27 @@ void check_result(size_t size, size_t align, void* p, int err, bool null)
|
||||
(static_cast<size_t>(reinterpret_cast<uintptr_t>(p) % align) != 0) &&
|
||||
(size != 0))
|
||||
{
|
||||
printf(
|
||||
"Address is 0x%zx, but required to be aligned to 0x%zx.\n",
|
||||
reinterpret_cast<size_t>(p),
|
||||
align);
|
||||
INFO("Address is {}, but required to be aligned to {}.\n", p, align);
|
||||
failed = true;
|
||||
}
|
||||
if (
|
||||
static_cast<size_t>(
|
||||
reinterpret_cast<uintptr_t>(p) % natural_alignment(size)) != 0)
|
||||
{
|
||||
printf(
|
||||
"Address is 0x%zx, but should have natural alignment to 0x%zx.\n",
|
||||
reinterpret_cast<size_t>(p),
|
||||
INFO(
|
||||
"Address is {}, but should have natural alignment to {}.\n",
|
||||
p,
|
||||
natural_alignment(size));
|
||||
failed = true;
|
||||
}
|
||||
|
||||
if (failed)
|
||||
{
|
||||
printf("check_result failed! %p", p);
|
||||
abort();
|
||||
}
|
||||
EXPECT(!failed, "check_result failed! {}", p);
|
||||
our_free(p);
|
||||
}
|
||||
|
||||
void test_calloc(size_t nmemb, size_t size, int err, bool null)
|
||||
{
|
||||
printf("calloc(%zu, %zu) combined size %zu\n", nmemb, size, nmemb * size);
|
||||
START_TEST("calloc({}, {}) combined size {}\n", nmemb, size, nmemb * size);
|
||||
errno = SUCCESS;
|
||||
void* p = our_calloc(nmemb, size);
|
||||
|
||||
@@ -135,11 +120,7 @@ void test_calloc(size_t nmemb, size_t size, int err, bool null)
|
||||
{
|
||||
for (size_t i = 0; i < (size * nmemb); i++)
|
||||
{
|
||||
if (((uint8_t*)p)[i] != 0)
|
||||
{
|
||||
printf("non-zero at @%zu\n", i);
|
||||
abort();
|
||||
}
|
||||
EXPECT(((uint8_t*)p)[i] == 0, "non-zero at {}", i);
|
||||
}
|
||||
}
|
||||
check_result(nmemb * size, 1, p, err, null);
|
||||
@@ -151,7 +132,7 @@ void test_realloc(void* p, size_t size, int err, bool null)
|
||||
if (p != nullptr)
|
||||
old_size = our_malloc_usable_size(p);
|
||||
|
||||
printf("realloc(%p(%zu), %zu)\n", p, old_size, size);
|
||||
START_TEST("realloc({}({}), {})", p, old_size, size);
|
||||
errno = SUCCESS;
|
||||
auto new_p = our_realloc(p, size);
|
||||
// Realloc failure case, deallocate original block
|
||||
@@ -162,7 +143,7 @@ void test_realloc(void* p, size_t size, int err, bool null)
|
||||
|
||||
void test_posix_memalign(size_t size, size_t align, int err, bool null)
|
||||
{
|
||||
printf("posix_memalign(&p, %zu, %zu)\n", align, size);
|
||||
START_TEST("posix_memalign(&p, {}, {})", align, size);
|
||||
void* p = nullptr;
|
||||
errno = our_posix_memalign(&p, align, size);
|
||||
check_result(size, align, p, err, null);
|
||||
@@ -170,7 +151,7 @@ void test_posix_memalign(size_t size, size_t align, int err, bool null)
|
||||
|
||||
void test_memalign(size_t size, size_t align, int err, bool null)
|
||||
{
|
||||
printf("memalign(%zu, %zu)\n", align, size);
|
||||
START_TEST("memalign({}, {})", align, size);
|
||||
errno = SUCCESS;
|
||||
void* p = our_memalign(align, size);
|
||||
check_result(size, align, p, err, null);
|
||||
@@ -183,7 +164,7 @@ void test_reallocarray(void* p, size_t nmemb, size_t size, int err, bool null)
|
||||
if (p != nullptr)
|
||||
old_size = our_malloc_usable_size(p);
|
||||
|
||||
printf("reallocarray(%p(%zu), %zu)\n", p, old_size, tsize);
|
||||
START_TEST("reallocarray({}({}), {})", p, old_size, tsize);
|
||||
errno = SUCCESS;
|
||||
auto new_p = our_reallocarray(p, nmemb, size);
|
||||
if (new_p == nullptr && tsize != 0)
|
||||
@@ -198,15 +179,11 @@ void test_reallocarr(
|
||||
|
||||
if (size_old != (size_t)~0)
|
||||
p = our_malloc(size_old);
|
||||
START_TEST("reallocarr({}({}), {})", p, nmemb, size);
|
||||
errno = SUCCESS;
|
||||
int r = our_reallocarr(&p, nmemb, size);
|
||||
if (r != err)
|
||||
{
|
||||
printf("reallocarr failed! expected %d got %d\n", err, r);
|
||||
abort();
|
||||
}
|
||||
EXPECT(r == err, "reallocarr failed! expected {} got {}\n", err, r);
|
||||
|
||||
printf("reallocarr(%p(%zu), %zu)\n", p, nmemb, size);
|
||||
check_result(nmemb * size, 1, p, err, null);
|
||||
p = our_malloc(size);
|
||||
if (!p)
|
||||
@@ -221,11 +198,7 @@ void test_reallocarr(
|
||||
|
||||
for (size_t i = 1; i < size; i++)
|
||||
{
|
||||
if (static_cast<char*>(p)[i] != 1)
|
||||
{
|
||||
printf("data consistency failed! at %zu", i);
|
||||
abort();
|
||||
}
|
||||
EXPECT(static_cast<char*>(p)[i] == 1, "data consistency failed! at {}", i);
|
||||
}
|
||||
our_free(p);
|
||||
}
|
||||
@@ -239,16 +212,23 @@ int main(int argc, char** argv)
|
||||
|
||||
// Smoke test the fatal error builder. Check that it can generate strings
|
||||
// including all of the kinds of things that it expects to be able to format.
|
||||
//
|
||||
// Note: We cannot use the check or assert macros here because they depend on
|
||||
// `MessageBuilder` working. They are safe to use in any other test.
|
||||
void* fakeptr = reinterpret_cast<void*>(static_cast<uintptr_t>(0x42));
|
||||
FatalErrorBuilder<1024> b{
|
||||
"testing pointer {} size_t {} message, {} world, null is {}",
|
||||
MessageBuilder<1024> b{
|
||||
"testing pointer {} size_t {} message, {} world, null is {}, -123456 is "
|
||||
"{}, 1234567 is {}",
|
||||
fakeptr,
|
||||
size_t(42),
|
||||
"hello",
|
||||
nullptr};
|
||||
nullptr,
|
||||
-123456,
|
||||
1234567};
|
||||
if (
|
||||
strcmp(
|
||||
"testing pointer 0x42 size_t 0x2a message, hello world, null is 0x0",
|
||||
"testing pointer 0x42 size_t 0x2a message, hello world, null is 0x0, "
|
||||
"-123456 is -123456, 1234567 is 1234567",
|
||||
b.get_message()) != 0)
|
||||
{
|
||||
printf("Incorrect rendering of fatal error message: %s\n", b.get_message());
|
||||
@@ -265,7 +245,7 @@ int main(int argc, char** argv)
|
||||
for (smallsizeclass_t sc = 0; sc < (MAX_SMALL_SIZECLASS_BITS + 4); sc++)
|
||||
{
|
||||
const size_t size = bits::one_at_bit(sc);
|
||||
printf("malloc: %zu\n", size);
|
||||
START_TEST("malloc: {}", size);
|
||||
errno = SUCCESS;
|
||||
check_result(size, 1, our_malloc(size), SUCCESS, false);
|
||||
errno = SUCCESS;
|
||||
@@ -320,7 +300,7 @@ int main(int argc, char** argv)
|
||||
for (smallsizeclass_t sc2 = 0; sc2 < (MAX_SMALL_SIZECLASS_BITS + 4); sc2++)
|
||||
{
|
||||
const size_t size2 = bits::one_at_bit(sc2);
|
||||
printf("size1: %zu, size2:%zu\n", size, size2);
|
||||
INFO("size1: {}, size2:{}\n", size, size2);
|
||||
test_realloc(our_malloc(size), size2, SUCCESS, false);
|
||||
test_realloc(our_malloc(size + 1), size2, SUCCESS, false);
|
||||
}
|
||||
@@ -373,32 +353,22 @@ int main(int argc, char** argv)
|
||||
test_reallocarr(size, 1, 0, SUCCESS, false);
|
||||
test_reallocarr(size, 2, size, SUCCESS, false);
|
||||
void* p = our_malloc(size);
|
||||
if (p == nullptr)
|
||||
{
|
||||
printf("realloc alloc failed with %zu\n", size);
|
||||
abort();
|
||||
}
|
||||
EXPECT(p != nullptr, "realloc alloc failed with {}", size);
|
||||
int r = our_reallocarr(&p, 1, too_big_size);
|
||||
if (r != ENOMEM)
|
||||
{
|
||||
printf("expected failure on allocation\n");
|
||||
abort();
|
||||
}
|
||||
EXPECT(r == ENOMEM, "expected failure on allocation\n");
|
||||
our_free(p);
|
||||
|
||||
for (smallsizeclass_t sc2 = 0; sc2 < (MAX_SMALL_SIZECLASS_BITS + 4); sc2++)
|
||||
{
|
||||
const size_t size2 = bits::one_at_bit(sc2);
|
||||
printf("size1: %zu, size2:%zu\n", size, size2);
|
||||
START_TEST("size1: {}, size2:{}", size, size2);
|
||||
test_reallocarr(size, 1, size2, SUCCESS, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (our_malloc_usable_size(nullptr) != 0)
|
||||
{
|
||||
printf("malloc_usable_size(nullptr) should be zero");
|
||||
abort();
|
||||
}
|
||||
EXPECT(
|
||||
our_malloc_usable_size(nullptr) == 0,
|
||||
"malloc_usable_size(nullptr) should be zero");
|
||||
|
||||
snmalloc::debug_check_empty<snmalloc::Globals>();
|
||||
return 0;
|
||||
|
||||
@@ -27,6 +27,7 @@ int main()
|
||||
# define my_free(x) free(x)
|
||||
# endif
|
||||
# include "override/memcpy.cc"
|
||||
# include "test/helpers.h"
|
||||
|
||||
# include <assert.h>
|
||||
# include <csetjmp>
|
||||
@@ -65,6 +66,7 @@ extern "C" void abort()
|
||||
*/
|
||||
void check_size(size_t size)
|
||||
{
|
||||
START_TEST("checking {}-byte memcpy", size);
|
||||
auto* s = reinterpret_cast<unsigned char*>(my_malloc(size + 1));
|
||||
auto* d = reinterpret_cast<unsigned char*>(my_malloc(size + 1));
|
||||
d[size] = 0;
|
||||
@@ -94,9 +96,13 @@ void check_size(size_t size)
|
||||
static_cast<unsigned char>(i),
|
||||
dst[i]);
|
||||
}
|
||||
SNMALLOC_CHECK(dst[i] == (unsigned char)i);
|
||||
EXPECT(
|
||||
dst[i] == (unsigned char)i,
|
||||
"dst[i] == {}, i == {}",
|
||||
size_t(dst[i]),
|
||||
i & 0xff);
|
||||
}
|
||||
SNMALLOC_CHECK(d[size] == 0);
|
||||
EXPECT(d[size] == 0, "d[size] == {}", d[size]);
|
||||
}
|
||||
my_free(s);
|
||||
my_free(d);
|
||||
@@ -104,6 +110,8 @@ void check_size(size_t size)
|
||||
|
||||
void check_bounds(size_t size, size_t out_of_bounds)
|
||||
{
|
||||
START_TEST(
|
||||
"memcpy bounds, size {}, {} bytes out of bounds", size, out_of_bounds);
|
||||
auto* s = reinterpret_cast<unsigned char*>(my_malloc(size));
|
||||
auto* d = reinterpret_cast<unsigned char*>(my_malloc(size));
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
@@ -125,7 +133,11 @@ void check_bounds(size_t size, size_t out_of_bounds)
|
||||
bounds_error = true;
|
||||
}
|
||||
can_longjmp = false;
|
||||
SNMALLOC_CHECK(bounds_error == (out_of_bounds > 0));
|
||||
EXPECT(
|
||||
bounds_error == (out_of_bounds > 0),
|
||||
"bounds error: {}, out_of_bounds: {}",
|
||||
bounds_error,
|
||||
out_of_bounds);
|
||||
my_free(s);
|
||||
my_free(d);
|
||||
}
|
||||
|
||||
39
src/test/helpers.h
Normal file
39
src/test/helpers.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#ifdef _MSC_VER
|
||||
# define __PRETTY_FUNCTION__ __FUNCSIG__
|
||||
#endif
|
||||
|
||||
namespace snmalloc
|
||||
{
|
||||
/**
|
||||
* The name of the function under test. This is set in the START_TEST macro
|
||||
* and used for error reporting in EXPECT.
|
||||
*/
|
||||
const char* current_test = "";
|
||||
|
||||
/**
|
||||
* Log that the test started.
|
||||
*/
|
||||
#define START_TEST(msg, ...) \
|
||||
do \
|
||||
{ \
|
||||
current_test = __PRETTY_FUNCTION__; \
|
||||
MessageBuilder<1024> mb{"Starting test: " msg "\n", ##__VA_ARGS__}; \
|
||||
fputs(mb.get_message(), stderr); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* An assertion that fires even in debug builds. Uses the value set by
|
||||
* START_TEST.
|
||||
*/
|
||||
#define EXPECT(x, msg, ...) \
|
||||
SNMALLOC_CHECK_MSG(x, " in test {} " msg "\n", current_test, ##__VA_ARGS__)
|
||||
|
||||
#define INFO(msg, ...) \
|
||||
do \
|
||||
{ \
|
||||
MessageBuilder<1024> mb{msg "\n", ##__VA_ARGS__}; \
|
||||
fputs(mb.get_message(), stderr); \
|
||||
} while (0)
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user