AK: Add stacktrace support using the standard lib

This commit adds support for using the standard library implementation
of <stacktrace> if libbacktrace is not found. This can also be
explicitly enabled through ENABLE_STD_STACKTRACE for platforms that have
libbacktrace available.

Co-Authored-By: Andrew Kaster <andrew@ladybird.org>
This commit is contained in:
R-Goc 2025-02-08 23:05:42 +01:00 committed by Andrew Kaster
commit fe26c3145d
Notes: github-actions[bot] 2025-03-05 17:26:11 +00:00
4 changed files with 103 additions and 18 deletions

View file

@ -8,18 +8,22 @@
#include <AK/Backtrace.h>
#include <AK/Format.h>
#include <AK/Platform.h>
#include <AK/StringBuilder.h>
#include <AK/StringView.h>
#if defined(AK_OS_ANDROID) && (__ANDROID_API__ >= 33)
# include <android/log.h>
# define EXECINFO_BACKTRACE
# define PRINT_ERROR(s) __android_log_write(ANDROID_LOG_WARN, "AK", (s))
#else
# define PRINT_ERROR(s) (void)fputs((s), stderr)
# include <stdio.h>
# define PRINT_ERROR(s) (void)::fputs((s), stderr)
#endif
#if defined(AK_HAS_BACKTRACE_HEADER)
#if defined(AK_HAS_STD_STACKTRACE)
# include <stacktrace>
# include <string>
#elif defined(AK_HAS_BACKTRACE_HEADER)
# include <AK/StringBuilder.h>
# include <AK/StringView.h>
# include <cxxabi.h>
#endif
@ -29,7 +33,16 @@
# define ERRORLN warnln
#endif
#if defined(AK_HAS_BACKTRACE_HEADER)
#if defined(AK_HAS_STD_STACKTRACE)
namespace {
ALWAYS_INLINE void dump_backtrace()
{
// 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");
}
}
#elif defined(AK_HAS_BACKTRACE_HEADER)
namespace {
ALWAYS_INLINE void dump_backtrace()
{
@ -94,7 +107,7 @@ bool ak_colorize_output(void)
void ak_trap(void)
{
#if defined(AK_HAS_BACKTRACE_HEADER)
#if defined(AK_HAS_BACKTRACE_HEADER) || defined(AK_HAS_STD_STACKTRACE)
dump_backtrace();
#endif
__builtin_trap();

View file

@ -49,19 +49,9 @@ serenity_lib(AK ak)
serenity_install_headers(AK)
serenity_install_sources(AK)
find_package(Backtrace)
include(stacktrace)
configure_file(Backtrace.h.in Backtrace.h @ONLY)
if (Backtrace_FOUND)
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.30)
target_link_libraries(AK PRIVATE Backtrace::Backtrace)
else()
target_include_directories(AK PRIVATE ${Backtrace_INCLUDE_DIRS})
target_link_libraries(AK PRIVATE ${Backtrace_LIBRARIES})
endif()
else()
message(WARNING "Backtrace not found, stack traces will be unavailable")
endif()
link_stacktrace_library(AK STD_DEFINITION AK_HAS_STD_STACKTRACE)
find_package(simdutf REQUIRED)
swizzle_target_properties_for_swift(simdutf::simdutf)

View file

@ -13,6 +13,11 @@ if (POLICY CMP0157)
set(CMAKE_Swift_COMPILATION_MODE "$<IF:$<CONFIG:Release>,wholemodule,incremental>")
endif()
# Check arguments to return()
if (POLICY CMP0140)
cmake_policy(SET CMP0140 NEW)
endif()
serenity_option(ENABLE_COMPILETIME_FORMAT_CHECK ON CACHE BOOL "Enable compiletime format string checks")
serenity_option(ENABLE_UNDEFINED_SANITIZER OFF CACHE BOOL "Enable undefined behavior sanitizer testing in gcc/clang")
serenity_option(UNDEFINED_BEHAVIOR_IS_FATAL OFF CACHE BOOL "Make undefined behavior sanitizer errors non-recoverable")
@ -35,6 +40,7 @@ serenity_option(ENABLE_CLANG_PLUGINS_INVALID_FUNCTION_MEMBERS OFF CACHE BOOL "En
serenity_option(ENABLE_GUI_TARGETS ON CACHE BOOL "Enable building GUI targets")
serenity_option(ENABLE_INSTALL_HEADERS ON CACHE BOOL "Enable installing headers")
serenity_option(ENABLE_SWIFT OFF CACHE BOOL "Enable building Swift files")
serenity_option(ENABLE_STD_STACKTRACE OFF CACHE BOOL "Force use of std::stacktrace instead of libbacktrace. If it is not supported the build will fail")
if (ENABLE_SWIFT)
include(${CMAKE_CURRENT_LIST_DIR}/Swift/swift-settings.cmake)

View file

@ -0,0 +1,76 @@
#
# 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()