From 70ba86d426accc06696402a8d3928ee5845b8f89 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Tue, 19 Nov 2024 23:18:45 -0700 Subject: libmisc: Logging/assert fixes --- libmisc/CMakeLists.txt | 1 + libmisc/assert.c | 3 +- libmisc/include/libmisc/assert.h | 18 +++++---- libmisc/include/libmisc/log.h | 3 +- libmisc/tests/test_assert.c | 85 ++++++++++++++++++++++++++-------------- libmisc/tests/test_log.c | 61 ++++++++++++++++++++++++++++ 6 files changed, 131 insertions(+), 40 deletions(-) create mode 100644 libmisc/tests/test_log.c diff --git a/libmisc/CMakeLists.txt b/libmisc/CMakeLists.txt index 1394978..02b19d5 100644 --- a/libmisc/CMakeLists.txt +++ b/libmisc/CMakeLists.txt @@ -14,6 +14,7 @@ target_compile_options(libmisc INTERFACE "$<$:-fplan9-extens add_lib_test(libmisc test_assert) add_lib_test(libmisc test_endian) add_lib_test(libmisc test_hash) +add_lib_test(libmisc test_log) add_lib_test(libmisc test_private) add_lib_test(libmisc test_rand) add_lib_test(libmisc test_vcall) diff --git a/libmisc/assert.c b/libmisc/assert.c index dbf25b7..456ac24 100644 --- a/libmisc/assert.c +++ b/libmisc/assert.c @@ -23,7 +23,8 @@ void __assert_msg_fail(const char *expr, errorf("%s:%u:%s(): assertion \"%s\" failed%s%s", file, line, func, expr, - msg ? ": " : "", msg); + msg ? ": " : "", msg ?: ""); + in_fail = false; } abort(); } diff --git a/libmisc/include/libmisc/assert.h b/libmisc/include/libmisc/assert.h index 525d7d5..92b9a51 100644 --- a/libmisc/include/libmisc/assert.h +++ b/libmisc/include/libmisc/assert.h @@ -8,16 +8,18 @@ #define _LIBMISC_ASSERT_H_ #ifdef NDEBUG -# define assert_msg(expr, msg) ((void)0) +# define __assert_msg(expr, expr_str, msg) ((void)0) #else -# define assert_msg(expr, msg) do { if (!(expr)) __assert_msg_fail(#expr, __FILE__, __LINE__, __func__, msg); } while (0) -void __assert_msg_fail(const char *expr, - const char *file, unsigned int line, const char *func, - const char *msg); +# define __assert_msg(expr, expr_str, msg) do { if (!(expr)) __assert_msg_fail(expr_str, __FILE__, __LINE__, __func__, msg); } while (0) +__attribute__((noreturn)) void +__assert_msg_fail(const char *expr, + const char *file, unsigned int line, const char *func, + const char *msg); #endif -#define assert(expr) assert_msg(expr, NULL) /* C89, POSIX-2001 */ -#define assert_notreached(msg) do { assert_msg(0, msg); __builtin_unreachable(); } while (0) -#define static_assert _Static_assert /* C11 */ +#define assert_msg(expr, msg) __assert_msg(expr, #expr, msg) /* libmisc */ +#define assert(expr) __assert_msg(expr, #expr, NULL) /* C89, POSIX-2001 */ +#define assert_notreached(msg) do { __assert_msg(0, "notreached", msg); __builtin_unreachable(); } while (0) /* libmisc */ +#define static_assert _Static_assert /* C11 */ #endif /* _LIBMISC_ASSERT_H_ */ diff --git a/libmisc/include/libmisc/log.h b/libmisc/include/libmisc/log.h index eb9db3b..85b2201 100644 --- a/libmisc/include/libmisc/log.h +++ b/libmisc/include/libmisc/log.h @@ -16,7 +16,8 @@ #else #define _LOG_NDEBUG 0 #endif -#define _LOG_STR(x) #x +#define __LOG_STR(x) #x +#define _LOG_STR(x) __LOG_STR(x) #define __LOG_CAT3(a, b, c) a ## b ## c #define _LOG_CAT3(a, b, c) __LOG_CAT3(a, b, c) diff --git a/libmisc/tests/test_assert.c b/libmisc/tests/test_assert.c index 949b4f9..a2ac743 100644 --- a/libmisc/tests/test_assert.c +++ b/libmisc/tests/test_assert.c @@ -4,62 +4,87 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +#include #include +#include +#include #include #include "test.h" -/* Intercept failures *********************************************************/ - -bool global_failed = false; -bool global_unreachable = false; - -void __assert_msg_fail(const char *expr, - const char *file, unsigned int line, const char *func, - const char *msg) { - static bool in_fail = false; - if (!in_fail) { - in_fail = true; - printf("error: %s:%u:%s(): assertion \"%s\" failed%s%s\n", - file, line, func, - expr, - msg ? ": " : "", msg); - } +#define UNUSED(name) + +/* Intercept failures and logging *********************************************/ + +bool global_failed; +char *global_log; +jmp_buf global_env; + +#define with_intercept() ({ \ + global_failed = false; \ + if (global_log) \ + free(global_log); \ + global_log = NULL; \ + setjmp(global_env) == 0; \ + }) + +__attribute__((noreturn)) void abort(void) { global_failed = true; + longjmp(global_env, 1); } -#define __builtin_unreachable() do { global_unreachable = true; } while (0) +#define __builtin_unreachable() test_assert(0) + +int vprintf(const char *format, va_list ap) { + return vasprintf(&global_log, format, ap); +} /* Utilities ******************************************************************/ -#define test_should_succeed(test) do { \ - global_failed = false; \ - test; \ - test_assert(global_failed == false); \ +#define test_should_succeed(test) do { \ + if (with_intercept()) { \ + test; \ + } \ + test_assert(global_failed == false); \ + test_assert(global_log == NULL); \ } while (0) -#define test_should_fail(test) do { \ - global_failed = false; \ - test; \ - test_assert(global_failed == true); \ +#define test_should_fail(test, exp_log) do { \ + if (with_intercept()) { \ + test; \ + } \ + test_assert(global_failed == true); \ + if (!(global_log != NULL && \ + strcmp(global_log, exp_log) == 0)) { \ + printf("exp = \"%s\"\n" \ + "act = \"%s\"\n", \ + exp_log, global_log); \ + test_assert(0); \ + } \ } while (0) +#define _STR(x) #x +#define STR(x) _STR(x) + /* Actual tests ***************************************************************/ static_assert(sizeof(char) == 1); int main() { test_should_succeed(assert(true)); - test_should_fail(assert(false)); + test_should_fail(assert(false), "error: ASSERT: "__FILE__":"STR(__LINE__)":main(): assertion \"false\" failed\n"); test_should_succeed(assert_msg(true, "foo")); - test_should_fail(assert_msg(false, "foo")); + test_should_fail(assert_msg(false, "foo"), "error: ASSERT: "__FILE__":"STR(__LINE__)":main(): assertion \"false\" failed: foo\n"); test_should_succeed(assert_msg(true, NULL)); - test_should_fail(assert_msg(false, NULL)); + test_should_fail(assert_msg(false, NULL), "error: ASSERT: "__FILE__":"STR(__LINE__)":main(): assertion \"false\" failed\n"); - test_should_fail(assert_notreached("")); - test_assert(global_unreachable == true); + test_should_fail(assert_notreached("xxx"), "error: ASSERT: "__FILE__":"STR(__LINE__)":main(): assertion \"notreached\" failed: xxx\n"); + if (global_log) { + free(global_log); + global_log = NULL; + } return 0; } diff --git a/libmisc/tests/test_log.c b/libmisc/tests/test_log.c new file mode 100644 index 0000000..286738d --- /dev/null +++ b/libmisc/tests/test_log.c @@ -0,0 +1,61 @@ +/* libmisc/tests/test_log.c - Tests for + * + * Copyright (C) 2024 Luke T. Shumaker + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#define _GNU_SOURCE /* for vasprintf() */ +#include /* for va_list */ +#include /* for vasprintf() */ +#include /* for free() */ +#include /* for strcmp() */ + +#define LOG_NAME FROBNICATE +#include + +#include "test.h" + +/* Intercept output ***********************************************************/ + +static char *log_output = NULL; + +int vprintf(const char *format, va_list ap) { + return vasprintf(&log_output, format, ap); +} + +/* Actual tests ***************************************************************/ + +#define should_print(_exp, cmd) do { \ + char *exp = _exp; \ + test_assert(!log_output); \ + cmd; \ + if (!exp) \ + test_assert(!log_output); \ + else \ + if (!(log_output != NULL && \ + strcmp(log_output, exp) == 0)) { \ + printf("exp = \"%s\"\n" \ + "act = \"%s\"\n", \ + exp, log_output); \ + test_assert(0); \ + } \ + if (log_output) { \ + free(log_output); \ + log_output = NULL; \ + } \ + } while (0) + +int main() { + should_print("error: FROBNICATE: val=42\n", + errorf("val=%d", 42)); + should_print("info : FROBNICATE: val=0\n", + infof("val=%d", 0)); +#define CONFIG_FROBNICATE_DEBUG 1 + should_print("debug: FROBNICATE: val=-2\n", + debugf("val=%d", -2)); +#undef CONFIG_FROBNICATE_DEBUG +#define CONFIG_FROBNICATE_DEBUG 0 + should_print(NULL, + debugf("val=%d", -2)); + return 0; +} -- cgit v1.2.3-2-g168b