diff --git a/CMakeLists.txt b/CMakeLists.txt index 9868fd7..bb484f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,6 +114,7 @@ find_package(assimp CONFIG REQUIRED) find_package(Bullet CONFIG REQUIRED) find_package(Vorbis CONFIG REQUIRED) find_package(glm CONFIG REQUIRED) +find_package(cpptrace REQUIRED) if(BUILD_SDL3_APP) add_executable(sdl3_app @@ -154,7 +155,7 @@ if(BUILD_SDL3_APP) src/services/impl/gui_renderer.cpp ) target_include_directories(sdl3_app PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src") - target_link_libraries(sdl3_app PRIVATE ${SDL_TARGET} Vulkan::Vulkan lua::lua CLI11::CLI11 rapidjson assimp::assimp Bullet::Bullet glm::glm Vorbis::vorbisfile Vorbis::vorbis) + target_link_libraries(sdl3_app PRIVATE ${SDL_TARGET} Vulkan::Vulkan lua::lua CLI11::CLI11 rapidjson assimp::assimp Bullet::Bullet glm::glm Vorbis::vorbisfile Vorbis::vorbis cpptrace) target_compile_definitions(sdl3_app PRIVATE SDL_MAIN_HANDLED) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/shaders") diff --git a/conanfile.py b/conanfile.py index f18e38c..8f8b182 100644 --- a/conanfile.py +++ b/conanfile.py @@ -25,6 +25,7 @@ class SDL3CppConan(ConanFile): self.requires("vulkan-headers/1.3.243.0") self.requires("vulkan-memory-allocator/3.3.0") self.requires("vulkan-validationlayers/1.3.243.0") + self.requires("cpptrace/1.0.4") self.requires("ogg/1.3.5") self.requires("theora/1.1.1") self.requires("cli11/2.6.0") diff --git a/src/services/impl/crash_recovery_service.cpp b/src/services/impl/crash_recovery_service.cpp index 827dd58..8119f66 100644 --- a/src/services/impl/crash_recovery_service.cpp +++ b/src/services/impl/crash_recovery_service.cpp @@ -3,12 +3,28 @@ #include #include #include +#include #include #include #include namespace sdl3cpp::services::impl { +namespace { +constexpr int kSignalExitCodeBase = 128; +constexpr size_t kStackTraceSkipFrames = 3; +constexpr size_t kStackTraceMaxDepth = 64; +volatile sig_atomic_t crashHandlingInProgress = 0; + +std::string BuildStackTrace() { + const auto trace = cpptrace::generate_trace(kStackTraceSkipFrames, kStackTraceMaxDepth); + if (trace.empty()) { + return {}; + } + return trace.to_string(); +} +} + // Static instance for signal handler CrashRecoveryService* CrashRecoveryService::instance_ = nullptr; @@ -111,6 +127,11 @@ std::string CrashRecoveryService::GetCrashReport() const { } void CrashRecoveryService::SignalHandler(int signal) { + if (crashHandlingInProgress != 0) { + _exit(kSignalExitCodeBase + signal); + } + crashHandlingInProgress = 1; + if (instance_) { if (instance_->logger_) { instance_->logger_->Trace("CrashRecoveryService", "SignalHandler", @@ -118,6 +139,8 @@ void CrashRecoveryService::SignalHandler(int signal) { } instance_->HandleCrash(signal); } + + _exit(kSignalExitCodeBase + signal); } void CrashRecoveryService::SetupSignalHandlers() { @@ -193,6 +216,12 @@ void CrashRecoveryService::HandleCrash(int signal) { crashReport_ = ss.str(); + const std::string stackTrace = BuildStackTrace(); + if (!stackTrace.empty()) { + crashReport_ += "\n=== STACK TRACE ===\n"; + crashReport_ += stackTrace; + } + logger_->Error("CrashRecoveryService::HandleCrash: " + crashReport_); // Note: In a real implementation, you might want to: