diff --git a/.docker/build-linux.sh b/.docker/build-linux.sh index f0521c78..8b074a4b 100755 --- a/.docker/build-linux.sh +++ b/.docker/build-linux.sh @@ -154,8 +154,8 @@ if [[ "-$BUILD_TYPE-" == *-noperfmon-* ]]; then CMAKE_ARGS="${CMAKE_ARGS} -DENABLE_PERFMON=0 -DWITH_MAN_OPTION=0" fi -if [[ "-$BUILD_TYPE-" == *-nostacktrace-* ]]; then - CMAKE_ARGS="${CMAKE_ARGS} -DENABLE_STACKTRACE=0" +if [[ "-$BUILD_TYPE-" == *-stacktrace-* ]]; then + CMAKE_ARGS="${CMAKE_ARGS} -DENABLE_STACKTRACE=ON" fi if [[ "-$BUILD_TYPE-" == *-source-* ]]; then diff --git a/.docker/install-static-libs.sh b/.docker/install-static-libs.sh index e59d92ea..43e952b3 100644 --- a/.docker/install-static-libs.sh +++ b/.docker/install-static-libs.sh @@ -6,18 +6,18 @@ cd "$HOME" mkdir pkgs cd pkgs -# TODO: move to gcc-14 once libunwind compiles with it -GCC_VERSION=13 +GCC_VERSION=14 CLANG_VERSION=18 FILE_VERSION=5.45 FILE_SHA512=12611a59ff766c22a55db4b4a9f80f95a0a2e916a1d8593612c6ead32c247102a8fdc23693c6bf81bda9b604d951a62c0051e91580b1b79e190a3504c0efc20a BZIP2_VERSION=1.0.8 -LIBARCHIVE_VERSION=3.7.4 +LIBARCHIVE_VERSION=3.7.7 FLAC_VERSION=1.4.3 -LIBUNWIND_VERSION=1.7.2 -BENCHMARK_VERSION=1.8.5 -OPENSSL_VERSION=3.0.14 +# LIBUNWIND_VERSION=1.7.2 +BENCHMARK_VERSION=1.9.0 +OPENSSL_VERSION=3.0.15 +CPPTRACE_VERSION=0.7.2 RETRY=0 while true; do @@ -36,9 +36,10 @@ done wget https://sourceware.org/pub/bzip2/bzip2-${BZIP2_VERSION}.tar.gz wget https://github.com/libarchive/libarchive/releases/download/v${LIBARCHIVE_VERSION}/libarchive-${LIBARCHIVE_VERSION}.tar.xz wget https://github.com/xiph/flac/releases/download/${FLAC_VERSION}/flac-${FLAC_VERSION}.tar.xz -wget https://github.com/libunwind/libunwind/releases/download/v${LIBUNWIND_VERSION}/libunwind-${LIBUNWIND_VERSION}.tar.gz +# wget https://github.com/libunwind/libunwind/releases/download/v${LIBUNWIND_VERSION}/libunwind-${LIBUNWIND_VERSION}.tar.gz wget https://github.com/google/benchmark/archive/refs/tags/v${BENCHMARK_VERSION}.tar.gz wget https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_VERSION}/openssl-${OPENSSL_VERSION}.tar.gz +wget https://github.com/jeremy-rifkin/cpptrace/archive/refs/tags/v${CPPTRACE_VERSION}.tar.gz for COMPILER in clang gcc; do if [[ "$COMPILER" == "clang" ]]; then @@ -57,12 +58,12 @@ for COMPILER in clang gcc; do INSTALL_DIR=/opt/static-libs/$COMPILER - cd "$HOME/pkgs/$COMPILER" - tar xf ../libunwind-${LIBUNWIND_VERSION}.tar.gz - cd libunwind-${LIBUNWIND_VERSION} - ./configure --prefix="$INSTALL_DIR" - make -j$(nproc) - make install + # cd "$HOME/pkgs/$COMPILER" + # tar xf ../libunwind-${LIBUNWIND_VERSION}.tar.gz + # cd libunwind-${LIBUNWIND_VERSION} + # ./configure --prefix="$INSTALL_DIR" + # make -j$(nproc) + # make install cd "$HOME/pkgs/$COMPILER" tar xf ../bzip2-${BZIP2_VERSION}.tar.gz @@ -75,7 +76,7 @@ for COMPILER in clang gcc; do cd openssl-${OPENSSL_VERSION} ./Configure --prefix="$INSTALL_DIR" --libdir=lib threads no-fips no-shared no-pic no-dso make -j$(nproc) - make install + make install_sw cd "$HOME/pkgs/$COMPILER" tar xf ../libarchive-${LIBARCHIVE_VERSION}.tar.xz @@ -106,6 +107,15 @@ for COMPILER in clang gcc; do cmake .. -DBENCHMARK_ENABLE_TESTING=OFF -DCMAKE_INSTALL_PREFIX="$INSTALL_DIR" make -j$(nproc) make install + + cd "$HOME/pkgs/$COMPILER" + tar xf ../v${CPPTRACE_VERSION}.tar.gz + cd cpptrace-${CPPTRACE_VERSION} + mkdir build + cd build + cmake .. -DCMAKE_INSTALL_PREFIX="$INSTALL_DIR" + make -j$(nproc) + make install done cd "$HOME" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0cf1e0ad..6fc6b40c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -182,9 +182,6 @@ jobs: - arch: arm64v8 dist: ubuntu build_type: gcc-debug-shared-noperfmon-ninja-split - - arch: amd64 - dist: ubuntu - build_type: gcc-debug-shared-nostacktrace-ninja-split - arch: arm64v8 dist: ubuntu build_type: oldclang-debug-shared-make-split @@ -291,10 +288,10 @@ jobs: build_type: gcc-release-ninja-O2-static - arch: amd64 dist: ubuntu - build_type: clang-release-nostacktrace-ninja-static + build_type: clang-release-stacktrace-ninja-static - arch: arm64v8 dist: ubuntu - build_type: clang-release-nostacktrace-ninja-static + build_type: clang-release-stacktrace-ninja-static - arch: amd64 dist: ubuntu build_type: clang-release-ninja-static @@ -309,16 +306,16 @@ jobs: build_type: clang-release-ninja-O2-static - arch: amd64 dist: ubuntu - build_type: clang-reldbg-ninja-static + build_type: clang-reldbg-stacktrace-ninja-static - arch: arm64v8 dist: ubuntu - build_type: clang-reldbg-ninja-static + build_type: clang-reldbg-stacktrace-ninja-static - arch: amd64 dist: ubuntu - build_type: clang-debug-ninja-static + build_type: clang-debug-stacktrace-ninja-static - arch: arm64v8 dist: ubuntu - build_type: clang-debug-ninja-static + build_type: clang-debug-stacktrace-ninja-static runs-on: - self-hosted @@ -435,7 +432,7 @@ jobs: - name: Configure Full Build run: | rm -rf ${{ runner.temp }}/build - cmake --fresh -B${{ runner.temp }}/build -S${{ runner.workspace }}/dwarfs/dwarfs -GNinja -DCMAKE_BUILD_TYPE=${{ matrix.build_mode }} -DWITH_TESTS=ON -DWITH_PXATTR=ON ${{ matrix.config.cmake_args }} + cmake --fresh -B${{ runner.temp }}/build -S${{ runner.workspace }}/dwarfs/dwarfs -GNinja -DCMAKE_BUILD_TYPE=${{ matrix.build_mode }} -DWITH_TESTS=ON -DWITH_PXATTR=ON ${{ matrix.config.cmake_args }} -DENABLE_STACKTRACE=${{ matrix.build_mode == 'Debug' && 'ON' || 'OFF' }} - name: Run Full Build run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 673ad6ca..4da091fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,9 +39,7 @@ option(ENABLE_RICEPP "build with RICEPP compression support" ON) option(WITH_UNIVERSAL_BINARY "build with universal binary" OFF) option(WITH_PXATTR "build with pxattr binary" OFF) option(WITH_EXAMPLE "build with example binary" OFF) -if(NOT (APPLE OR WIN32)) - option(ENABLE_STACKTRACE "build with symbolizer support" ON) -endif() +option(ENABLE_STACKTRACE "build with stack trace support" OFF) if(APPLE) option(USE_HOMEBREW_LIBARCHIVE "use libarchive from homebrew" ON) endif() @@ -202,7 +200,7 @@ if(WITH_LIBDWARFS) endif() endif() - if(WIN32) + if(ENABLE_STACKTRACE) find_package(cpptrace REQUIRED CONFIG) endif() @@ -235,6 +233,7 @@ if(WITH_LIBDWARFS) include(${CMAKE_SOURCE_DIR}/cmake/libdwarfs.cmake) + set(DWARFS_HAVE_CPPTRACE ${cpptrace_FOUND}) set(DWARFS_HAVE_LIBZSTD ON) set(DWARFS_USE_JEMALLOC ${USE_JEMALLOC}) set(DWARFS_HAVE_RICEPP ${ENABLE_RICEPP}) @@ -657,7 +656,6 @@ foreach(tgt ${LIBDWARFS_TARGETS} ${LIBDWARFS_OBJECT_TARGETS} dwarfs_test_helpers if(WIN32) target_link_libraries(${tgt} PRIVATE ntdll.lib dbghelp.lib) - target_link_libraries(${tgt} PRIVATE cpptrace::cpptrace) endif() endforeach() diff --git a/cmake/dwarfs-config.cmake.in b/cmake/dwarfs-config.cmake.in index 342c6177..e4ebb2ea 100644 --- a/cmake/dwarfs-config.cmake.in +++ b/cmake/dwarfs-config.cmake.in @@ -29,6 +29,10 @@ pkg_check_modules(LIBARCHIVE REQUIRED IMPORTED_TARGET libarchive>=@LIBARCHIVE_RE pkg_check_modules(XXHASH REQUIRED IMPORTED_TARGET libxxhash>=@XXHASH_REQUIRED_VERSION@) pkg_check_modules(ZSTD REQUIRED IMPORTED_TARGET libzstd>=@ZSTD_REQUIRED_VERSION@) +if(@cpptrace_FOUND@) # cpptrace_FOUND + find_dependency(cpptrace CONFIG REQUIRED) +endif() + if(@LIBLZ4_FOUND@) # LIBLZ4_FOUND pkg_check_modules(LIBLZ4 REQUIRED IMPORTED_TARGET liblz4>=@LIBLZ4_REQUIRED_VERSION@) endif() diff --git a/cmake/folly.cmake b/cmake/folly.cmake index 7e7d03aa..4c17d79b 100644 --- a/cmake/folly.cmake +++ b/cmake/folly.cmake @@ -118,25 +118,6 @@ if(WIN32) ) endif() -if(ENABLE_STACKTRACE) - target_sources(dwarfs_folly_lite PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/debugging/symbolizer/Dwarf.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/debugging/symbolizer/DwarfImpl.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/debugging/symbolizer/DwarfLineNumberVM.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/debugging/symbolizer/DwarfSection.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/debugging/symbolizer/DwarfUtil.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/debugging/symbolizer/Elf.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/debugging/symbolizer/ElfCache.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/debugging/symbolizer/LineReader.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/debugging/symbolizer/SignalHandler.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/debugging/symbolizer/StackTrace.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/debugging/symbolizer/SymbolizePrinter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/debugging/symbolizer/SymbolizedFrame.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/debugging/symbolizer/Symbolizer.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/tracing/AsyncStack.cpp - ) -endif() - set_property(TARGET dwarfs_folly_lite PROPERTY CXX_STANDARD 20) target_include_directories( dwarfs_folly_lite SYSTEM PUBLIC @@ -147,7 +128,7 @@ apply_folly_compile_options_to_target(dwarfs_folly_lite) target_link_libraries(dwarfs_folly_lite PUBLIC folly_deps) if(WITH_BENCHMARKS) - add_library(dwarfs_follybenchmark_lite OBJECT + add_library(dwarfs_follybenchmark_lite OBJECT ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/Benchmark.cpp ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/Format.cpp ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/Unicode.cpp @@ -192,13 +173,8 @@ if(WITH_BENCHMARKS) target_link_libraries(dwarfs_follybenchmark_lite PUBLIC dwarfs_folly_lite) endif() -if(ENABLE_STACKTRACE OR WITH_BENCHMARKS) - if(ENABLE_STACKTRACE) - set(_target dwarfs_folly_lite) - else() - set(_target dwarfs_follybenchmark_lite) - endif() - target_sources(${_target} PRIVATE +if(WITH_BENCHMARKS) + target_sources(dwarfs_follybenchmark_lite PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/SharedMutex.cpp ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/concurrency/CacheLocality.cpp ${CMAKE_CURRENT_SOURCE_DIR}/folly/folly/detail/Futex.cpp diff --git a/cmake/libdwarfs.cmake b/cmake/libdwarfs.cmake index fe73d05b..90e20f77 100644 --- a/cmake/libdwarfs.cmake +++ b/cmake/libdwarfs.cmake @@ -201,6 +201,10 @@ if(LIBBROTLIDEC_FOUND AND LIBBROTLIENC_FOUND) target_link_libraries(dwarfs_common PRIVATE PkgConfig::LIBBROTLIDEC PkgConfig::LIBBROTLIENC) endif() +if(ENABLE_STACKTRACE) + target_link_libraries(dwarfs_common PRIVATE cpptrace::cpptrace) +endif() + target_link_libraries(dwarfs_extractor PRIVATE PkgConfig::LIBARCHIVE) target_include_directories(dwarfs_common SYSTEM PRIVATE $) diff --git a/src/logger.cpp b/src/logger.cpp index 4aa60d82..0d3ddf0d 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -30,11 +30,7 @@ #include #ifdef DWARFS_STACKTRACE_ENABLED -#include - -#if !(FOLLY_USE_SYMBOLIZER && FOLLY_HAVE_DWARF && FOLLY_HAVE_ELF) -#error "folly symbolizer is unavailable" -#endif +#include #endif #include @@ -180,15 +176,7 @@ void stream_logger::write(level_type level, const std::string& output, std::vector st_lines; if (enable_stack_trace_ || level == FATAL) { - using namespace folly::symbolizer; - Symbolizer symbolizer(LocationInfoMode::FULL); - FrameArray<8> addresses; - getStackTraceSafe(addresses); - symbolizer.symbolize(addresses); - folly::symbolizer::StringSymbolizePrinter printer( - color_ ? folly::symbolizer::SymbolizePrinter::COLOR : 0); - printer.println(addresses, 3); - stacktrace = printer.str(); + stacktrace = cpptrace::generate_trace().to_string(true); split_to(stacktrace, '\n', st_lines); if (st_lines.back().empty()) { st_lines.pop_back(); diff --git a/src/util.cpp b/src/util.cpp index c485db3e..955bcc63 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -45,13 +45,12 @@ #include -#ifdef _WIN32 +#ifdef DWARFS_STACKTRACE_ENABLED #include #include + +#ifdef _WIN32 #include -#else -#ifdef DWARFS_STACKTRACE_ENABLED -#include #endif #endif @@ -364,10 +363,28 @@ int get_current_umask() { return mask; } -#ifdef _WIN32 +#ifdef DWARFS_STACKTRACE_ENABLED namespace { +struct fatal_signal { + int signum; + std::string_view name; +}; + +static constexpr std::array kFatalSignals{ + fatal_signal{SIGSEGV, "SIGSEGV"}, + fatal_signal{SIGILL, "SIGILL"}, + fatal_signal{SIGFPE, "SIGFPE"}, + fatal_signal{SIGABRT, "SIGABRT"}, + fatal_signal{SIGTERM, "SIGTERM"}, +#ifndef _WIN32 + fatal_signal{SIGBUS, "SIGBUS"}, +#endif +}; + +#ifdef _WIN32 + std::vector suspend_other_threads() { std::vector handles; DWORD currend_tid = ::GetCurrentThreadId(); @@ -411,25 +428,79 @@ void resume_suspended_threads(const std::vector& handles) { } } -void abort_handler(int signal) { +void fatal_signal_handler_win(int signal) { auto suspended = suspend_other_threads(); - std::cerr << "Caught signal " << signal << "\n"; + + std::optional signame; + + for (size_t i = 0; i < kFatalSignals.size(); ++i) { + if (signal == kFatalSignals[i].signum) { + signame = kFatalSignals[i].name; + break; + } + } + + if (!signame) { + signame = std::to_string(signal); + } + + std::cerr << "Caught signal " << *signame << "\n"; cpptrace::generate_trace().print(); + resume_suspended_threads(suspended); + ::exit(1); } +#else + +std::array old_handlers; + +void fatal_signal_handler_posix(int signal) { + std::optional signame; + + for (size_t i = 0; i < kFatalSignals.size(); ++i) { + if (signal == kFatalSignals[i].signum) { + ::sigaction(signal, &old_handlers[i], nullptr); + signame = kFatalSignals[i].name; + break; + } + } + + if (!signame) { + struct ::sigaction sa_dfl; + ::memset(&sa_dfl, 0, sizeof(sa_dfl)); + sa_dfl.sa_handler = SIG_DFL; + ::sigaction(signal, &sa_dfl, nullptr); + signame = std::to_string(signal); + } + + std::cerr << "Caught signal " << *signame << "\n"; + cpptrace::generate_trace().print(); + + ::raise(signal); +} + +#endif + } // namespace #endif void install_signal_handlers() { +#ifdef DWARFS_STACKTRACE_ENABLED + for (size_t i = 0; i < kFatalSignals.size(); ++i) { #ifdef _WIN32 - ::signal(SIGABRT, abort_handler); + ::signal(kFatalSignals[i].signum, fatal_signal_handler_win); #else -#ifdef DWARFS_STACKTRACE_ENABLED - folly::symbolizer::installFatalSignalHandler(); + struct ::sigaction new_sa; + ::memset(&new_sa, 0, sizeof(new_sa)); + // this is potentially implemented as a macro + sigfillset(&new_sa.sa_mask); + new_sa.sa_handler = fatal_signal_handler_posix; + ::sigaction(kFatalSignals[i].signum, &new_sa, &old_handlers[i]); #endif + } #endif }