#ifdef _WIN32 #include "windowscrashhandler.h" #include "../defines.h" #include #include #include "StackWalker.h" // minidump #include #include //#include #include //#include #include #include #include #include #include #include #include "../common/common.h" #ifdef _MSC_VER #if _MSC_VER < 1400 #define strcpy_s(dst, len, src) strcpy(dst, src) #define strncpy_s(dst, len, src, maxLen) strncpy(dst, len, src) #define strcat_s(dst, len, src) strcat(dst, src) #define _snprintf_s _snprintf #define _tcscat_s _tcscat #endif #endif #ifdef __MINGW32__ #define strcpy_s(dst, len, src) strcpy(dst, src) #define strncpy_s(dst, len, src, maxLen) strncpy(dst, src, len) #define strcat_s(dst, len, src) strcat(dst, src) #define _snprintf_s _snprintf #endif #define CR_SEH_EXCEPTION 0 //!< SEH exception. #define CR_CPP_TERMINATE_CALL 1 //!< C++ terminate() call. #define CR_CPP_UNEXPECTED_CALL 2 //!< C++ unexpected() call. #define CR_CPP_PURE_CALL 3 //!< C++ pure virtual function call (VS .NET and later). #define CR_CPP_NEW_OPERATOR_ERROR 4 //!< C++ new operator fault (VS .NET and later). #define CR_CPP_SECURITY_ERROR 5 //!< Buffer overrun error (VS .NET only). #define CR_CPP_INVALID_PARAMETER 6 //!< Invalid parameter exception (VS 2005 and later). #define CR_CPP_SIGABRT 7 //!< C++ SIGABRT signal (abort). #define CR_CPP_SIGFPE 8 //!< C++ SIGFPE signal (flotating point exception). #define CR_CPP_SIGILL 9 //!< C++ SIGILL signal (illegal instruction). #define CR_CPP_SIGINT 10 //!< C++ SIGINT signal (CTRL+C). #define CR_CPP_SIGSEGV 11 //!< C++ SIGSEGV signal (invalid storage access). #define CR_CPP_SIGTERM 12 //!< C++ SIGTERM signal (termination request). #define STRINGIZE_(x) #x #define STRINGIZE(x) STRINGIZE_(x) #ifndef _AddressOfReturnAddress // Taken from: http://msdn.microsoft.com/en-us/library/s975zw7k(VS.71).aspx #ifdef __cplusplus #define EXTERNC extern "C" #else #define EXTERNC #endif // _ReturnAddress and _AddressOfReturnAddress should be prototyped before use EXTERNC void * _AddressOfReturnAddress(void); EXTERNC void * _ReturnAddress(void); #endif #pragma GCC diagnostic ignored "-Wcast-function-type" namespace Debug { class StackWalkerWithCallback : public StackWalker { public: StackWalkerWithCallback(const std::function &callback): StackWalker(RetrieveVerbose | SymBuildPath), m_callback(callback) { } protected: virtual void OnOutput(LPCSTR szText) override { m_callback(szText); } private: std::function m_callback; }; BOOL PreventSetUnhandledExceptionFilter() { HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll")); if (hKernel32 == NULL) return FALSE; void* pOrgEntry = reinterpret_cast(GetProcAddress(hKernel32, "SetUnhandledExceptionFilter")); if (pOrgEntry == NULL) return FALSE; #ifdef _M_IX86 // Code for x86: // 33 C0 xor eax,eax // C2 04 00 ret 4 unsigned char szExecute[] = { 0x33, 0xC0, 0xC2, 0x04, 0x00 }; #elif _M_X64 // 33 C0 xor eax,eax // C3 ret unsigned char szExecute[] = { 0x33, 0xC0, 0xC3 }; #else #error "The following code only works for x86 and x64!" #endif SIZE_T bytesWritten = 0; BOOL bRet = WriteProcessMemory(GetCurrentProcess(), pOrgEntry, szExecute, sizeof(szExecute), &bytesWritten); return bRet; } BOOL CALLBACK MyMiniDumpCallback( PVOID pParam, const PMINIDUMP_CALLBACK_INPUT pInput, PMINIDUMP_CALLBACK_OUTPUT pOutput ) { BOOL bRet = FALSE; // Check parameters if( pInput == 0 ) return FALSE; if( pOutput == 0 ) return FALSE; // Process the callbacks WindowsCrashHandler *handler = (WindowsCrashHandler*)pParam; switch( pInput->CallbackType ) { case IncludeModuleCallback: { // Include the module into the dump bRet = TRUE; } break; case IncludeThreadCallback: { // Include the thread into the dump bRet = TRUE; } break; case ModuleCallback: { // Are data sections available for this module ? if( pOutput->ModuleWriteFlags & ModuleWriteDataSeg ) { // Yes, they are, but do we need them? if( !handler->isDataSectionNeeded( pInput->Module.FullPath ) ) { pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg); } } if( !(pOutput->ModuleWriteFlags & ModuleReferencedByMemory) ) { // No, it does not - exclude it pOutput->ModuleWriteFlags &= (~ModuleWriteModule); } bRet = TRUE; } break; case ThreadCallback: { // Include all thread information into the minidump bRet = TRUE; } break; case ThreadExCallback: { // Include this information bRet = TRUE; } break; case MemoryCallback: { // We do not include any information here -> return FALSE bRet = FALSE; } break; case CancelCallback: break; } return bRet; } void DoHandleCrash() { WindowsCrashHandler &handler = WindowsCrashHandler::getInstance(); handler.handleCrash(); } // http://groups.google.com/group/crashrpt/browse_thread/thread/a1dbcc56acb58b27/fbd0151dd8e26daf?lnk=gst&q=stack+overflow#fbd0151dd8e26daf // Thread procedure doing the dump for stack overflow. DWORD WINAPI StackOverflowThreadFunction(LPVOID) { DoHandleCrash(); TerminateProcess(GetCurrentProcess(), CHILLOUT_EXIT_CODE); return 0; } static LONG WINAPI SehHandler(EXCEPTION_POINTERS*) { #ifdef _DEBUG fprintf(stderr, "Chillout SehHandler"); #endif DoHandleCrash(); TerminateProcess(GetCurrentProcess(), CHILLOUT_EXIT_CODE); // unreachable return EXCEPTION_EXECUTE_HANDLER; } // The following code is intended to fix the issue with 32-bit applications in 64-bit environment. // http://support.microsoft.com/kb/976038/en-us // http://code.google.com/p/crashrpt/issues/detail?id=104 void EnableCrashingOnCrashes() { typedef BOOL (WINAPI *tGetPolicy)(LPDWORD lpFlags); typedef BOOL (WINAPI *tSetPolicy)(DWORD dwFlags); static const DWORD EXCEPTION_SWALLOWING = 0x1; const HMODULE kernel32 = LoadLibraryA("kernel32.dll"); const tGetPolicy pGetPolicy = reinterpret_cast(GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy")); const tSetPolicy pSetPolicy = reinterpret_cast(GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy")); if(pGetPolicy && pSetPolicy) { DWORD dwFlags; if(pGetPolicy(&dwFlags)) { // Turn off the filter pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING); } } } WindowsCrashHandler::WindowsCrashHandler() { m_oldSehHandler = NULL; #if _MSC_VER>=1300 m_prevPurec = NULL; m_prevNewHandler = NULL; #endif #if _MSC_VER>=1300 && _MSC_VER<1400 m_prevSec = NULL; #endif #if _MSC_VER>=1400 m_prevInvpar = NULL; #endif m_prevSigABRT = NULL; m_prevSigINT = NULL; m_prevSigTERM = NULL; } void WindowsCrashHandler::setup(const std::wstring &appName, const std::wstring &dumpsDir) { m_appName = appName; if (!appName.empty() && !dumpsDir.empty()) { std::wstring path = dumpsDir; while ((path.size() > 1) && (path[path.size() - 1] == L'\\')) { path.erase(path.size() - 1); } } EnableCrashingOnCrashes(); setProcessExceptionHandlers(); setThreadExceptionHandlers(); } void WindowsCrashHandler::teardown() { unsetProcessExceptionHandlers(); unsetThreadExceptionHandlers(); } void WindowsCrashHandler::setCrashCallback(const std::function &callback) { m_crashCallback = callback; } void WindowsCrashHandler::setBacktraceCallback(const std::function &callback) { m_backtraceCallback = callback; } void WindowsCrashHandler::handleCrash() { std::lock_guard guard(m_crashMutex); (void)guard; if (m_crashCallback) { m_crashCallback(); } // Terminate process TerminateProcess(GetCurrentProcess(), CHILLOUT_EXIT_CODE); } bool WindowsCrashHandler::isDataSectionNeeded(const WCHAR* pModuleName) { if( pModuleName == 0 ) { _ASSERTE( _T("Parameter is null.") ); return false; } // Extract the module name WCHAR szFileName[_MAX_FNAME] = L""; _wsplitpath( pModuleName, NULL, NULL, szFileName, NULL ); // Compare the name with the list of known names and decide // if contains app name in its path if( wcsstr( pModuleName, m_appName.c_str() ) != 0 ) { return true; } else if( _wcsicmp( szFileName, L"ntdll" ) == 0 ) { return true; } else if( wcsstr( szFileName, L"Qt5" ) != 0 ) { return true; } // Complete return false; } void WindowsCrashHandler::setProcessExceptionHandlers() { //SetErrorMode(SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS); m_oldSehHandler = SetUnhandledExceptionFilter(SehHandler); #if defined _M_X64 || defined _M_IX86 if (m_oldSehHandler) PreventSetUnhandledExceptionFilter(); #endif #if _MSC_VER>=1300 // Catch pure virtual function calls. // Because there is one _purecall_handler for the whole process, // calling this function immediately impacts all threads. The last // caller on any thread sets the handler. // http://msdn.microsoft.com/en-us/library/t296ys27.aspx m_prevPurec = _set_purecall_handler(PureCallHandler); // Catch new operator memory allocation exceptions //_set_new_mode(1); // Force malloc() to call new handler too m_prevNewHandler = _set_new_handler(NewHandler); #endif #if _MSC_VER>=1400 // Catch invalid parameter exceptions. m_prevInvpar = _set_invalid_parameter_handler(InvalidParameterHandler); #endif #if _MSC_VER>=1300 && _MSC_VER<1400 // Catch buffer overrun exceptions // The _set_security_error_handler is deprecated in VC8 C++ run time library m_prevSec = _set_security_error_handler(SecurityHandler); #endif #if _MSC_VER>=1400 _set_abort_behavior(0, _WRITE_ABORT_MSG); _set_abort_behavior(_CALL_REPORTFAULT, _CALL_REPORTFAULT); #endif #if defined(_MSC_VER) // Disable all of the possible ways Windows conspires to make automated // testing impossible. // ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); // ::_set_error_mode(_OUT_TO_STDERR); // _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); // _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); // _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); // _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); // _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); // _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); m_crtReportHook = _CrtSetReportHook(CrtReportHook); #endif // Set up C++ signal handlers SetConsoleCtrlHandler(ConsoleCtrlHandler, true); // Catch an abnormal program termination m_prevSigABRT = signal(SIGABRT, SigabrtHandler); // Catch illegal instruction handler m_prevSigINT = signal(SIGINT, SigintHandler); // Catch a termination request m_prevSigINT = signal(SIGTERM, SigtermHandler); } void WindowsCrashHandler::unsetProcessExceptionHandlers() { #if _MSC_VER>=1300 if(m_prevPurec!=NULL) { _set_purecall_handler(m_prevPurec); } if(m_prevNewHandler!=NULL) { _set_new_handler(m_prevNewHandler); } #endif #if _MSC_VER>=1400 if(m_prevInvpar!=NULL) { _set_invalid_parameter_handler(m_prevInvpar); } #endif //_MSC_VER>=1400 #if _MSC_VER>=1300 && _MSC_VER<1400 if(m_prevSec!=NULL) _set_security_error_handler(m_prevSec); #endif //_MSC_VER<1400 if(m_prevSigABRT!=NULL) { signal(SIGABRT, m_prevSigABRT); } if(m_prevSigINT!=NULL) { signal(SIGINT, m_prevSigINT); } if(m_prevSigTERM!=NULL) { signal(SIGTERM, m_prevSigTERM); } // Reset SEH exception filter if (m_oldSehHandler) { SetUnhandledExceptionFilter(m_oldSehHandler); } m_oldSehHandler = NULL; } int WindowsCrashHandler::setThreadExceptionHandlers() { DWORD dwThreadId = GetCurrentThreadId(); std::lock_guard guard(m_threadHandlersMutex); auto it = m_threadExceptionHandlers.find(dwThreadId); if (it != m_threadExceptionHandlers.end()) { // handlers are already set for the thread return 1; // failed } ThreadExceptionHandlers handlers; // Catch terminate() calls. // In a multithreaded environment, terminate functions are maintained // separately for each thread. Each new thread needs to install its own // terminate function. Thus, each thread is in charge of its own termination handling. // http://msdn.microsoft.com/en-us/library/t6fk7h29.aspx handlers.m_prevTerm = std::set_terminate(TerminateHandler); // Catch unexpected() calls. // In a multithreaded environment, unexpected functions are maintained // separately for each thread. Each new thread needs to install its own // unexpected function. Thus, each thread is in charge of its own unexpected handling. // http://msdn.microsoft.com/en-us/library/h46t5b69.aspx handlers.m_prevUnexp = std::set_unexpected(UnexpectedHandler); // Catch a floating point error typedef void (*sigh)(int); handlers.m_prevSigFPE = signal(SIGFPE, (sigh)SigfpeHandler); // Catch an illegal instruction handlers.m_prevSigILL = signal(SIGILL, SigillHandler); // Catch illegal storage access errors handlers.m_prevSigSEGV = signal(SIGSEGV, SigsegvHandler); m_threadExceptionHandlers[dwThreadId] = handlers; return 0; } int WindowsCrashHandler::unsetThreadExceptionHandlers() { DWORD dwThreadId = GetCurrentThreadId(); std::lock_guard guard(m_threadHandlersMutex); (void)guard; auto it = m_threadExceptionHandlers.find(dwThreadId); if (it == m_threadExceptionHandlers.end()) { return 1; } ThreadExceptionHandlers* handlers = &(it->second); if(handlers->m_prevTerm!=NULL) { std::set_terminate(handlers->m_prevTerm); } if(handlers->m_prevUnexp!=NULL) { std::set_unexpected(handlers->m_prevUnexp); } if(handlers->m_prevSigFPE!=NULL) { signal(SIGFPE, handlers->m_prevSigFPE); } if(handlers->m_prevSigILL!=NULL) { signal(SIGINT, handlers->m_prevSigILL); } if(handlers->m_prevSigSEGV!=NULL) { signal(SIGSEGV, handlers->m_prevSigSEGV); } // Remove from the list m_threadExceptionHandlers.erase(it); return 0; } int __cdecl WindowsCrashHandler::CrtReportHook(int nReportType, char* szMsg, int* pnRet) { (void)szMsg; switch (nReportType) { case _CRT_WARN: case _CRT_ERROR: case _CRT_ASSERT: // Put some debug code here break; } if (pnRet) { *pnRet = 0; } return TRUE; } // CRT terminate() call handler void __cdecl WindowsCrashHandler::TerminateHandler() { // Abnormal program termination (terminate() function was called) DoHandleCrash(); } // CRT unexpected() call handler void __cdecl WindowsCrashHandler::UnexpectedHandler() { // Unexpected error (unexpected() function was called) DoHandleCrash(); } // CRT Pure virtual method call handler void __cdecl WindowsCrashHandler::PureCallHandler() { // Pure virtual function call DoHandleCrash(); } // CRT buffer overrun handler. Available in CRT 7.1 only #if _MSC_VER>=1300 && _MSC_VER<1400 void __cdecl WindowsCrashHandler::SecurityHandler(int code, void *x) { // Security error (buffer overrun). code; x; EXCEPTION_POINTERS* pExceptionPtrs = (PEXCEPTION_POINTERS)_pxcptinfoptrs; if (pExceptionPtrs == nullptr) { GetExceptionPointers(CR_CPP_SECURITY_ERROR, &pExceptionPtrs); } DoHandleCrash(pExceptionPtrs); } #endif #if _MSC_VER>=1400 // CRT invalid parameter handler void __cdecl WindowsCrashHandler::InvalidParameterHandler( const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved) { pReserved; expression; function; file; line; // fwprintf(stderr, L"Invalid parameter detected in function %s." // L" File: %s Line: %d\n", function, file, line); // Retrieve exception information EXCEPTION_POINTERS* pExceptionPtrs = NULL; GetExceptionPointers(CR_CPP_INVALID_PARAMETER, &pExceptionPtrs); DoHandleCrash(pExceptionPtrs); } #endif // CRT new operator fault handler int __cdecl WindowsCrashHandler::NewHandler(size_t) { // 'new' operator memory allocation exception DoHandleCrash(); // Unreacheable code return 0; } // CRT SIGABRT signal handler void WindowsCrashHandler::SigabrtHandler(int) { // Caught SIGABRT C++ signal DoHandleCrash(); } // CRT SIGFPE signal handler void WindowsCrashHandler::SigfpeHandler(int /*code*/, int subcode) { // Floating point exception (SIGFPE) (void)subcode; DoHandleCrash(); } // CRT sigill signal handler void WindowsCrashHandler::SigillHandler(int) { // Illegal instruction (SIGILL) DoHandleCrash(); } // CRT sigint signal handler void WindowsCrashHandler::SigintHandler(int) { // Interruption (SIGINT) DoHandleCrash(); } // CRT SIGSEGV signal handler void WindowsCrashHandler::SigsegvHandler(int) { // Invalid storage access (SIGSEGV) DoHandleCrash(); } // CRT SIGTERM signal handler void WindowsCrashHandler::SigtermHandler(int) { // Termination request (SIGTERM) DoHandleCrash(); } // CRT SIGTERM signal handler BOOL WINAPI WindowsCrashHandler::ConsoleCtrlHandler(DWORD type) { // Termination request (CTRL_C_EVENT) switch (type) { case CTRL_C_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: case CTRL_CLOSE_EVENT: DoHandleCrash(); break; } return false; } } #endif