4545#include < cstdlib>
4646#include < unordered_map>
4747
48+ #if defined(__linux__) || defined(__APPLE__)
49+ #include < signal.h>
50+ #include < unistd.h>
51+ #elif defined(WIN32)
52+ #include < windows.h>
53+ #endif
54+
4855#if defined(__unix__)
4956extern char ** environ;
5057#endif
@@ -70,6 +77,10 @@ static std::mutex external_trim_trigger_mutex_g;
7077static bool externally_set_trimming_state_g = false ;
7178static bool previous_externally_set_trimming_state_g = false ;
7279
80+ std::mutex CommonCaptureManager::crash_thread_lock_;
81+ std::vector<util::ThreadData*> CommonCaptureManager::crash_threads_;
82+ std::atomic<bool > CommonCaptureManager::crash_handler_installed_{ false };
83+
7384extern " C"
7485{
7586 // The following two functions are made public and should be discoverable via dlsym.
@@ -427,6 +438,15 @@ bool CommonCaptureManager::Initialize(format::ApiFamilyId api_
427438 use_asset_file_ = trace_settings.use_asset_file ;
428439 ignore_frame_boundary_android_ = trace_settings.ignore_frame_boundary_android ;
429440 skip_threads_with_invalid_data_ = trace_settings.skip_threads_with_invalid_data ;
441+ crash_command_enabled_ = trace_settings.capture_crash_command ;
442+
443+ if (crash_command_enabled_)
444+ {
445+ // Crash command capture requires force flush to ensure all prior calls are on disk.
446+ force_file_flush_ = true ;
447+ GFXRECON_LOG_INFO (" Crash command capture enabled. Force file flush activated." );
448+ InstallCrashHandler ();
449+ }
430450
431451 rv_annotation_info_.gpuva_mask = trace_settings.rv_anotation_info .gpuva_mask ;
432452 rv_annotation_info_.descriptor_mask = trace_settings.rv_anotation_info .descriptor_mask ;
@@ -1688,6 +1708,135 @@ void CommonCaptureManager::WriteToFile(const void* data, size_t size, util::File
16881708 ++block_index_;
16891709}
16901710
1711+ void CommonCaptureManager::InstallCrashHandler ()
1712+ {
1713+ bool expected = false ;
1714+ if (!crash_handler_installed_.compare_exchange_strong (expected, true ))
1715+ {
1716+ return ; // Already installed.
1717+ }
1718+
1719+ #if defined(__linux__) || defined(__APPLE__)
1720+ struct sigaction sa = {};
1721+ sa.sa_handler = CrashSignalHandler;
1722+ sa.sa_flags = SA_RESETHAND ; // One-shot: restore default handler after first invocation.
1723+ sigemptyset (&sa.sa_mask );
1724+
1725+ sigaction (SIGSEGV , &sa, nullptr );
1726+ sigaction (SIGABRT , &sa, nullptr );
1727+ sigaction (SIGBUS , &sa, nullptr );
1728+ sigaction (SIGFPE , &sa, nullptr );
1729+ sigaction (SIGILL , &sa, nullptr );
1730+ #elif defined(WIN32)
1731+ SetUnhandledExceptionFilter ([](EXCEPTION_POINTERS *) -> LONG {
1732+ CrashSignalHandler (0 );
1733+ return EXCEPTION_CONTINUE_SEARCH ;
1734+ });
1735+ #endif
1736+
1737+ GFXRECON_LOG_INFO (" Crash signal handler installed for capture recovery." );
1738+ }
1739+
1740+ void CommonCaptureManager::CrashSignalHandler (int signal)
1741+ {
1742+ // This runs in a signal handler context. Only async-signal-safe operations are allowed.
1743+ // We avoid mutexes, malloc, and logging here. We use direct file descriptor writes.
1744+ CommonCaptureManager* manager = singleton_;
1745+ if (manager == nullptr || manager->file_stream_ == nullptr )
1746+ {
1747+ return ;
1748+ }
1749+
1750+ // First flush any buffered data from prior completed writes.
1751+ manager->file_stream_ ->Flush ();
1752+
1753+ // Scan all registered threads for in-flight calls and write their pre-call buffers.
1754+ // Note: We don't lock crash_thread_lock_ because we're in a signal handler and the
1755+ // mutex may be held by the crashing thread. The vector is append-only during normal
1756+ // operation, so reading it without the lock is safe enough for crash recovery.
1757+ for (auto * thread_data : crash_threads_)
1758+ {
1759+ format::ApiCallId call_id = thread_data->inflight_call_id_ .load (std::memory_order_acquire);
1760+ if (call_id != format::ApiCallId::ApiCall_Unknown && thread_data->precall_data_size_ > 0 )
1761+ {
1762+ // Write the pre-call function call block to the capture file.
1763+ manager->file_stream_ ->Write (thread_data->precall_buffer_ .data (), thread_data->precall_data_size_ );
1764+ }
1765+ }
1766+
1767+ // Final flush to ensure the crash command data reaches disk.
1768+ manager->file_stream_ ->Flush ();
1769+
1770+ // Re-raise the signal with default handler (SA_RESETHAND already restored it).
1771+ #if defined(__linux__) || defined(__APPLE__)
1772+ raise (signal);
1773+ #endif
1774+ }
1775+
1776+ void CommonCaptureManager::RegisterThreadForCrashCapture ()
1777+ {
1778+ auto thread_data = GetThreadData ();
1779+ std::lock_guard<std::mutex> lock (crash_thread_lock_);
1780+ crash_threads_.push_back (thread_data);
1781+ }
1782+
1783+ void CommonCaptureManager::SavePreCallData (format::ApiCallId call_id)
1784+ {
1785+ if (!crash_command_enabled_)
1786+ {
1787+ return ;
1788+ }
1789+
1790+ auto thread_data = GetThreadData ();
1791+
1792+ // Lazy-register this thread for crash capture tracking.
1793+ if (!thread_data->crash_capture_registered_ )
1794+ {
1795+ std::lock_guard<std::mutex> lock (crash_thread_lock_);
1796+ crash_threads_.push_back (thread_data);
1797+ thread_data->crash_capture_registered_ = true ;
1798+ }
1799+
1800+ // Build an uncompressed FunctionCallBlock for the call ID with no parameter data.
1801+ // This serves as a "this call was in-flight when we crashed" marker.
1802+ format::FunctionCallHeader header = {};
1803+ header.block_header .type = format::BlockType::kFunctionCallBlock ;
1804+ header.api_call_id = call_id;
1805+ header.thread_id = thread_data->thread_id_ ;
1806+ header.block_header .size = sizeof (header.api_call_id ) + sizeof (header.thread_id );
1807+
1808+ size_t total_size = sizeof (header);
1809+ if (thread_data->precall_buffer_ .size () < total_size)
1810+ {
1811+ thread_data->precall_buffer_ .resize (total_size);
1812+ }
1813+ memcpy (thread_data->precall_buffer_ .data (), &header, sizeof (header));
1814+ thread_data->precall_data_size_ = total_size;
1815+
1816+ // Mark this thread as having an in-flight call. This must be set AFTER the buffer is populated.
1817+ thread_data->inflight_call_id_ .store (call_id, std::memory_order_release);
1818+ }
1819+
1820+ void CommonCaptureManager::ClearPreCallData ()
1821+ {
1822+ if (!crash_command_enabled_)
1823+ {
1824+ return ;
1825+ }
1826+
1827+ auto thread_data = GetThreadData ();
1828+ thread_data->inflight_call_id_ .store (format::ApiCallId::ApiCall_Unknown, std::memory_order_release);
1829+ thread_data->precall_data_size_ = 0 ;
1830+ }
1831+
1832+ void CommonCaptureManager::FlushCaptureFile ()
1833+ {
1834+ if (file_stream_ != nullptr )
1835+ {
1836+ file_stream_->Flush ();
1837+ }
1838+ }
1839+
16911840void CommonCaptureManager::AtExit ()
16921841{
16931842 if (CommonCaptureManager::singleton_)
0 commit comments