Add type-safe debugging macros for C.
These refactor the C++ bits slightly so that the library functions are
safe to use from C or C++ (without defining identifiers called things
like Bool in the global namespace) and provides some debug macros that
can be used from both C and C++, so that they can be used in headers.
As with the C++ versions, these use {} as the placeholders (no format
specifiers supported yet) and pass the type along with the value as an
argument.
diff --git a/sdk/core/allocator/token.h b/sdk/core/allocator/token.h
index 55f0385..1c75670 100644
--- a/sdk/core/allocator/token.h
+++ b/sdk/core/allocator/token.h
@@ -59,7 +59,7 @@
{
return {reinterpret_cast<uintptr_t>(
static_cast<const volatile void *>(value)),
- DebugFormatArgument::Pointer};
+ DebugFormatArgumentKind::DebugFormatArgumentPointer};
}
};
diff --git a/sdk/include/__debug.h b/sdk/include/__debug.h
new file mode 100644
index 0000000..637e166
--- /dev/null
+++ b/sdk/include/__debug.h
@@ -0,0 +1,74 @@
+#pragma once
+#include <compartment-macros.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * The kind of value, for values that have special-cased handling.
+ */
+enum DebugFormatArgumentKind : ptraddr_t
+{
+ /// Boolean, printed as "true" or "false".
+ DebugFormatArgumentBool,
+ /// Single character.
+ DebugFormatArgumentCharacter,
+ /// Signed 32-bit integer, printed as decimal.
+ DebugFormatArgumentSignedNumber32,
+ /// Unsigned 32-bit integer, printed as hexadecimal
+ DebugFormatArgumentUnsignedNumber32,
+ /// Signed 64-bit integer, printed as decimal.
+ DebugFormatArgumentSignedNumber64,
+ /// Unsigned 64-bit integer, printed as hexadecimal.
+ DebugFormatArgumentUnsignedNumber64,
+ /// Pointer, printed as a full capability.
+ DebugFormatArgumentPointer,
+ /// Special case for permission sets, printed as in the capability
+ /// format.
+ DebugFormatArgumentPermissionSet,
+ /// C string, printed as-is.
+ DebugFormatArgumentCString,
+ /// String view, printed as-is.
+ DebugFormatArgumentStringView,
+};
+
+struct DebugFormatArgument
+{
+ /**
+ * The value that is being written.
+ */
+ uintptr_t value;
+ /**
+ * The kind of value that is being written. This is either a pointer
+ * to a `DebugCallback` or one of the `Kind` enumeration, depending on
+ * whether the tag is set or not.
+ */
+ uintptr_t kind;
+};
+
+/**
+ * Library function that writes a debug message. This runs with interrupts
+ * disabled (to avoid interleaving) and prints an array of debug messages. This
+ * is intended to allow a single call to print multiple format strings without
+ * requiring the format strings to be copied, so that the debugging APIs can
+ * wrap a user-provided format string.
+ */
+__cheri_libcall void
+debug_log_message_write(const char *context,
+ const char *format,
+ struct DebugFormatArgument *messages,
+ size_t messageCount);
+
+/**
+ * Helper to write a debug message reporting an assertion or invariant failure.
+ * This should be used only with the helper macros / templates in `debug.h[h]`.
+ * This takes the kind of failure (for example, assert or invariant), the file,
+ * function, and line number where the failure occurred, a format string, and
+ * an array of arguments to the format string.
+ */
+__cheri_libcall void debug_report_failure(const char *kind,
+ const char *file,
+ const char *function,
+ int line,
+ const char *fmt,
+ struct DebugFormatArgument *arguments,
+ size_t argumentCount);
diff --git a/sdk/include/__macro_map.h b/sdk/include/__macro_map.h
new file mode 100644
index 0000000..ac507b7
--- /dev/null
+++ b/sdk/include/__macro_map.h
@@ -0,0 +1,69 @@
+/*
+ * Created by William Swanson in 2012.
+ *
+ * I, William Swanson, dedicate this work to the public domain.
+ * I waive all rights to the work worldwide under copyright law,
+ * including all related and neighboring rights,
+ * to the extent allowed by law.
+ *
+ * You can copy, modify, distribute and perform the work,
+ * even for commercial purposes, all without asking permission.
+ *
+ * CHERIoT modifications:
+ * - Added CHERIOT_ prefix to all macros to avoid namespace pollution
+ * - Used pragma once instead of include guards
+ */
+
+#pragma once
+
+#define CHERIOT_EVAL0(...) __VA_ARGS__
+#define CHERIOT_EVAL1(...) \
+ CHERIOT_EVAL0(CHERIOT_EVAL0(CHERIOT_EVAL0(__VA_ARGS__)))
+#define CHERIOT_EVAL2(...) \
+ CHERIOT_EVAL1(CHERIOT_EVAL1(CHERIOT_EVAL1(__VA_ARGS__)))
+#define CHERIOT_EVAL3(...) \
+ CHERIOT_EVAL2(CHERIOT_EVAL2(CHERIOT_EVAL2(__VA_ARGS__)))
+#define CHERIOT_EVAL4(...) \
+ CHERIOT_EVAL3(CHERIOT_EVAL3(CHERIOT_EVAL3(__VA_ARGS__)))
+#define CHERIOT_EVAL(...) \
+ CHERIOT_EVAL4(CHERIOT_EVAL4(CHERIOT_EVAL4(__VA_ARGS__)))
+
+#define CHERIOT_MAP_END(...)
+#define CHERIOT_MAP_OUT
+#define CHERIOT_MAP_COMMA ,
+
+#define CHERIOT_MAP_GET_END2() 0, CHERIOT_MAP_END
+#define CHERIOT_MAP_GET_END1(...) CHERIOT_MAP_GET_END2
+#define CHERIOT_MAP_GET_END(...) CHERIOT_MAP_GET_END1
+#define CHERIOT_MAP_NEXT0(test, next, ...) next CHERIOT_MAP_OUT
+#define CHERIOT_MAP_NEXT1(test, next) CHERIOT_MAP_NEXT0(test, next, 0)
+#define CHERIOT_MAP_NEXT(test, next) \
+ CHERIOT_MAP_NEXT1(CHERIOT_MAP_GET_END test, next)
+
+#define CHERIOT_MAP0(f, x, peek, ...) \
+ f(x) CHERIOT_MAP_NEXT(peek, CHERIOT_MAP1)(f, peek, __VA_ARGS__)
+#define CHERIOT_MAP1(f, x, peek, ...) \
+ f(x) CHERIOT_MAP_NEXT(peek, CHERIOT_MAP0)(f, peek, __VA_ARGS__)
+
+#define CHERIOT_MAP_LIST_NEXT1(test, next) \
+ CHERIOT_MAP_NEXT0(test, CHERIOT_MAP_COMMA next, 0)
+#define CHERIOT_MAP_LIST_NEXT(test, next) \
+ CHERIOT_MAP_LIST_NEXT1(CHERIOT_MAP_GET_END test, next)
+
+#define CHERIOT_MAP_LIST0(f, x, peek, ...) \
+ f(x) CHERIOT_MAP_LIST_NEXT(peek, CHERIOT_MAP_LIST1)(f, peek, __VA_ARGS__)
+#define CHERIOT_MAP_LIST1(f, x, peek, ...) \
+ f(x) CHERIOT_MAP_LIST_NEXT(peek, CHERIOT_MAP_LIST0)(f, peek, __VA_ARGS__)
+
+/**
+ * Applies the function macro `f` to each of the remaining parameters.
+ */
+#define CHERIOT_MAP(f, ...) \
+ CHERIOT_EVAL(CHERIOT_MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
+
+/**
+ * Applies the function macro `f` to each of the remaining parameters and
+ * inserts commas between the results.
+ */
+#define CHERIOT_MAP_LIST(f, ...) \
+ CHERIOT_EVAL(CHERIOT_MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
diff --git a/sdk/include/debug.h b/sdk/include/debug.h
new file mode 100644
index 0000000..9e6a56a
--- /dev/null
+++ b/sdk/include/debug.h
@@ -0,0 +1,139 @@
+#pragma once
+#include <__debug.h>
+#include <__macro_map.h>
+
+#ifndef __cplusplus
+
+/**
+ * Helper macro to convert the type of an argument to the corresponding
+ * `DebugFormatArgument` value.
+ *
+ * Should not be used directly.
+ */
+# define CHERIOT_DEBUG_MAP_ARGUMENT(x) \
+ { \
+ (uintptr_t)(x), _Generic((x), \
+ _Bool: DebugFormatArgumentBool, \
+ char: DebugFormatArgumentCharacter, \
+ short: DebugFormatArgumentSignedNumber32, \
+ unsigned short: DebugFormatArgumentUnsignedNumber32, \
+ int: DebugFormatArgumentSignedNumber32, \
+ unsigned int: DebugFormatArgumentUnsignedNumber32, \
+ signed long long: DebugFormatArgumentSignedNumber64, \
+ unsigned long long: DebugFormatArgumentUnsignedNumber64, \
+ char *: DebugFormatArgumentCString, \
+ default: DebugFormatArgumentPointer) \
+ }
+
+/**
+ * Helper to map a list of arguments to an initialiser for a
+ * `DebugFormatArgument` array.
+ *
+ * Should not be used directly.
+ */
+# define CHERIOT_DEBUG_MAP_ARGUMENTS(...) \
+ CHERIOT_MAP_LIST(CHERIOT_DEBUG_MAP_ARGUMENT, __VA_ARGS__)
+
+/**
+ * Macro that logs a message. The `context` argument is a string that is
+ * printed in magenta at the start of the line, followed by the format string.
+ * Each format argument is referenced with {} in the format string and is
+ * inserted in the output with the default rendering for that type.
+ */
+# define CHERIOT_DEBUG_LOG(context, msg, ...) \
+ do \
+ { \
+ struct DebugFormatArgument args[] = { \
+ __VA_OPT__(CHERIOT_DEBUG_MAP_ARGUMENTS(__VA_ARGS__))}; \
+ debug_log_message_write( \
+ context, \
+ msg, \
+ args, \
+ 0 __VA_OPT__(+(sizeof(args) / sizeof(args[0])))); \
+ } while (0)
+
+/**
+ * Assert that `condition` is true, printing a message and aborting the
+ * compartment invocation if not.
+ */
+# define CHERIOT_INVARIANT(condition, msg, ...) \
+ do \
+ { \
+ if (!(condition)) \
+ { \
+ struct DebugFormatArgument args[] = { \
+ __VA_OPT__(CHERIOT_DEBUG_MAP_ARGUMENTS(__VA_ARGS__))}; \
+ debug_report_failure( \
+ "Invariant", \
+ __FILE__, \
+ __func__, \
+ __LINE__, \
+ msg, \
+ args, \
+ 0 __VA_OPT__(+(sizeof(args) / sizeof(args[0])))); \
+ __builtin_trap(); \
+ } \
+ } while (0)
+
+#else
+# include <debug.hh>
+
+namespace
+{
+ template<typename... Args>
+ __always_inline void
+ cheriot_debug_log(const char *context, const char *msg, Args... args)
+ {
+ DebugFormatArgument arguments[sizeof...(Args)];
+ make_debug_arguments_list(arguments, args...);
+ debug_log_message_write(context, msg, arguments, sizeof...(Args));
+ }
+
+ template<typename... Args>
+ __always_inline void cheriot_invariant(bool condition,
+ const char *file,
+ const char *function,
+ int line,
+ const char *msg,
+ Args... args)
+ {
+ if (!condition)
+ {
+ DebugFormatArgument arguments[sizeof...(Args)];
+ make_debug_arguments_list(arguments, args...);
+ debug_report_failure("Invariant",
+ file,
+ function,
+ line,
+ msg,
+ arguments,
+ sizeof...(Args));
+ }
+ }
+} // namespace
+
+/**
+ * C++ version of `CHERIOT_DEBUG_LOG`. This uses the C++ helpers and so will
+ * pretty-print a richer set of types than the C version.
+ */
+# define CHERIOT_DEBUG_LOG(context, msg, ...) \
+ do \
+ { \
+ cheriot_debug_log(context, msg __VA_OPT__(, ) __VA_ARGS__); \
+ } while (0)
+
+/**
+ * C++ version of `CHERIOT_INVARIANT`. This uses the C++ helpers and so will
+ * pretty-print a richer set of types than the C version.
+ */
+# define CHERIOT_INVARIANT(condition, msg, ...) \
+ do \
+ { \
+ cheriot_invariant(condition, \
+ __FILE__, \
+ __func__, \
+ __LINE__, \
+ msg __VA_OPT__(, ) __VA_ARGS__); \
+ } while (0)
+
+#endif
diff --git a/sdk/include/debug.hh b/sdk/include/debug.hh
index a95470b..21fb6b8 100644
--- a/sdk/include/debug.hh
+++ b/sdk/include/debug.hh
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: MIT
#pragma once
+#include <__debug.h>
#include <cheri.hh>
#include <compartment.h>
#include <concepts>
@@ -101,48 +102,6 @@
*/
using DebugCallback = void (*)(uintptr_t, DebugWriter &);
-struct DebugFormatArgument
-{
- /**
- * The kind of value, for values that have special-cased handling.
- */
- enum Kind : ptraddr_t
- {
- /// Boolean, printed as "true" or "false".
- Bool,
- /// Single character.
- Character,
- /// Signed 32-bit integer, printed as decimal.
- SignedNumber32,
- /// Unsigned 32-bit integer, printed as hexadecimal
- UnsignedNumber32,
- /// Signed 64-bit integer, printed as decimal.
- SignedNumber64,
- /// Unsigned 64-bit integer, printed as hexadecimal.
- UnsignedNumber64,
- /// Pointer, printed as a full capability.
- Pointer,
- /// Special case for permission sets, printed as in the capability
- /// format.
- PermissionSet,
- /// C string, printed as-is.
- CString,
- /// String view, printed as-is.
- StringView,
- };
-
- /**
- * The value that is being written.
- */
- uintptr_t value;
- /**
- * The kind of value that is being written. This is either a pointer
- * to a `DebugCallback` or one of the `Kind` enumeration, depending on
- * whether the tag is set or not.
- */
- uintptr_t kind;
-};
-
/**
* Adaptor that turns an argument of type `T` into a `DebugFormatArgument`.
*
@@ -160,7 +119,8 @@
{
__always_inline static DebugFormatArgument construct(bool value)
{
- return {static_cast<uintptr_t>(value), DebugFormatArgument::Bool};
+ return {static_cast<uintptr_t>(value),
+ DebugFormatArgumentKind::DebugFormatArgumentBool};
}
};
@@ -172,7 +132,8 @@
{
__always_inline static DebugFormatArgument construct(char value)
{
- return {static_cast<uintptr_t>(value), DebugFormatArgument::Character};
+ return {static_cast<uintptr_t>(value),
+ DebugFormatArgumentKind::DebugFormatArgumentCharacter};
}
};
@@ -185,7 +146,7 @@
__always_inline static DebugFormatArgument construct(uint8_t value)
{
return {static_cast<uintptr_t>(value),
- DebugFormatArgument::UnsignedNumber32};
+ DebugFormatArgumentKind::DebugFormatArgumentUnsignedNumber32};
}
};
@@ -198,7 +159,7 @@
__always_inline static DebugFormatArgument construct(uint16_t value)
{
return {static_cast<uintptr_t>(value),
- DebugFormatArgument::UnsignedNumber32};
+ DebugFormatArgumentKind::DebugFormatArgumentUnsignedNumber32};
}
};
@@ -211,7 +172,7 @@
__always_inline static DebugFormatArgument construct(uint32_t value)
{
return {static_cast<uintptr_t>(value),
- DebugFormatArgument::UnsignedNumber32};
+ DebugFormatArgumentKind::DebugFormatArgumentUnsignedNumber32};
}
};
@@ -229,7 +190,8 @@
{
uintptr_t fudgedValue;
memcpy(&fudgedValue, &value, sizeof(fudgedValue));
- return {fudgedValue, DebugFormatArgument::UnsignedNumber64};
+ return {fudgedValue,
+ DebugFormatArgumentKind::DebugFormatArgumentUnsignedNumber64};
}
};
@@ -242,7 +204,7 @@
__always_inline static DebugFormatArgument construct(int8_t value)
{
return {static_cast<uintptr_t>(value),
- DebugFormatArgument::SignedNumber32};
+ DebugFormatArgumentKind::DebugFormatArgumentSignedNumber32};
}
};
@@ -255,7 +217,7 @@
__always_inline static DebugFormatArgument construct(int16_t value)
{
return {static_cast<uintptr_t>(value),
- DebugFormatArgument::SignedNumber32};
+ DebugFormatArgumentKind::DebugFormatArgumentSignedNumber32};
}
};
@@ -268,7 +230,7 @@
__always_inline static DebugFormatArgument construct(int32_t value)
{
return {static_cast<uintptr_t>(value),
- DebugFormatArgument::SignedNumber32};
+ DebugFormatArgumentKind::DebugFormatArgumentSignedNumber32};
}
};
@@ -287,7 +249,8 @@
static_assert(sizeof(uintptr_t) == sizeof(uint64_t));
uintptr_t fudgedValue;
memcpy(&fudgedValue, &value, sizeof(fudgedValue));
- return {fudgedValue, DebugFormatArgument::SignedNumber64};
+ return {fudgedValue,
+ DebugFormatArgumentKind::DebugFormatArgumentSignedNumber64};
}
};
@@ -300,7 +263,7 @@
__always_inline static DebugFormatArgument construct(const char *value)
{
return {reinterpret_cast<uintptr_t>(value),
- DebugFormatArgument::CString};
+ DebugFormatArgumentKind::DebugFormatArgumentCString};
}
};
@@ -317,7 +280,7 @@
construct(std::string_view &value)
{
return {reinterpret_cast<uintptr_t>(&value),
- DebugFormatArgument::StringView};
+ DebugFormatArgumentKind::DebugFormatArgumentStringView};
}
};
@@ -334,7 +297,7 @@
{
#ifdef CHERIOT_AVOID_CAPRELOCS
return {static_cast<uintptr_t>(value),
- DebugFormatArgument::UnsignedNumber32};
+ DebugFormatArgumentKind::DebugFormatArgumentUnsignedNumber32};
#else
return {static_cast<uintptr_t>(value),
reinterpret_cast<uintptr_t>(&debug_enum_helper<T>)};
@@ -352,7 +315,19 @@
construct(CHERI::PermissionSet value)
{
return {static_cast<uintptr_t>(value.as_raw()),
- DebugFormatArgument::PermissionSet};
+ DebugFormatArgumentKind::DebugFormatArgumentPermissionSet};
+ }
+};
+
+/**
+ * Null pointer specialisation.
+ */
+template<>
+struct DebugFormatArgumentAdaptor<std::nullptr_t>
+{
+ __always_inline static DebugFormatArgument construct(std::nullptr_t)
+ {
+ return {0, DebugFormatArgumentKind::DebugFormatArgumentPointer};
}
};
@@ -366,7 +341,7 @@
{
return {reinterpret_cast<uintptr_t>(
static_cast<const volatile void *>(value)),
- DebugFormatArgument::Pointer};
+ DebugFormatArgumentKind::DebugFormatArgumentPointer};
}
};
@@ -382,7 +357,7 @@
{
return {reinterpret_cast<uintptr_t>(
static_cast<const volatile void *>(value)),
- DebugFormatArgument::Pointer};
+ DebugFormatArgumentKind::DebugFormatArgumentPointer};
}
};
@@ -397,7 +372,7 @@
{
return {static_cast<uintptr_t>(value),
- DebugFormatArgument::UnsignedNumber32};
+ DebugFormatArgumentKind::DebugFormatArgumentUnsignedNumber32};
}
};
diff --git a/sdk/lib/debug/debug.cc b/sdk/lib/debug/debug.cc
index 123bf59..0756164 100644
--- a/sdk/lib/debug/debug.cc
+++ b/sdk/lib/debug/debug.cc
@@ -257,48 +257,58 @@
else
{
switch (
- static_cast<DebugFormatArgument::Kind>(argument.kind))
+ static_cast<DebugFormatArgumentKind>(argument.kind))
{
- case DebugFormatArgument::Kind::Bool:
+ case DebugFormatArgumentKind::
+ DebugFormatArgumentBool:
write(static_cast<bool>(argument.value)
? "true"
: "false");
break;
- case DebugFormatArgument::Kind::Character:
+ case DebugFormatArgumentKind::
+ DebugFormatArgumentCharacter:
write(static_cast<char>(argument.value));
break;
- case DebugFormatArgument::Kind::Pointer:
+ case DebugFormatArgumentKind::
+ DebugFormatArgumentPointer:
write(reinterpret_cast<void *>(argument.value));
break;
- case DebugFormatArgument::Kind::SignedNumber32:
+ case DebugFormatArgumentKind::
+ DebugFormatArgumentSignedNumber32:
write(static_cast<int32_t>(argument.value));
break;
- case DebugFormatArgument::Kind::UnsignedNumber32:
+ case DebugFormatArgumentKind::
+ DebugFormatArgumentUnsignedNumber32:
write(static_cast<uint32_t>(argument.value));
break;
- case DebugFormatArgument::Kind::SignedNumber64:
+ case DebugFormatArgumentKind::
+ DebugFormatArgumentSignedNumber64:
{
int64_t value;
memcpy(&value, &argument.value, sizeof(value));
write(value);
break;
}
- case DebugFormatArgument::Kind::UnsignedNumber64:
+ case DebugFormatArgumentKind::
+ DebugFormatArgumentUnsignedNumber64:
{
uint64_t value;
memcpy(&value, &argument.value, sizeof(value));
write(value);
break;
}
- case DebugFormatArgument::Kind::CString:
+ case DebugFormatArgumentKind::
+ DebugFormatArgumentCString:
write(reinterpret_cast<const char *>(
argument.value));
break;
- case DebugFormatArgument::Kind::StringView:
+ case DebugFormatArgumentKind::
+ DebugFormatArgumentStringView:
write(*reinterpret_cast<std::string_view *>(
argument.value));
break;
- case DebugFormatArgument::Kind::PermissionSet:
+ case DebugFormatArgumentKind::
+ DebugFormatArgumentPermissionSet:
write(CHERI::PermissionSet::from_raw(
argument.value));
break;
diff --git a/tests/debug-test.c b/tests/debug-test.c
new file mode 100644
index 0000000..03c008c
--- /dev/null
+++ b/tests/debug-test.c
@@ -0,0 +1,25 @@
+#include <compartment.h>
+#include <debug.h>
+
+__cheri_compartment("debug_test") void test_debug_c()
+{
+ unsigned char x = 'c';
+ _Bool t = true;
+ CHERIOT_DEBUG_LOG(
+ "Debug messages",
+ "Testing C debug log: 42:{}, true:{}, hello world:{}, "
+ "'c':{}, &x:{}, NULL:{}, short 3:{}, unsigned short 0xf:{}",
+ 42,
+ t,
+ "hello world",
+ (char)'c',
+ &x,
+ NULL,
+ (short)3,
+ (unsigned short)0xf);
+ // Just test that these compile:
+ CHERIOT_INVARIANT(true, "Testing C++ invariant failure: 42:{}", 42);
+ CHERIOT_INVARIANT(true, "Testing C++ invariant failure");
+ CHERIOT_INVARIANT(
+ true, "Testing C++ invariant failure: 42:{}", 42, 1, 3, 4, "oops");
+}
diff --git a/tests/debug-test.cc b/tests/debug-test.cc
new file mode 100644
index 0000000..e44bb65
--- /dev/null
+++ b/tests/debug-test.cc
@@ -0,0 +1,22 @@
+#include "tests.hh"
+#include <compartment.h>
+#include <debug.h>
+
+void test_debug_cxx()
+{
+ unsigned char x = 'c';
+ CHERIOT_DEBUG_LOG("Debug messages",
+ "Testing C++ debug log: 42:{}, true:{}, hello world:{}, "
+ "'c':{}, &x:{}, nullptr:{}",
+ 42,
+ true,
+ "hello world",
+ 'c',
+ &x,
+ nullptr);
+ // Just test that these compile:
+ CHERIOT_INVARIANT(true, "Testing C++ invariant failure: 42:{}", 42);
+ CHERIOT_INVARIANT(true, "Testing C++ invariant failure");
+ CHERIOT_INVARIANT(
+ true, "Testing C++ invariant failure: 42:{}", 42, 1, 3, 4, "oops");
+}
diff --git a/tests/test-runner.cc b/tests/test-runner.cc
index b59a823..8d99fdd 100644
--- a/tests/test-runner.cc
+++ b/tests/test-runner.cc
@@ -107,6 +107,8 @@
debug_log("Trying to print string: {}", std::string_view{testString, 13});
run_timed("All tests", []() {
+ run_timed("Debug helpers (C++)", test_debug_cxx);
+ run_timed("Debug helpers (C)", test_debug_c);
run_timed("MMIO", test_mmio);
run_timed("stdio", test_stdio);
run_timed("Static sealing", test_static_sealing);
diff --git a/tests/tests.hh b/tests/tests.hh
index ffb71d0..bd63a3c 100644
--- a/tests/tests.hh
+++ b/tests/tests.hh
@@ -20,6 +20,8 @@
__cheri_compartment("misc_test") void test_misc();
__cheri_compartment("static_sealing_test") void test_static_sealing();
__cheri_compartment("stdio_test") void test_stdio();
+__cheri_compartment("debug_test") void test_debug_cxx();
+__cheri_compartment("debug_test") void test_debug_c();
// Simple tests don't need a separate compartment.
void test_global_constructors();
diff --git a/tests/xmake.lua b/tests/xmake.lua
index 48c294a..2f9e5ed 100644
--- a/tests/xmake.lua
+++ b/tests/xmake.lua
@@ -54,6 +54,9 @@
test("queue")
-- Test minimal stdio implementation
test("stdio")
+-- Test the debug helpers.
+test("debug")
+ add_files("debug-test.c")
-- Test the static sealing types
test("static_sealing")
compartment("static_sealing_inner")
@@ -112,6 +115,7 @@
add_deps("check_pointer_test")
add_deps("misc_test")
add_deps("stdio_test")
+ add_deps("debug_test")
-- Set the thread entry point to the test runner.
on_load(function(target)
target:values_set("board", "$(board)")