21#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
26#elif defined(_WIN32) || defined(_WIN64)
33#if defined(DBGHELP_SUPPORT)
37#pragma warning(disable:4091)
50class ExceptionsHandler::Impl
53 Impl() : _initialized(false), _handler(ExceptionsHandler::Impl::DefaultHandler) {}
55 static ExceptionsHandler::Impl& GetInstance()
56 {
return ExceptionsHandler::GetInstance().impl(); }
58 void SetupHandler(
const std::function<
void (
const SystemException&,
const StackTrace&)>& handler)
60 assert((handler) &&
"Exceptions handler function must be valid!");
73#if defined(_WIN32) || defined(_WIN64)
75 SetUnhandledExceptionFilter(SehHandler);
83 _set_purecall_handler(PureCallHandler);
86 _set_new_handler(NewHandler);
89 _set_invalid_parameter_handler(InvalidParameterHandler);
92 _set_abort_behavior(_CALL_REPORTFAULT, _CALL_REPORTFAULT);
96 signal(SIGABRT, SigabrtHandler);
99 signal(SIGINT, SigintHandler);
102 signal(SIGTERM, SigtermHandler);
103#elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
106 memset(&sa, 0,
sizeof(sa));
107 sa.sa_sigaction = SignalHandler;
108 sa.sa_flags = SA_SIGINFO;
131 for (
size_t i = 0; i <
countof(signals); ++i)
133 int result = sigaction(signals[i], &sa,
nullptr);
135 throwex SystemException(
format(
"Failed to setup signal handler - {}", signals[i]));
142 static void SetupThread()
144#if defined(_WIN32) || defined(_WIN64)
151 std::set_terminate(TerminateHandler);
153#if (__cplusplus < 201703L)
159 std::set_unexpected(UnexpectedHandler);
164 typedef void (*sigh)(int);
165 signal(SIGFPE, (sigh)SigfpeHandler);
168 signal(SIGILL, SigillHandler);
171 signal(SIGSEGV, SigsegvHandler);
179 std::function<void (
const SystemException&,
const StackTrace&)> _handler;
182 static void DefaultHandler(
const SystemException& exception,
const StackTrace& trace)
184 std::cerr << exception;
185 std::cerr <<
"Stack trace:" << std::endl;
189#if defined(_WIN32) || defined(_WIN64)
192 static LONG WINAPI SehHandler(PEXCEPTION_POINTERS pExceptionPtrs)
195 GetInstance()._handler(
__LOCATION__ + SystemException(
"Unhandled SEH exception"), StackTrace(1));
198 CreateDumpFile(pExceptionPtrs);
201 TerminateProcess(GetCurrentProcess(), 1);
204 return EXCEPTION_EXECUTE_HANDLER;
209 static void __cdecl TerminateHandler()
212 GetInstance()._handler(
__LOCATION__ + SystemException(
"Abnormal program termination (terminate() function was called)"), StackTrace(1));
215 EXCEPTION_POINTERS* pExceptionPtrs =
nullptr;
216 GetExceptionPointers(0, &pExceptionPtrs);
219 CreateDumpFile(pExceptionPtrs);
222 DeleteExceptionPointers(pExceptionPtrs);
225 TerminateProcess(GetCurrentProcess(), 1);
229 static void __cdecl UnexpectedHandler()
232 GetInstance()._handler(
__LOCATION__ + SystemException(
"Unexpected error (unexpected() function was called)"), StackTrace(1));
235 EXCEPTION_POINTERS* pExceptionPtrs =
nullptr;
236 GetExceptionPointers(0, &pExceptionPtrs);
239 CreateDumpFile(pExceptionPtrs);
242 DeleteExceptionPointers(pExceptionPtrs);
245 TerminateProcess(GetCurrentProcess(), 1);
250 static void __cdecl PureCallHandler()
253 GetInstance()._handler(
__LOCATION__ + SystemException(
"Pure virtual function call"), StackTrace(1));
256 EXCEPTION_POINTERS* pExceptionPtrs =
nullptr;
257 GetExceptionPointers(0, &pExceptionPtrs);
260 CreateDumpFile(pExceptionPtrs);
263 DeleteExceptionPointers(pExceptionPtrs);
266 TerminateProcess(GetCurrentProcess(), 1);
270 static void __cdecl InvalidParameterHandler(
const wchar_t* expression,
const wchar_t* function,
const wchar_t* file,
unsigned int line, uintptr_t pReserved)
273 GetInstance()._handler(
__LOCATION__ + SystemException(
"Invalid parameter exception"), StackTrace(1));
276 EXCEPTION_POINTERS* pExceptionPtrs =
nullptr;
277 GetExceptionPointers(0, &pExceptionPtrs);
280 CreateDumpFile(pExceptionPtrs);
283 DeleteExceptionPointers(pExceptionPtrs);
286 TerminateProcess(GetCurrentProcess(), 1);
290 static int __cdecl NewHandler(
size_t)
293 GetInstance()._handler(
__LOCATION__ + SystemException(
"'new' operator memory allocation exception"), StackTrace(1));
296 EXCEPTION_POINTERS* pExceptionPtrs =
nullptr;
297 GetExceptionPointers(0, &pExceptionPtrs);
300 CreateDumpFile(pExceptionPtrs);
303 DeleteExceptionPointers(pExceptionPtrs);
306 TerminateProcess(GetCurrentProcess(), 1);
313 static void SigabrtHandler(
int signum)
316 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught abort (SIGABRT) signal"), StackTrace(1));
319 EXCEPTION_POINTERS* pExceptionPtrs =
nullptr;
320 GetExceptionPointers(0, &pExceptionPtrs);
323 CreateDumpFile(pExceptionPtrs);
326 DeleteExceptionPointers(pExceptionPtrs);
329 TerminateProcess(GetCurrentProcess(), 1);
333 static void SigfpeHandler(
int signum,
int subcode)
336 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught floating point exception (SIGFPE) signal"), StackTrace(1));
339 EXCEPTION_POINTERS* pExceptionPtrs = (PEXCEPTION_POINTERS)_pxcptinfoptrs;
342 CreateDumpFile(pExceptionPtrs);
345 TerminateProcess(GetCurrentProcess(), 1);
349 static void SigillHandler(
int signum)
352 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught illegal instruction (SIGILL) signal"), StackTrace(1));
355 EXCEPTION_POINTERS* pExceptionPtrs =
nullptr;
356 GetExceptionPointers(0, &pExceptionPtrs);
359 CreateDumpFile(pExceptionPtrs);
362 DeleteExceptionPointers(pExceptionPtrs);
365 TerminateProcess(GetCurrentProcess(), 1);
369 static void SigintHandler(
int signum)
372 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught interruption (SIGINT) signal"), StackTrace(1));
375 EXCEPTION_POINTERS* pExceptionPtrs =
nullptr;
376 GetExceptionPointers(0, &pExceptionPtrs);
379 CreateDumpFile(pExceptionPtrs);
382 DeleteExceptionPointers(pExceptionPtrs);
385 TerminateProcess(GetCurrentProcess(), 1);
389 static void SigsegvHandler(
int signum)
392 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught invalid storage access (SIGSEGV) signal"), StackTrace(1));
395 EXCEPTION_POINTERS* pExceptionPtrs =
nullptr;
396 GetExceptionPointers(0, &pExceptionPtrs);
399 CreateDumpFile(pExceptionPtrs);
402 TerminateProcess(GetCurrentProcess(), 1);
406 static void SigtermHandler(
int signum)
409 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught termination request (SIGTERM) signal"), StackTrace(1));
412 EXCEPTION_POINTERS* pExceptionPtrs =
nullptr;
413 GetExceptionPointers(0, &pExceptionPtrs);
416 CreateDumpFile(pExceptionPtrs);
419 DeleteExceptionPointers(pExceptionPtrs);
422 TerminateProcess(GetCurrentProcess(), 1);
425 static void GetExceptionPointers(DWORD dwExceptionCode, EXCEPTION_POINTERS** ppExceptionPointers)
427 CONTEXT ContextRecord;
428 memset(&ContextRecord, 0,
sizeof(CONTEXT));
430 EXCEPTION_RECORD ExceptionRecord;
431 memset(&ExceptionRecord, 0,
sizeof(EXCEPTION_RECORD));
439 volatile ULONG dw[(
sizeof(CONTEXT) +
sizeof(EXCEPTION_RECORD)) /
sizeof(ULONG)];
446 mov dword ptr [ContextRecord.Eax ], eax
447 mov dword ptr [ContextRecord.Ecx ], ecx
448 mov dword ptr [ContextRecord.Edx ], edx
449 mov dword ptr [ContextRecord.Ebx ], ebx
450 mov dword ptr [ContextRecord.Esi ], esi
451 mov dword ptr [ContextRecord.Edi ], edi
452 mov word ptr [ContextRecord.SegSs], ss
453 mov word ptr [ContextRecord.SegCs], cs
454 mov word ptr [ContextRecord.SegDs], ds
455 mov word ptr [ContextRecord.SegEs], es
456 mov word ptr [ContextRecord.SegFs], fs
457 mov word ptr [ContextRecord.SegGs], gs
459 pop [ContextRecord.EFlags]
464 mov dword ptr [ContextRecord.Ebp], eax
466 mov dword ptr [ContextRecord.Eip], eax
468 mov dword ptr [ContextRecord.Esp], eax
471 mov eax, dword ptr dw
474 ContextRecord.ContextFlags = CONTEXT_CONTROL;
475 ExceptionRecord.ExceptionAddress = (PVOID)(ULONG_PTR)ContextRecord.Eip;
478 ULONG64 EstablisherFrame;
480 PRUNTIME_FUNCTION FunctionEntry;
483 RtlCaptureContext(&ContextRecord);
485 ControlPc = ContextRecord.Rip;
486 FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase,
nullptr);
488 if (FunctionEntry !=
nullptr)
489 RtlVirtualUnwind(UNW_FLAG_NHANDLER, ImageBase, ControlPc, FunctionEntry, &ContextRecord, &HandlerData, &EstablisherFrame,
nullptr);
492 void* return_address = __builtin_return_address(0);
493 ContextRecord.Rip = (ULONGLONG)return_address;
494 ContextRecord.Rsp = (ULONGLONG)__builtin_extract_return_addr(return_address) + 8;
495#elif defined(_MSC_VER)
496 ContextRecord.Rip = (ULONGLONG)_ReturnAddress();
497 ContextRecord.Rsp = (ULONGLONG)_AddressOfReturnAddress() + 8;
499 ExceptionRecord.ExceptionAddress = (PVOID)ContextRecord.Rip;
501 #error Unsupported architecture
503 ExceptionRecord.ExceptionCode = dwExceptionCode;
505 ExceptionRecord.ExceptionAddress = __builtin_return_address(0);
506#elif defined(_MSC_VER)
507 ExceptionRecord.ExceptionAddress = _ReturnAddress();
510 CONTEXT* pContextRecord =
new CONTEXT;
511 memcpy(pContextRecord, &ContextRecord,
sizeof(CONTEXT));
513 EXCEPTION_RECORD* pExceptionRecord =
new EXCEPTION_RECORD;
514 memcpy(pExceptionRecord, &ExceptionRecord,
sizeof(EXCEPTION_RECORD));
516 *ppExceptionPointers =
new EXCEPTION_POINTERS;
517 (*ppExceptionPointers)->ContextRecord = pContextRecord;
518 (*ppExceptionPointers)->ExceptionRecord = pExceptionRecord;
521 static void DeleteExceptionPointers(EXCEPTION_POINTERS* pExceptionPointers)
523 delete pExceptionPointers->ContextRecord;
524 delete pExceptionPointers->ExceptionRecord;
525 delete pExceptionPointers;
528 static void CreateDumpFile(EXCEPTION_POINTERS* pExcPtrs)
530#if defined(DBGHELP_SUPPORT)
532 Path dump = Path::executable().parent() /
format(
"crash.{}.dmp", Timestamp::utc());
535 HANDLE hDumpFile = CreateFileW(dump.wstring().c_str(), GENERIC_WRITE, 0,
nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
nullptr);
536 if (hDumpFile == INVALID_HANDLE_VALUE)
537 throwex FileSystemException(
"Cannot create a dump file!").Attach(dump);
540 auto file =
resource(hDumpFile, [](HANDLE hObject) { CloseHandle(hObject); });
542 MINIDUMP_EXCEPTION_INFORMATION mei;
543 MINIDUMP_CALLBACK_INFORMATION mci;
546 mei.ThreadId = GetCurrentThreadId();
547 mei.ExceptionPointers = pExcPtrs;
548 mei.ClientPointers = FALSE;
549 mci.CallbackRoutine =
nullptr;
550 mci.CallbackParam =
nullptr;
553 if (!MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file.get(), MiniDumpNormal, &mei,
nullptr, &mci))
554 throwex FileSystemException(
"Cannot write a dump file!").Attach(dump);
557 if (!CloseHandle(hDumpFile))
558 throwex FileSystemException(
"Cannot close a dump file!").Attach(dump);
563#elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
566 static void SignalHandler(
int signo, siginfo_t* info,
void* context)
572 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught abnormal program termination (SIGABRT) signal"), StackTrace(1));
575 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught alarm clock (SIGALRM) signal"), StackTrace(1));
578 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught memory access error (SIGBUS) signal"), StackTrace(1));
581 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught floating point exception (SIGFPE) signal"), StackTrace(1));
584 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught hangup instruction (SIGHUP) signal"), StackTrace(1));
587 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught illegal instruction (SIGILL) signal"), StackTrace(1));
590 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught terminal interrupt (SIGINT) signal"), StackTrace(1));
593 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught pipe write error (SIGPIPE) signal"), StackTrace(1));
596 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught profiling timer expired error (SIGPROF) signal"), StackTrace(1));
599 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught terminal quit (SIGQUIT) signal"), StackTrace(1));
602 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught illegal storage access error (SIGSEGV) signal"), StackTrace(1));
605 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught bad system call (SIGSYS) signal"), StackTrace(1));
608 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught termination request (SIGTERM) signal"), StackTrace(1));
611 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught CPU time limit exceeded (SIGXCPU) signal"), StackTrace(1));
614 GetInstance()._handler(
__LOCATION__ + SystemException(
"Caught file size limit exceeded (SIGXFSZ) signal"), StackTrace(1));
617 GetInstance()._handler(
__LOCATION__ + SystemException(
format(
"Caught unknown signal - {}", signo)), StackTrace(1));
623 memset(&sa, 0,
sizeof(sa));
624 sa.sa_handler = SIG_DFL;
627 int result = sigaction(signo, &sa,
nullptr);
631 kill(getpid(), SIGKILL);
639ExceptionsHandler::ExceptionsHandler()
642 [[maybe_unused]] ValidateAlignedStorage<
sizeof(Impl),
alignof(Impl), StorageSize, StorageAlign> _;
643 static_assert((StorageSize >=
sizeof(Impl)),
"ExceptionsHandler::StorageSize must be increased!");
644 static_assert(((StorageAlign %
alignof(Impl)) == 0),
"ExceptionsHandler::StorageAlign must be adjusted!");
647 new(&_storage)Impl();
653 reinterpret_cast<Impl*
>(&_storage)->~Impl();
static void SetupProcess()
Setup exceptions handler for the current process.
static void SetupHandler(const std::function< void(const SystemException &, const StackTrace &)> &handler)
Setup new global exceptions handler function.
static void SetupThread()
Setup exceptions handler for the current thread.
static ExceptionsHandler & GetInstance()
Get singleton instance.
Stack trace snapshot provider.
Static array countof definition.
#define throwex
Throw extended exception macro.
Exceptions handler definition.
C++ Common project definitions.
std::string format(fmt::format_string< T... > pattern, T &&... args)
Format string.
auto resource(T handle, TCleaner cleaner)
Resource smart cleaner pattern.
constexpr size_t countof(const T(&)[N]) noexcept
Count of elements in static array.
Filesystem path definition.
Resource smart cleaner pattern definition.
#define __LOCATION__
Current source location macro.
Stack trace snapshot provider definition.
Aligned storage validator definition.