Browse Source

AK: Always use our assertion failure method, and add backtrace to it

On platforms that support it, enable using ``<execinfo.h>`` to get
backtrace(3) to dump a backtrace on assertion failure. This should make
debugging things like WebContent crashes in Lagom much easier.
Andrew Kaster 1 năm trước cách đây
mục cha
commit
4641af7873
3 tập tin đã thay đổi với 65 bổ sung10 xóa
  1. 55 0
      AK/Assertions.cpp
  2. 6 10
      AK/Assertions.h
  3. 4 0
      Meta/Lagom/CMakeLists.txt

+ 55 - 0
AK/Assertions.cpp

@@ -6,13 +6,68 @@
 
 #include <AK/Assertions.h>
 #include <AK/Format.h>
+#include <AK/Platform.h>
+#include <AK/StringView.h>
+
+#if defined(AK_OS_LINUX) || defined(AK_OS_BSD_GENERIC) || defined(AK_OS_SOLARIS)
+#    define EXECINFO_BACKTRACE
+#endif
+
+#if defined(EXECINFO_BACKTRACE)
+#    include <cxxabi.h>
+#    include <execinfo.h>
+#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<char*>(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();
 }
 }

+ 6 - 10
AK/Assertions.h

@@ -11,16 +11,12 @@
 #else
 #    include <assert.h>
 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 */

+ 4 - 0
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