From 4641af7873eca592e1d9039b0a48e1763c29cb1c Mon Sep 17 00:00:00 2001 From: Andrew Kaster Date: Wed, 30 Aug 2023 16:54:33 +0200 Subject: [PATCH] AK: Always use our assertion failure method, and add backtrace to it On platforms that support it, enable using ```` to get backtrace(3) to dump a backtrace on assertion failure. This should make debugging things like WebContent crashes in Lagom much easier. --- AK/Assertions.cpp | 55 +++++++++++++++++++++++++++++++++++++++ AK/Assertions.h | 16 +++++------- Meta/Lagom/CMakeLists.txt | 4 +++ 3 files changed, 65 insertions(+), 10 deletions(-) diff --git a/AK/Assertions.cpp b/AK/Assertions.cpp index 6f40a0b085f..95d46626387 100644 --- a/AK/Assertions.cpp +++ b/AK/Assertions.cpp @@ -6,13 +6,68 @@ #include #include +#include +#include + +#if defined(AK_OS_LINUX) || defined(AK_OS_BSD_GENERIC) || defined(AK_OS_SOLARIS) +# define EXECINFO_BACKTRACE +#endif + +#if defined(EXECINFO_BACKTRACE) +# include +# include +#endif #if !defined(KERNEL) + +# if defined(EXECINFO_BACKTRACE) +namespace { +ALWAYS_INLINE void dump_backtrace() +{ + // Grab symbols and dso name for up to 256 frames + void* trace[256] = {}; + int const num_frames = backtrace(trace, sizeof(trace)); + char** syms = backtrace_symbols(trace, num_frames); + + for (auto i = 0; i < num_frames; ++i) { + // If there is a C++ symbol name in the line of the backtrace, demangle it + StringView sym(syms[i], strlen(syms[i])); + if (auto idx = sym.find("_Z"sv); idx.has_value()) { + // Play C games with the original string so we can print before and after the mangled symbol with a C API + // We don't want to call dbgln() here on substring StringView because we might VERIFY() within AK::Format + syms[i][idx.value() - 1] = '\0'; + (void)fprintf(stderr, "%s ", syms[i]); + + auto end_of_sym = sym.find(' ', idx.value()).value_or(sym.length() - 1); + syms[i][end_of_sym] = '\0'; + + size_t buf_size = 128u; + char* buf = static_cast(malloc(buf_size)); + auto* raw_str = &syms[i][idx.value()]; + buf = abi::__cxa_demangle(raw_str, buf, &buf_size, nullptr); + + (void)fputs(buf ? buf : raw_str, stderr); + free(buf); + + (void)fprintf(stderr, " %s", &syms[i][end_of_sym + 1]); + } else { + (void)fputs(sym.characters_without_null_termination(), stderr); + } + (void)fputs("\n", stderr); + } + free(syms); +} +} +# endif + extern "C" { void ak_verification_failed(char const* message) { dbgln("VERIFICATION FAILED: {}", message); +# if defined(EXECINFO_BACKTRACE) + dump_backtrace(); +# endif __builtin_trap(); } } diff --git a/AK/Assertions.h b/AK/Assertions.h index 7327d52cf4c..fb486092260 100644 --- a/AK/Assertions.h +++ b/AK/Assertions.h @@ -11,16 +11,12 @@ #else # include extern "C" __attribute__((noreturn)) void ak_verification_failed(char const*); -# if !defined(NDEBUG) && !defined(WIN32) -# define VERIFY assert -# else -# define __stringify_helper(x) #x -# define __stringify(x) __stringify_helper(x) -# define VERIFY(expr) \ - (__builtin_expect(!(expr), 0) \ - ? ak_verification_failed(#expr "\n" __FILE__ ":" __stringify(__LINE__)) \ - : (void)0) -# endif +# define __stringify_helper(x) #x +# define __stringify(x) __stringify_helper(x) +# define VERIFY(expr) \ + (__builtin_expect(!(expr), 0) \ + ? ak_verification_failed(#expr " at " __FILE__ ":" __stringify(__LINE__)) \ + : (void)0) # define VERIFY_NOT_REACHED() VERIFY(false) /* NOLINT(cert-dcl03-c,misc-static-assert) No, this can't be static_assert, it's a runtime check */ static constexpr bool TODO = false; # define TODO() VERIFY(TODO) /* NOLINT(cert-dcl03-c,misc-static-assert) No, this can't be static_assert, it's a runtime check */ diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index e75d346a900..5f2690199ed 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -331,6 +331,10 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "SunOS") # Solaris has socket and networking related functions in two extra libraries target_link_libraries(LibCore PRIVATE nsl socket) endif() +if (${CMAKE_SYSTEM_NAME} MATCHES "BSD$") + # BSD Platforms have backtrace(3) in a separate library + target_link_libraries(LibCore PRIVATE execinfo) +endif() target_sources(LibCore PRIVATE ${AK_SOURCES}) # LibMain