1616 */
1717#pragma once
1818
19+ #include < atomic>
1920#include < chrono>
2021#include < cstring>
2122#include < ctime>
2526#include < mutex>
2627#include < string>
2728#include < thread>
29+ #include < unordered_map>
2830
2931#ifdef _WIN32
30- # include < dbghelp.h>
3132# include < windows.h>
33+ # include < dbghelp.h>
3234# pragma comment(lib, "dbghelp.lib")
3335#elif defined(__APPLE__) || defined(__linux__)
3436# include < execinfo.h>
@@ -118,18 +120,7 @@ class CrashReporter
118120 */
119121 void HandleCrash (const std::string &message)
120122 {
121- std::lock_guard<std::mutex> lock (mutex);
122-
123- LOG_FATAL (" CrashReporter" , " Crash detected: " + message);
124-
125- // Generate minidump
126- GenerateMinidump (message);
127-
128- // Call registered callbacks
129- for (const auto &callback : crashCallbacks)
130- {
131- callback (message);
132- }
123+ HandleCrashInternal (message, nullptr );
133124 }
134125
135126 /* *
@@ -161,7 +152,7 @@ class CrashReporter
161152 * @brief Generate a minidump.
162153 * @param message The crash message.
163154 */
164- void GenerateMinidump (const std::string &message)
155+ void GenerateMinidump (const std::string &message, void *platformExceptionPointers = nullptr )
165156 {
166157 // Get current time for filename
167158 auto now = std::chrono::system_clock::now ();
@@ -171,12 +162,37 @@ class CrashReporter
171162
172163 // Create minidump filename
173164 std::string filename = minidumpDir + " /" + appName + " _" + timeStr + " .dmp" ;
165+ std::string report = minidumpDir + " /" + appName + " _" + timeStr + " .txt" ;
174166
175- LOG_INFO (" CrashReporter" , " Generating minidump: " + filename);
167+ // Also write a small sidecar text file so users can quickly see the exception code/address
168+ // without needing a debugger.
169+ try
170+ {
171+ std::ofstream rep (report, std::ios::out | std::ios::trunc);
172+ rep << " Crash Report for " << appName << " " << appVersion << " \n " ;
173+ rep << " Timestamp: " << timeStr << " \n " ;
174+ rep << " Message: " << message << " \n " ;
175+ #ifdef _WIN32
176+ if (platformExceptionPointers)
177+ {
178+ auto *exPtrs = reinterpret_cast <EXCEPTION_POINTERS *>(platformExceptionPointers);
179+ if (exPtrs && exPtrs->ExceptionRecord )
180+ {
181+ rep << " ExceptionCode: 0x" << std::hex << exPtrs->ExceptionRecord ->ExceptionCode << std::dec << " \n " ;
182+ rep << " ExceptionAddress: " << exPtrs->ExceptionRecord ->ExceptionAddress << " \n " ;
183+ rep << " ExceptionFlags: 0x" << std::hex << exPtrs->ExceptionRecord ->ExceptionFlags << std::dec << " \n " ;
184+ }
185+ }
186+ #endif
187+ }
188+ catch (...)
189+ {
190+ }
176191
177192// Generate minidump based on platform
178193#ifdef _WIN32
179194 // Windows implementation
195+ EXCEPTION_POINTERS *exPtrs = reinterpret_cast <EXCEPTION_POINTERS *>(platformExceptionPointers);
180196 HANDLE hFile = CreateFileA (
181197 filename.c_str (),
182198 GENERIC_WRITE,
@@ -188,19 +204,19 @@ class CrashReporter
188204
189205 if (hFile != INVALID_HANDLE_VALUE)
190206 {
191- MINIDUMP_EXCEPTION_INFORMATION exInfo;
207+ MINIDUMP_EXCEPTION_INFORMATION exInfo{} ;
192208 exInfo.ThreadId = GetCurrentThreadId ();
193- exInfo.ExceptionPointers = NULL ; // Would be set in a real exception handler
209+ exInfo.ExceptionPointers = exPtrs;
194210 exInfo.ClientPointers = FALSE ;
195211
196- MiniDumpWriteDump (
197- GetCurrentProcess (),
198- GetCurrentProcessId (),
199- hFile,
200- MiniDumpNormal,
201- &exInfo ,
202- NULL ,
203- NULL );
212+ MINIDUMP_EXCEPTION_INFORMATION *exInfoPtr = exPtrs ? &exInfo : nullptr ;
213+ MiniDumpWriteDump ( GetCurrentProcess (),
214+ GetCurrentProcessId (),
215+ hFile,
216+ MiniDumpNormal,
217+ exInfoPtr ,
218+ NULL ,
219+ NULL );
204220
205221 CloseHandle (hFile);
206222 }
@@ -232,7 +248,10 @@ class CrashReporter
232248 }
233249#endif
234250
235- LOG_INFO (" CrashReporter" , " Minidump generated: " + filename);
251+ // Best-effort stderr note (stdout/stderr redirection will capture this even if DebugSystem isn't initialized)
252+ std::fprintf (stderr, " [CrashReporter] Wrote minidump: %s\n " , filename.c_str ());
253+ std::fprintf (stderr, " [CrashReporter] Wrote report: %s\n " , report.c_str ());
254+ std::fflush (stderr);
236255 }
237256
238257 private:
@@ -259,6 +278,80 @@ class CrashReporter
259278 // Crash callbacks
260279 std::unordered_map<int , std::function<void (const std::string &)>> crashCallbacks;
261280 int nextCallbackId = 0 ;
281+ std::atomic<bool > handlingCrash{false };
282+
283+ #ifdef _WIN32
284+ static bool ShouldCaptureException (EXCEPTION_POINTERS *exInfo, bool unhandled)
285+ {
286+ if (unhandled)
287+ return true ;
288+ if (!exInfo || !exInfo->ExceptionRecord )
289+ return false ;
290+ const DWORD code = exInfo->ExceptionRecord ->ExceptionCode ;
291+ const DWORD flags = exInfo->ExceptionRecord ->ExceptionFlags ;
292+ // Ignore common first-chance C++ exceptions and breakpoint exceptions.
293+ if (code == 0xE06D7363u /* MSVC C++ EH */ || code == 0x80000003u /* breakpoint */ )
294+ return false ;
295+ // Capture likely-fatal errors and non-continuable exceptions.
296+ if ((flags & EXCEPTION_NONCONTINUABLE) != 0 )
297+ return true ;
298+ switch (code)
299+ {
300+ case 0xC0000409u : // STATUS_STACK_BUFFER_OVERRUN
301+ case 0xC0000005u : // STATUS_ACCESS_VIOLATION
302+ case 0xC000001Du : // STATUS_ILLEGAL_INSTRUCTION
303+ case 0xC00000FDu : // STATUS_STACK_OVERFLOW
304+ case 0xC0000374u : // STATUS_HEAP_CORRUPTION
305+ return true ;
306+ default :
307+ return false ;
308+ }
309+ }
310+ #endif
311+
312+ #ifdef _WIN32
313+ void *vectoredHandlerHandle = nullptr ;
314+ #endif
315+
316+ void HandleCrashInternal (const std::string &message, void *platformExceptionPointers)
317+ {
318+ bool expected = false ;
319+ if (!handlingCrash.compare_exchange_strong (expected, true ))
320+ {
321+ // Already handling a crash; avoid recursion.
322+ return ;
323+ }
324+ std::lock_guard<std::mutex> lock (mutex);
325+
326+ std::string msg = message;
327+ (void ) platformExceptionPointers;
328+
329+ #ifdef _WIN32
330+ if (platformExceptionPointers)
331+ {
332+ auto *exPtrs = reinterpret_cast <EXCEPTION_POINTERS *>(platformExceptionPointers);
333+ if (exPtrs && exPtrs->ExceptionRecord )
334+ {
335+ const DWORD code = exPtrs->ExceptionRecord ->ExceptionCode ;
336+ void *addr = exPtrs->ExceptionRecord ->ExceptionAddress ;
337+ char buf[128 ];
338+ std::snprintf (buf, sizeof (buf), " (code=0x%08lX, addr=%p)" , static_cast <unsigned long >(code), addr);
339+ msg += buf;
340+ }
341+ }
342+ #endif
343+
344+ LOG_FATAL (" CrashReporter" , " Crash detected: " + msg);
345+
346+ // Generate minidump
347+ GenerateMinidump (msg, platformExceptionPointers);
348+
349+ // Call registered callbacks
350+ for (const auto &callback : crashCallbacks)
351+ {
352+ callback.second (msg);
353+ }
354+ }
262355
263356 /* *
264357 * @brief Install platform-specific crash handlers.
@@ -267,8 +360,16 @@ class CrashReporter
267360 {
268361#ifdef _WIN32
269362 // Windows implementation
363+ // Vectored handler runs before SEH/unhandled filters and is more likely to fire for fast-fail style crashes.
364+ vectoredHandlerHandle = AddVectoredExceptionHandler (1 , [](EXCEPTION_POINTERS *exInfo) -> LONG {
365+ if (CrashReporter::ShouldCaptureException (exInfo, /* unhandled=*/ false ))
366+ {
367+ CrashReporter::GetInstance ().HandleCrashInternal (" Vectored exception" , exInfo);
368+ }
369+ return EXCEPTION_CONTINUE_SEARCH;
370+ });
270371 SetUnhandledExceptionFilter ([](EXCEPTION_POINTERS *exInfo) -> LONG {
271- CrashReporter::GetInstance ().HandleCrash (" Unhandled exception" );
372+ CrashReporter::GetInstance ().HandleCrashInternal (" Unhandled exception" , exInfo );
272373 return EXCEPTION_EXECUTE_HANDLER;
273374 });
274375#else
@@ -303,6 +404,11 @@ class CrashReporter
303404#ifdef _WIN32
304405 // Windows implementation
305406 SetUnhandledExceptionFilter (NULL );
407+ if (vectoredHandlerHandle)
408+ {
409+ RemoveVectoredExceptionHandler (vectoredHandlerHandle);
410+ vectoredHandlerHandle = nullptr ;
411+ }
306412#else
307413 // Unix implementation
308414 signal (SIGSEGV, SIG_DFL);
0 commit comments