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)
50 class ExceptionsHandler::Impl
53 Impl() : _initialized(false), _handler(ExceptionsHandler::Impl::DefaultHandler) {}
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]));
144 #if defined(_WIN32) || defined(_WIN64)
145 #if defined(_MSC_VER)
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)
198 CreateDumpFile(pExceptionPtrs);
201 TerminateProcess(GetCurrentProcess(), 1);
204 return EXCEPTION_EXECUTE_HANDLER;
207 #if defined(_MSC_VER)
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()
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)
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)
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)
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;
476 #elif defined(_M_X64)
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);
491 #if defined(__GNUC__)
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;
504 #
if defined(__GNUC__)
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)
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));
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));
623 memset(&sa, 0,
sizeof(sa));
624 sa.sa_handler = SIG_DFL;
627 int result = sigaction(signo, &sa,
nullptr);
631 kill(getpid(), SIGKILL);
639 ExceptionsHandler::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.
Path parent() const
Decompose parent path from the current path.
static Path executable()
Get the executable path of the process.
static ExceptionsHandler & GetInstance()
Get singleton instance.
Stack trace snapshot provider.
static uint64_t utc()
Get the UTC timestamp.
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.