diff --git a/.docker/build-linux.sh b/.docker/build-linux.sh index c35d38ba..f3a322c3 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..b173bfa8 100644 --- a/.docker/install-static-libs.sh +++ b/.docker/install-static-libs.sh @@ -13,11 +13,12 @@ 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 +BENCHMARK_VERSION=1.9.0 +OPENSSL_VERSION=3.0.15 +CPPTRACE_VERSION=0.7.2 RETRY=0 while true; do @@ -39,6 +40,7 @@ wget https://github.com/xiph/flac/releases/download/${FLAC_VERSION}/flac-${FLAC_ 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 @@ -75,7 +77,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 +108,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 b5391643..f94e4399 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,7 +49,7 @@ jobs: - name: Configure Build shell: cmd run: | - cmake -B${{ runner.temp }}\build -S${{ runner.workspace }}\dwarfs -GNinja -DCMAKE_MAKE_PROGRAM=C:\bin\ninja.exe -DCMAKE_BUILD_TYPE=${{ matrix.build_mode }} -DWITH_UNIVERSAL_BINARY=ON -DWITH_TESTS=ON ${{ matrix.config.cmake_args }} -DCMAKE_TOOLCHAIN_FILE=C:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DVCPKG_INSTALLED_DIR=${{ runner.workspace }}\vcpkg-install-dwarfs -DDWARFS_ARTIFACTS_DIR=Z:\artifacts\dwarfs -DWITH_PXATTR=ON + cmake -B${{ runner.temp }}\build -S${{ runner.workspace }}\dwarfs -GNinja -DCMAKE_MAKE_PROGRAM=C:\bin\ninja.exe -DCMAKE_BUILD_TYPE=${{ matrix.build_mode }} -DWITH_UNIVERSAL_BINARY=ON -DWITH_TESTS=ON ${{ matrix.config.cmake_args }} -DCMAKE_TOOLCHAIN_FILE=C:\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DVCPKG_INSTALLED_DIR=${{ runner.workspace }}\vcpkg-install-dwarfs -DDWARFS_ARTIFACTS_DIR=Z:\artifacts\dwarfs -DWITH_PXATTR=ON -DENABLE_STACKTRACE=${{ matrix.build_mode == 'Debug' && 'ON' || 'OFF' }} - name: Run Build shell: cmd @@ -186,9 +186,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 @@ -295,10 +292,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 @@ -313,16 +310,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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 673ad6ca..82ac9be3 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}) @@ -274,6 +273,9 @@ if(WITH_TOOLS) list(APPEND BINARY_TARGETS ${tgt}) install(TARGETS ${tgt} RUNTIME DESTINATION bin) + if(WIN32) + install(FILES $ DESTINATION bin OPTIONAL) + endif() endforeach() target_link_libraries(mkdwarfs_main PRIVATE dwarfs_reader dwarfs_writer dwarfs_rewrite) @@ -296,6 +298,9 @@ if(WITH_PXATTR) add_executable(pxattr tools/src/pxattr.cpp) list(APPEND BINARY_TARGETS pxattr) install(TARGETS pxattr RUNTIME DESTINATION bin) + if(WIN32) + install(FILES $ DESTINATION bin OPTIONAL) + endif() endif() if(WITH_EXAMPLE) @@ -336,6 +341,7 @@ if(WITH_FUSE_DRIVER) endif() if(WINFSP) install(TARGETS dwarfs-bin RUNTIME DESTINATION bin) + install(FILES $ DESTINATION bin OPTIONAL) else() add_custom_command(OUTPUT mount.dwarfs COMMAND ${CMAKE_COMMAND} -E create_symlink dwarfs mount.dwarfs @@ -657,7 +663,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 baaee7d2..feb4474a 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 a0346cd1..18f1dd89 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -32,11 +32,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 @@ -207,15 +203,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 a7c54ae3..b0609c14 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #if __has_include() @@ -45,13 +47,12 @@ #include -#ifdef _WIN32 +#ifdef DWARFS_STACKTRACE_ENABLED #include #include + +#ifdef _WIN32 #include -#else -#ifdef DWARFS_STACKTRACE_ENABLED -#include #endif #endif @@ -399,10 +400,28 @@ int get_current_umask() { return mask; } -#ifdef _WIN32 +#ifdef DWARFS_STACKTRACE_ENABLED namespace { +struct fatal_signal { + int signum; + std::string_view name; +}; + +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 +}; + +std::once_flag g_signal_handlers_installed; + +#ifdef _WIN32 + std::vector suspend_other_threads() { std::vector handles; DWORD currend_tid = ::GetCurrentThreadId(); @@ -446,25 +465,83 @@ 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 + +void install_signal_handlers_impl() { + for (size_t i = 0; i < kFatalSignals.size(); ++i) { +#ifdef _WIN32 + ::signal(kFatalSignals[i].signum, fatal_signal_handler_win); +#else + 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 + } +} + } // namespace #endif void install_signal_handlers() { -#ifdef _WIN32 - ::signal(SIGABRT, abort_handler); -#else #ifdef DWARFS_STACKTRACE_ENABLED - folly::symbolizer::installFatalSignalHandler(); -#endif + std::call_once(g_signal_handlers_installed, install_signal_handlers_impl); #endif }