diff --git a/AK/Assertions.cpp b/AK/Assertions.cpp index 36a5fdc69e6..e3d26387dc0 100644 --- a/AK/Assertions.cpp +++ b/AK/Assertions.cpp @@ -22,9 +22,11 @@ # define PRINT_ERROR(s) (void)::fputs((s), stderr) #endif -#if defined(AK_HAS_STD_STACKTRACE) -# include -# include +#if defined(AK_HAS_CPPTRACE) +# include +# include +# include +# include #elif defined(AK_HAS_BACKTRACE_HEADER) # include # include @@ -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(); } diff --git a/AK/Assertions.h b/AK/Assertions.h index 1d339e4d46b..36cc69e38ad 100644 --- a/AK/Assertions.h +++ b/AK/Assertions.h @@ -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) \ diff --git a/AK/CMakeLists.txt b/AK/CMakeLists.txt index 1e05917e57d..a2fac82cf61 100644 --- a/AK/CMakeLists.txt +++ b/AK/CMakeLists.txt @@ -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) diff --git a/AK/Format.cpp b/AK/Format.cpp index ef7406876cf..c8af5f605d2 100644 --- a/AK/Format.cpp +++ b/AK/Format.cpp @@ -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; diff --git a/Meta/CMake/common_options.cmake b/Meta/CMake/common_options.cmake index f4b91a32147..cac86e732e4 100644 --- a/Meta/CMake/common_options.cmake +++ b/Meta/CMake/common_options.cmake @@ -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 "$,wholemodule,incremental>") + cmake_policy(SET CMP0157 NEW) + set(CMAKE_Swift_COMPILATION_MODE "$,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") diff --git a/Meta/CMake/stacktrace.cmake b/Meta/CMake/stacktrace.cmake deleted file mode 100644 index f23f4b53ea1..00000000000 --- a/Meta/CMake/stacktrace.cmake +++ /dev/null @@ -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 - #include - #include - #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 not found, stack traces will be unavailable") - endif() - endif() -endfunction() diff --git a/Meta/CMake/vcpkg/overlay-ports/cpptrace/delay.patch b/Meta/CMake/vcpkg/overlay-ports/cpptrace/delay.patch new file mode 100644 index 00000000000..dffea50b57e --- /dev/null +++ b/Meta/CMake/vcpkg/overlay-ports/cpptrace/delay.patch @@ -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 " -no_warning_for_no_symbols -c ") + 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 diff --git a/Meta/CMake/vcpkg/overlay-ports/cpptrace/portfile.cmake b/Meta/CMake/vcpkg/overlay-ports/cpptrace/portfile.cmake new file mode 100644 index 00000000000..75f7551be62 --- /dev/null +++ b/Meta/CMake/vcpkg/overlay-ports/cpptrace/portfile.cmake @@ -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") diff --git a/Meta/CMake/vcpkg/overlay-ports/cpptrace/usage b/Meta/CMake/vcpkg/overlay-ports/cpptrace/usage new file mode 100644 index 00000000000..eeebe202f3f --- /dev/null +++ b/Meta/CMake/vcpkg/overlay-ports/cpptrace/usage @@ -0,0 +1,4 @@ +cpptrace provides CMake targets: + + find_package(cpptrace CONFIG REQUIRED) + target_link_libraries(main PRIVATE cpptrace::cpptrace) diff --git a/Meta/CMake/vcpkg/overlay-ports/cpptrace/vcpkg.json b/Meta/CMake/vcpkg/overlay-ports/cpptrace/vcpkg.json new file mode 100644 index 00000000000..bb54c842158 --- /dev/null +++ b/Meta/CMake/vcpkg/overlay-ports/cpptrace/vcpkg.json @@ -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 + } + ] +} diff --git a/vcpkg.json b/vcpkg.json index 06c9863182f..3319d41b3cf 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -12,6 +12,10 @@ "name": "angle", "platform": "linux | windows | android | freebsd" }, + { + "name": "cpptrace", + "platform": "linux | windows | freebsd | osx" + }, { "name": "curl", "default-features": false,