From e38b20695788f2640ed01437f40817588e6516ce Mon Sep 17 00:00:00 2001 From: Jonne Ransijn Date: Sat, 30 Nov 2024 16:29:25 +0100 Subject: [PATCH] AK: Print a more useful error message when a `MUST(...)` fails ``` VERIFICATION FAILED: !_temporary_result.is_error() ``` is not really a helpful error message. When we are including `AK/Format.h`, which is most of the time, we can easily print a much more useful error message: ``` UNEXPECTED ERROR: Cannot allocate memory (errno=12) ``` --- AK/Assertions.cpp | 41 +++++++++++++++++++---------------------- AK/Assertions.h | 3 +++ AK/Format.h | 15 +++++++++++++++ AK/Try.h | 5 ++++- 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/AK/Assertions.cpp b/AK/Assertions.cpp index 79d2de8c0ec..bc096d6a72a 100644 --- a/AK/Assertions.cpp +++ b/AK/Assertions.cpp @@ -81,45 +81,42 @@ ALWAYS_INLINE void dump_backtrace() extern "C" { -void ak_verification_failed(char const* message) +bool ak_colorize_output(void) { #if defined(AK_OS_SERENITY) || defined(AK_OS_ANDROID) - bool colorize_output = true; + return true; #elif defined(AK_OS_WINDOWS) - bool colorize_output = false; + return false; #else - bool colorize_output = isatty(STDERR_FILENO) == 1; + return isatty(STDERR_FILENO) == 1; #endif +} - if (colorize_output) +void ak_trap(void) +{ +#if defined(AK_HAS_BACKTRACE_HEADER) + dump_backtrace(); +#endif + __builtin_trap(); +} + +void ak_verification_failed(char const* message) +{ + if (ak_colorize_output()) ERRORLN("\033[31;1mVERIFICATION FAILED\033[0m: {}", message); else ERRORLN("VERIFICATION FAILED: {}", message); -#if defined(AK_HAS_BACKTRACE_HEADER) - dump_backtrace(); -#endif - __builtin_trap(); + ak_trap(); } void ak_assertion_failed(char const* message) { -#if defined(AK_OS_SERENITY) || defined(AK_OS_ANDROID) - bool colorize_output = true; -#elif defined(AK_OS_WINDOWS) - bool colorize_output = false; -#else - bool colorize_output = isatty(STDERR_FILENO) == 1; -#endif - - if (colorize_output) + if (ak_colorize_output()) ERRORLN("\033[31;1mASSERTION FAILED\033[0m: {}", message); else ERRORLN("ASSERTION FAILED: {}", message); -#if defined(AK_HAS_BACKTRACE_HEADER) - dump_backtrace(); -#endif - __builtin_trap(); + ak_trap(); } } diff --git a/AK/Assertions.h b/AK/Assertions.h index 58583dfc8a1..fb349ad4d11 100644 --- a/AK/Assertions.h +++ b/AK/Assertions.h @@ -6,6 +6,9 @@ #pragma once +extern "C" bool ak_colorize_output(void); +extern "C" __attribute__((noreturn)) void ak_trap(void); + extern "C" __attribute__((noreturn)) void ak_verification_failed(char const*); #define __stringify_helper(x) #x #define __stringify(x) __stringify_helper(x) diff --git a/AK/Format.h b/AK/Format.h index b4df3bb0e69..0c0d4fe7783 100644 --- a/AK/Format.h +++ b/AK/Format.h @@ -775,6 +775,21 @@ struct Formatter> : Formatter { } // namespace AK +#undef AK_HANDLE_UNEXPECTED_ERROR +#define AK_HANDLE_UNEXPECTED_ERROR(result) \ + if (result.is_error()) [[unlikely]] { \ + if (ak_colorize_output()) { \ + ::AK::warn("\033[31;1mUNEXPECTED ERROR\033[0m"); \ + } else { \ + ::AK::warn("UNEXPECTED ERROR"); \ + } \ + if constexpr (::AK::HasFormatter) { \ + ::AK::warn(": {}", result.release_error()); \ + } \ + ::AK::warnln(" at {}:{}", __FILE__, __LINE__); \ + ak_trap(); \ + } + #if USING_AK_GLOBALLY using AK::out; using AK::outln; diff --git a/AK/Try.h b/AK/Try.h index 14edb5c0676..09813597457 100644 --- a/AK/Try.h +++ b/AK/Try.h @@ -9,6 +9,9 @@ #include #include +// This macro is redefined in `AK/Format.h` to give a nicer error message. +#define AK_HANDLE_UNEXPECTED_ERROR(result) VERIFY(!result.is_error()); + // NOTE: This macro works with any result type that has the expected APIs. // It's designed with AK::Result and AK::Error in mind. // @@ -40,6 +43,6 @@ auto&& _temporary_result = (expression)); \ static_assert(!::AK::Detail::IsLvalueReference, \ "Do not return a reference from a fallible expression"); \ - VERIFY(!_temporary_result.is_error()); \ + AK_HANDLE_UNEXPECTED_ERROR(_temporary_result) \ _temporary_result.release_value(); \ })