mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-19 07:33:20 +00:00
AK: Clean up backtraces
This commit replaces the default backtrace logic with cpptrace, for nicer, colored backtraces. Cpptrace runs on all of our supported platforms excpet android. As such backtrace.h is left in place. All the backtrace functions are made noinline to have a consistent number of frames. A maximum depth parameter is added to dump_backtrace with a default of 100. This should be enough, and can be easily changed, and allows for limiting the maximum depth. Setting the LADYBIRD_BACKTRACE_SNIPPETS environment variable enables surrouding code snippets in the backtrace. Specifically 2 lines above and below. This number can be changed by calling snippet_context on the formatter. For the whole list of options of what can be done with formatting see the cpptrace repository. On Windows we skipped frames when verification fails and when dump_backtrace was added the logic was wrong and would have skipped frames we care about. This commit also implements skipping frames on Linux. The only time where this does not skip all frames is when the call to backtrace gets intercepted. Then we will end up skipping one frame less than needed. To keep delayload on Windows a patch and overlay port is used. When upstream accepts these changes and vcpkg bumps the version the patch could be removed to have just the cmake define.
This commit is contained in:
parent
1e6ac54b75
commit
3c7bad32cd
Notes:
github-actions[bot]
2025-10-08 05:09:20 +00:00
Author: https://github.com/R-Goc
Commit: 3c7bad32cd
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5225
Reviewed-by: https://github.com/gmta
11 changed files with 139 additions and 100 deletions
|
@ -22,9 +22,11 @@
|
|||
# define PRINT_ERROR(s) (void)::fputs((s), stderr)
|
||||
#endif
|
||||
|
||||
#if defined(AK_HAS_STD_STACKTRACE)
|
||||
# include <stacktrace>
|
||||
# include <string>
|
||||
#if defined(AK_HAS_CPPTRACE)
|
||||
# include <cpptrace/cpptrace.hpp>
|
||||
# include <cpptrace/formatting.hpp>
|
||||
# include <cstdlib>
|
||||
# include <iostream>
|
||||
#elif defined(AK_HAS_BACKTRACE_HEADER)
|
||||
# include <AK/StringBuilder.h>
|
||||
# include <AK/StringView.h>
|
||||
|
@ -39,22 +41,25 @@
|
|||
|
||||
extern "C" {
|
||||
|
||||
#if defined(AK_HAS_STD_STACKTRACE)
|
||||
void dump_backtrace()
|
||||
#if defined(AK_HAS_CPPTRACE)
|
||||
void dump_backtrace(unsigned frames_to_skip, unsigned max_depth)
|
||||
{
|
||||
// We assume the stacktrace implementation demangles symbols, as does microsoft/STL
|
||||
PRINT_ERROR(std::to_string(std::stacktrace::current(2)).c_str());
|
||||
PRINT_ERROR("\n");
|
||||
// We should be using cpptrace for everything but android.
|
||||
auto stacktrace = cpptrace::generate_trace(frames_to_skip, max_depth);
|
||||
auto* var = getenv("LADYBIRD_BACKTRACE_SNIPPETS");
|
||||
bool print_snippets = var && strnlen(var, 1) > 0;
|
||||
static auto formatter = cpptrace::formatter {}.snippets(print_snippets);
|
||||
formatter.print(std::cerr, stacktrace);
|
||||
}
|
||||
#elif defined(AK_HAS_BACKTRACE_HEADER)
|
||||
void dump_backtrace()
|
||||
void dump_backtrace(int frames_to_skip)
|
||||
{
|
||||
// Grab symbols and dso name for up to 256 frames
|
||||
void* trace[256] = {};
|
||||
int const num_frames = backtrace(trace, array_size(trace));
|
||||
char** syms = backtrace_symbols(trace, num_frames);
|
||||
|
||||
for (auto i = 0; i < num_frames; ++i) {
|
||||
for (auto i = frames_to_skip; 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]));
|
||||
StringBuilder error_builder;
|
||||
|
@ -93,7 +98,7 @@ void dump_backtrace()
|
|||
free(syms);
|
||||
}
|
||||
#else
|
||||
void dump_backtrace()
|
||||
void dump_backtrace([[maybe_unused]] int frames_to_skip)
|
||||
{
|
||||
PRINT_ERROR("dump_backtrace() is not supported with the current compilation options.\n");
|
||||
}
|
||||
|
@ -104,16 +109,27 @@ bool ak_colorize_output(void)
|
|||
#if defined(AK_OS_SERENITY) || defined(AK_OS_ANDROID)
|
||||
return true;
|
||||
#elif defined(AK_OS_WINDOWS)
|
||||
return false;
|
||||
HANDLE hStdErr = GetStdHandle(STD_ERROR_HANDLE);
|
||||
if (hStdErr == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
DWORD dwMode = 0;
|
||||
if (!GetConsoleMode(hStdErr, &dwMode)) {
|
||||
return false;
|
||||
}
|
||||
DWORD mask = ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
|
||||
return (dwMode & mask) == mask;
|
||||
#else
|
||||
return isatty(STDERR_FILENO) == 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ak_trap(void)
|
||||
NEVER_INLINE void ak_trap(void)
|
||||
{
|
||||
#if defined(AK_HAS_BACKTRACE_HEADER) || defined(AK_HAS_STD_STACKTRACE)
|
||||
dump_backtrace();
|
||||
#if defined(AK_HAS_BACKTRACE_HEADER) || defined(AK_HAS_CPPTRACE)
|
||||
// Skip 3 frames to get to caller. That is dump_backtrace, ak_trap, and ak_verification_failed.
|
||||
dump_backtrace(3, 100);
|
||||
#endif
|
||||
__builtin_trap();
|
||||
}
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
extern "C" void dump_backtrace();
|
||||
// All the functions for stack traces are never inline as we want a consistent number of frames
|
||||
extern "C" __attribute__((noinline)) void dump_backtrace(unsigned frames_to_skip = 1, unsigned max_depth = 100);
|
||||
extern "C" bool ak_colorize_output(void);
|
||||
extern "C" __attribute__((noreturn)) void ak_trap(void);
|
||||
extern "C" __attribute__((noreturn, noinline)) void ak_trap(void);
|
||||
|
||||
extern "C" __attribute__((noreturn)) void ak_verification_failed(char const*);
|
||||
extern "C" __attribute__((noreturn, noinline)) void ak_verification_failed(char const*);
|
||||
#define __stringify_helper(x) #x
|
||||
#define __stringify(x) __stringify_helper(x)
|
||||
#define VERIFY(...) \
|
||||
|
@ -25,7 +26,7 @@ static constexpr bool TODO = false;
|
|||
#define TODO_PPC64() VERIFY(TODO) /* NOLINT(cert-dcl03-c,misc-static-assert) No, this can't be static_assert, it's a runtime check */
|
||||
#define TODO_PPC() VERIFY(TODO) /* NOLINT(cert-dcl03-c,misc-static-assert) No, this can't be static_assert, it's a runtime check */
|
||||
|
||||
extern "C" __attribute__((noreturn)) void ak_assertion_failed(char const*);
|
||||
extern "C" __attribute__((noreturn, noinline)) void ak_assertion_failed(char const*);
|
||||
#ifndef NDEBUG
|
||||
# define ASSERT(...) \
|
||||
(__builtin_expect(/* NOLINT(readability-simplify-boolean-expr) */ !(__VA_ARGS__), 0) \
|
||||
|
|
|
@ -49,9 +49,23 @@ endif()
|
|||
|
||||
ladybird_lib(AK ak)
|
||||
|
||||
include(stacktrace)
|
||||
find_package(cpptrace CONFIG)
|
||||
find_package(Backtrace)
|
||||
|
||||
if(cpptrace_FOUND AND LADYBIRD_ENABLE_CPPTRACE)
|
||||
target_link_libraries(AK PRIVATE cpptrace::cpptrace)
|
||||
target_compile_definitions(AK PRIVATE AK_HAS_CPPTRACE=1)
|
||||
elseif(Backtrace_FOUND)
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.30)
|
||||
target_link_libraries(${target} PRIVATE Backtrace::Backtrace)
|
||||
else()
|
||||
target_include_directories(${target} PRIVATE ${Backtrace_INCLUDE_DIRS})
|
||||
target_link_libraries(${target} PRIVATE ${Backtrace_LIBRARIES})
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "Cpptrace and Backtrace.h not found. Stack traces will not be available.")
|
||||
endif()
|
||||
configure_file(Backtrace.h.in Backtrace.h @ONLY)
|
||||
link_stacktrace_library(AK STD_DEFINITION AK_HAS_STD_STACKTRACE)
|
||||
|
||||
find_package(simdutf REQUIRED)
|
||||
swizzle_target_properties_for_swift(simdutf::simdutf)
|
||||
|
|
|
@ -1321,6 +1321,7 @@ static int initialize_console_settings()
|
|||
|
||||
// Enable Virtual Terminal Processing to allow ANSI escape codes
|
||||
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
mode |= ENABLE_PROCESSED_OUTPUT;
|
||||
if (!SetConsoleMode(console_handle, mode)) {
|
||||
dbgln("Unable to set console mode");
|
||||
return 0;
|
||||
|
|
|
@ -5,8 +5,8 @@ endif()
|
|||
|
||||
# Enable better flags for configuring swift compilation mode
|
||||
if (POLICY CMP0157)
|
||||
cmake_policy(SET CMP0157 NEW)
|
||||
set(CMAKE_Swift_COMPILATION_MODE "$<IF:$<CONFIG:Release>,wholemodule,incremental>")
|
||||
cmake_policy(SET CMP0157 NEW)
|
||||
set(CMAKE_Swift_COMPILATION_MODE "$<IF:$<CONFIG:Release>,wholemodule,incremental>")
|
||||
endif()
|
||||
|
||||
# Check arguments to return()
|
||||
|
@ -42,7 +42,7 @@ ladybird_option(ENABLE_GUI_TARGETS ON CACHE BOOL "Enable building GUI targets")
|
|||
ladybird_option(ENABLE_INSTALL_HEADERS ON CACHE BOOL "Enable installing headers")
|
||||
ladybird_option(ENABLE_INSTALL_FREEDESKTOP_FILES ${freedesktop_files_default} CACHE BOOL "Enable installing .desktop and .service files")
|
||||
ladybird_option(ENABLE_SWIFT OFF CACHE BOOL "Enable building Swift files")
|
||||
ladybird_option(ENABLE_STD_STACKTRACE OFF CACHE BOOL "Force use of std::stacktrace instead of libbacktrace. If it is not supported the build will fail")
|
||||
ladybird_option(LADYBIRD_ENABLE_CPPTRACE ON CACHE BOOL "Enable use of cpptrace as the default library for stacktraces. If not available falls back to backtrace.h")
|
||||
ladybird_option(ENABLE_WINDOWS_CI OFF CACHE BOOL "Enable building targets supported on Windows for CI")
|
||||
ladybird_option(ENABLE_CI_BASELINE_CPU OFF CACHE BOOL "Use a baseline CPU target for improved ccache sharing")
|
||||
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
#
|
||||
# Provides definitions for stack trace support via libbacktrace or std::stacktrace
|
||||
#
|
||||
|
||||
include_guard()
|
||||
|
||||
find_package(Backtrace)
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
function(check_std_stacktrace link_lib library_target out_var)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${link_lib})
|
||||
set(check_var HAVE_STD_STACKTRACE_CHECK)
|
||||
if (link_lib)
|
||||
set(check_var "HAVE_STD_STACKTRACE_WITH_${link_lib}")
|
||||
endif()
|
||||
check_cxx_source_compiles("
|
||||
#include <version>
|
||||
#include <stacktrace>
|
||||
#include <iostream>
|
||||
#if !defined(__cpp_lib_stacktrace) || (__cpp_lib_stacktrace < 202011L)
|
||||
# error \"No std::stacktrace available\"
|
||||
#endif
|
||||
int main() {
|
||||
std::cout << std::stacktrace::current() << std::endl;
|
||||
return 0;
|
||||
}"
|
||||
${check_var}
|
||||
)
|
||||
set(${out_var} ${${check_var}})
|
||||
if (${out_var})
|
||||
target_link_libraries(${library_target} PRIVATE "${link_lib}")
|
||||
endif()
|
||||
return(PROPAGATE ${out_var})
|
||||
endfunction()
|
||||
|
||||
function(link_stacktrace_library target)
|
||||
cmake_parse_arguments(PARSE_ARGV 1 ARG "" "STD_DEFINITION" "")
|
||||
|
||||
if (Backtrace_FOUND AND NOT ENABLE_STD_STACKTRACE)
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.30)
|
||||
target_link_libraries(${target} PRIVATE Backtrace::Backtrace)
|
||||
else()
|
||||
target_include_directories(${target} PRIVATE ${Backtrace_INCLUDE_DIRS})
|
||||
target_link_libraries(${target} PRIVATE ${Backtrace_LIBRARIES})
|
||||
endif()
|
||||
else()
|
||||
check_std_stacktrace("" ${target} HAVE_STD_STACKTRACE)
|
||||
|
||||
if(NOT HAVE_STD_STACKTRACE AND CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14.1)
|
||||
check_std_stacktrace("stdc++exp" ${target} HAVE_STD_STACKTRACE)
|
||||
else()
|
||||
check_std_stacktrace("stdc++_libbacktrace" ${target} HAVE_STD_STACKTRACE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT HAVE_STD_STACKTRACE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang$")
|
||||
foreach(lib IN ITEMS "stdc++exp" "stdc++_libbacktrace" "c++experimental" )
|
||||
check_std_stacktrace("${lib}" ${target} HAVE_STD_STACKTRACE)
|
||||
if(HAVE_STD_STACKTRACE)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(HAVE_STD_STACKTRACE)
|
||||
target_compile_definitions(${target} PRIVATE ${ARG_STD_DEFINITION})
|
||||
else()
|
||||
set(msg_level WARNING)
|
||||
if (ENABLE_STD_STACKTRACE)
|
||||
set(msg_level FATAL_ERROR)
|
||||
endif()
|
||||
message(${msg_level} "Backtrace and <stacktrace> not found, stack traces will be unavailable")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
28
Meta/CMake/vcpkg/overlay-ports/cpptrace/delay.patch
Normal file
28
Meta/CMake/vcpkg/overlay-ports/cpptrace/delay.patch
Normal file
|
@ -0,0 +1,28 @@
|
|||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 78e0af04..b4aa973d 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -276,6 +276,11 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
SET(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
|
||||
endif()
|
||||
|
||||
+if(CPPTRACE_DELAYLOAD_DBGHELP AND BUILD_SHARED_LIBS AND MSVC)
|
||||
+ target_link_libraries(${target_name} PRIVATE delayimp.lib)
|
||||
+ target_link_options(${target_name} PRIVATE /DELAYLOAD:dbghelp.dll)
|
||||
+endif()
|
||||
+
|
||||
# =================================================== Back-end setup ===================================================
|
||||
|
||||
if(HAS_CXX_EXCEPTION_TYPE)
|
||||
diff --git a/cmake/OptionVariables.cmake b/cmake/OptionVariables.cmake
|
||||
index 417982bd..e726db11 100644
|
||||
--- a/cmake/OptionVariables.cmake
|
||||
+++ b/cmake/OptionVariables.cmake
|
||||
@@ -187,6 +187,7 @@ set(CPPTRACE_LIBDWARF_SHALLOW "1" CACHE STRING "")
|
||||
option(CPPTRACE_PROVIDE_EXPORT_SET "" ON)
|
||||
option(CPPTRACE_PROVIDE_EXPORT_SET_FOR_LIBDWARF "" OFF)
|
||||
option(CPPTRACE_DISABLE_CXX_20_MODULES "" OFF)
|
||||
+option(CPPTRACE_DELAYLOAD_DBGHELP "" OFF)
|
||||
|
||||
mark_as_advanced(
|
||||
CPPTRACE_BACKTRACE_PATH
|
25
Meta/CMake/vcpkg/overlay-ports/cpptrace/portfile.cmake
Normal file
25
Meta/CMake/vcpkg/overlay-ports/cpptrace/portfile.cmake
Normal file
|
@ -0,0 +1,25 @@
|
|||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
REPO jeremy-rifkin/cpptrace
|
||||
REF "v${VERSION}"
|
||||
SHA512 4ae394fb3c21149bf2441a754eebe639e6a5534927426b6507806c7bee0b1c982e047c972904d472f1c660adb5be3881e7e3a6eddd18e4e9d376ae3855d50a7c
|
||||
HEAD_REF main
|
||||
PATCHES
|
||||
delay.patch
|
||||
)
|
||||
|
||||
vcpkg_cmake_configure(
|
||||
SOURCE_PATH "${SOURCE_PATH}"
|
||||
OPTIONS -DCPPTRACE_USE_EXTERNAL_LIBDWARF=ON -DCPPTRACE_USE_EXTERNAL_ZSTD=ON -DCPPTRACE_VCPKG=ON -DCPPTRACE_DELAYLOAD_DBGHELP=ON
|
||||
)
|
||||
|
||||
vcpkg_cmake_install()
|
||||
vcpkg_cmake_config_fixup(
|
||||
PACKAGE_NAME "cpptrace"
|
||||
CONFIG_PATH "lib/cmake/cpptrace"
|
||||
)
|
||||
|
||||
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
|
||||
file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
|
||||
|
||||
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE")
|
4
Meta/CMake/vcpkg/overlay-ports/cpptrace/usage
Normal file
4
Meta/CMake/vcpkg/overlay-ports/cpptrace/usage
Normal file
|
@ -0,0 +1,4 @@
|
|||
cpptrace provides CMake targets:
|
||||
|
||||
find_package(cpptrace CONFIG REQUIRED)
|
||||
target_link_libraries(main PRIVATE cpptrace::cpptrace)
|
22
Meta/CMake/vcpkg/overlay-ports/cpptrace/vcpkg.json
Normal file
22
Meta/CMake/vcpkg/overlay-ports/cpptrace/vcpkg.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "cpptrace",
|
||||
"version": "1.0.2",
|
||||
"description": "Simple, portable, and self-contained stacktrace library for C++11 and newer",
|
||||
"homepage": "https://github.com/jeremy-rifkin/cpptrace",
|
||||
"license": "MIT",
|
||||
"supports": "!(uwp | android)",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "libdwarf",
|
||||
"platform": "!windows | mingw"
|
||||
},
|
||||
{
|
||||
"name": "vcpkg-cmake",
|
||||
"host": true
|
||||
},
|
||||
{
|
||||
"name": "vcpkg-cmake-config",
|
||||
"host": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -12,6 +12,10 @@
|
|||
"name": "angle",
|
||||
"platform": "linux | windows | android | freebsd"
|
||||
},
|
||||
{
|
||||
"name": "cpptrace",
|
||||
"platform": "linux | windows | freebsd | osx"
|
||||
},
|
||||
{
|
||||
"name": "curl",
|
||||
"default-features": false,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue