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:
David Chisnall
2022-02-25 15:57:28 +00:00
committed by GitHub
parent 93efbb4807
commit 95bd974fb0
7 changed files with 234 additions and 147 deletions

View File

@@ -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

View File

@@ -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.
*/