From 7fb9d6d573a724049e9c46d9050a85bba72f437f Mon Sep 17 00:00:00 2001 From: kgv Date: Mon, 3 Jun 2019 08:06:24 +0300 Subject: [PATCH] 0030762: Foundation Classes - include backtrace within OSD_SIGSEGV Standard_Failure now holds an optional stack trace dump. Added function Standard::StackTrace() dumping backtrace to the string. SegvHandler within OSD_signal now appends backtrace to the message if OSD::SignalStackTraceLength() is set to non-zero value or environment variable "CSF_DEBUG_MODE" is set for debugging. Added auxiliary macros Standard_NOINLINE disallowing function inlining. Command "dsetsignal" has been extended by -strackTraceLength argument for defining stack trace length within signals redirected to C++ exceptions. Added "ddebugtraces" command for debugging purposes (adding stack traces to all exceptions). --- src/Draw/Draw.cxx | 1 + src/Draw/Draw_BasicCommands.cxx | 55 ++- src/OSD/OSD.hxx | 8 + src/OSD/OSD_ThreadPool.cxx | 8 +- src/OSD/OSD_signal.cxx | 423 +++++++++++++--------- src/QABugs/QABugs_11.cxx | 83 +++++ src/Standard/FILES | 1 + src/Standard/Standard.hxx | 22 ++ src/Standard/Standard_DefineException.hxx | 7 +- src/Standard/Standard_Failure.cxx | 366 ++++++++++++++----- src/Standard/Standard_Failure.hxx | 111 ++++-- src/Standard/Standard_Macro.hxx | 11 + src/Standard/Standard_OutOfMemory.cxx | 10 +- src/Standard/Standard_OutOfMemory.hxx | 6 +- src/Standard/Standard_StackTrace.cxx | 362 ++++++++++++++++++ tests/bugs/fclasses/bug30762 | 22 ++ 16 files changed, 1167 insertions(+), 329 deletions(-) create mode 100644 src/Standard/Standard_StackTrace.cxx create mode 100644 tests/bugs/fclasses/bug30762 diff --git a/src/Draw/Draw.cxx b/src/Draw/Draw.cxx index 3a77a71c6a..3b1a165072 100644 --- a/src/Draw/Draw.cxx +++ b/src/Draw/Draw.cxx @@ -377,6 +377,7 @@ void Draw_Appli(int argc, char** argv, const FDraw_InitAppli Draw_InitAppli) // set signals // ***************************************************************** OSD::SetSignal(Standard_False); + //OSD::SetSignalStackTraceLength (10); #ifdef _WIN32 // in interactive mode, force Windows to report dll loading problems interactively diff --git a/src/Draw/Draw_BasicCommands.cxx b/src/Draw/Draw_BasicCommands.cxx index 4a0bb7b49b..7d6b37e66a 100644 --- a/src/Draw/Draw_BasicCommands.cxx +++ b/src/Draw/Draw_BasicCommands.cxx @@ -954,6 +954,7 @@ static int dsetsignal (Draw_Interpretor& theDI, Standard_Integer theArgNb, const { OSD_SignalMode aMode = OSD_SignalMode_Set; Standard_Boolean aSetFPE = OSD::ToCatchFloatingSignals(); + Standard_Integer aStackLen = OSD::SignalStackTraceLength(); // default for FPE signal is defined by CSF_FPE variable, if set OSD_Environment aEnv("CSF_FPE"); @@ -995,6 +996,14 @@ static int dsetsignal (Draw_Interpretor& theDI, Standard_Integer theArgNb, const else if (anArg == "default") { } + else if (anArgIter + 1 < theArgNb + && (anArg == "-stracktracelength" + || anArg == "-stracktracelen" + || anArg == "-stracklength" + || anArg == "-stracklen")) + { + aStackLen = Draw::Atoi (theArgVec[++anArgIter]); + } else { Message::SendFail() << "Syntax error: unknown argument '" << anArg << "'"; @@ -1003,19 +1012,21 @@ static int dsetsignal (Draw_Interpretor& theDI, Standard_Integer theArgNb, const } OSD::SetSignal(aMode, aSetFPE); + OSD::SetSignalStackTraceLength (aStackLen); // report actual status in the end const char* aModeStr = 0; switch (OSD::SignalMode()) { - default: - case OSD_SignalMode_AsIs: aModeStr = "asis"; break; - case OSD_SignalMode_Set: aModeStr = "set"; break; - case OSD_SignalMode_SetUnhandled: aModeStr = "unhandled"; break; - case OSD_SignalMode_Unset: aModeStr = "unset"; break; + default: + case OSD_SignalMode_AsIs: aModeStr = "asis"; break; + case OSD_SignalMode_Set: aModeStr = "set"; break; + case OSD_SignalMode_SetUnhandled: aModeStr = "unhandled"; break; + case OSD_SignalMode_Unset: aModeStr = "unset"; break; } theDI << "Signal mode: " << aModeStr << "\n" - << "Catch FPE: " << (OSD::ToCatchFloatingSignals() ? "1" : "0") << "\n"; + << "Catch FPE: " << (OSD::ToCatchFloatingSignals() ? "1" : "0") << "\n" + << "Stack Trace Length: " << aStackLen << "\n"; return 0; } @@ -1112,6 +1123,27 @@ static int dtracelevel (Draw_Interpretor& theDI, return 0; } +//============================================================================== +//function : ddebugtraces +//purpose : +//============================================================================== +static int ddebugtraces (Draw_Interpretor& theDI, Standard_Integer theArgNb, const char** theArgVec) +{ + if (theArgNb < 2) + { + theDI << Standard_Failure::DefaultStackTraceLength(); + return 0; + } + else if (theArgNb != 2) + { + theDI << "Syntax error: wrong number of arguments"; + return 1; + } + + Standard_Failure::SetDefaultStackTraceLength (Draw::Atoi (theArgVec[1])); + return 0; +} + //============================================================================== //function : dputs //purpose : @@ -1243,7 +1275,11 @@ void Draw::BasicCommands(Draw_Interpretor& theCommands) __FILE__, dmeminfo, g); theCommands.Add("dperf","dperf [reset] -- show performance counters, reset if argument is provided", __FILE__,dperf,g); - theCommands.Add("dsetsignal","dsetsignal [{asis|set|unhandled|unset}=set] [{0|1|default=$CSF_FPE}]\n -- set OSD signal handler, with FPE option if argument is given", + theCommands.Add("dsetsignal", + "dsetsignal [{asIs|set|unhandled|unset}=set] [{0|1|default=$CSF_FPE}]" + "\n\t\t: [-strackTraceLength Length]" + "\n\t\t: Sets OSD signal handler, with FPE option if argument is given." + "\n\t\t: -strackTraceLength specifies length of stack trace to put into exceptions redirected from signals.", __FILE__,dsetsignal,g); theCommands.Add("dparallel", @@ -1264,6 +1300,11 @@ void Draw::BasicCommands(Draw_Interpretor& theCommands) __FILE__,decho,g); theCommands.Add("dtracelevel", "dtracelevel [trace|info|warn|alarm|fail]", __FILE__, dtracelevel, g); + theCommands.Add("ddebugtraces", + "ddebugtraces nbTraces" + "\n\t\t: Sets the number of lines for the stack trace within Standard_Failure constructor." + "\n\t\t: Intended for debug purposes.", + __FILE__, ddebugtraces, g); theCommands.Add("dbreak", "raises Tcl exception if user has pressed Control-Break key", __FILE__,dbreak,g); diff --git a/src/OSD/OSD.hxx b/src/OSD/OSD.hxx index 78cfb38d5a..7ce019500c 100644 --- a/src/OSD/OSD.hxx +++ b/src/OSD/OSD.hxx @@ -134,6 +134,14 @@ public: //! not. If yes then raises Exception_CTRL_BREAK. Standard_EXPORT static void ControlBreak(); + //! Returns a length of stack trace to be put into exception redirected from signal; + //! 0 by default meaning no stack trace. + //! @sa Standard_Failure::GetStackString() + Standard_EXPORT static Standard_Integer SignalStackTraceLength(); + + //! Sets a length of stack trace to be put into exception redirected from signal. + Standard_EXPORT static void SetSignalStackTraceLength (Standard_Integer theLength); + }; #endif // _OSD_HeaderFile diff --git a/src/OSD/OSD_ThreadPool.cxx b/src/OSD/OSD_ThreadPool.cxx index 0d3bc2bb0e..04a65bba56 100644 --- a/src/OSD/OSD_ThreadPool.cxx +++ b/src/OSD/OSD_ThreadPool.cxx @@ -269,7 +269,7 @@ void OSD_ThreadPool::Launcher::wait() } aFailures = TCollection_AsciiString("Multiple exceptions:\n") + aFailures; - throw Standard_ProgramError (aFailures.ToCString()); + throw Standard_ProgramError (aFailures.ToCString(), NULL); } // ======================================================================= @@ -289,17 +289,17 @@ void OSD_ThreadPool::performJob (Handle(Standard_Failure)& theFailure, { TCollection_AsciiString aMsg = TCollection_AsciiString (aFailure.DynamicType()->Name()) + ": " + aFailure.GetMessageString(); - theFailure = new Standard_ProgramError (aMsg.ToCString()); + theFailure = new Standard_ProgramError (aMsg.ToCString(), aFailure.GetStackString()); } catch (std::exception& anStdException) { TCollection_AsciiString aMsg = TCollection_AsciiString (typeid(anStdException).name()) + ": " + anStdException.what(); - theFailure = new Standard_ProgramError (aMsg.ToCString()); + theFailure = new Standard_ProgramError (aMsg.ToCString(), NULL); } catch (...) { - theFailure = new Standard_ProgramError ("Error: Unknown exception"); + theFailure = new Standard_ProgramError ("Error: Unknown exception", NULL); } } diff --git a/src/OSD/OSD_signal.cxx b/src/OSD/OSD_signal.cxx index 9b5225fb1f..5c4d7b2629 100644 --- a/src/OSD/OSD_signal.cxx +++ b/src/OSD/OSD_signal.cxx @@ -21,6 +21,7 @@ #include static OSD_SignalMode OSD_WasSetSignal = OSD_SignalMode_AsIs; +static Standard_Integer OSD_SignalStackTraceLength = 0; //======================================================================= //function : SignalMode @@ -31,6 +32,24 @@ OSD_SignalMode OSD::SignalMode() return OSD_WasSetSignal; } +// ======================================================================= +// function : SignalStackTraceLength +// purpose : +// ======================================================================= +Standard_Integer OSD::SignalStackTraceLength() +{ + return OSD_SignalStackTraceLength; +} + +// ======================================================================= +// function : SetSignalStackTraceLength +// purpose : +// ======================================================================= +void OSD::SetSignalStackTraceLength (Standard_Integer theLength) +{ + OSD_SignalStackTraceLength = theLength; +} + #ifdef _WIN32 //---------------------------- Windows NT System -------------------------------- @@ -85,7 +104,7 @@ static Standard_Boolean fMsgBox; // used to forbid simultaneous execution of setting / executing handlers static Standard_Mutex THE_SIGNAL_MUTEX; -static LONG __fastcall _osd_raise ( DWORD, LPSTR ); +static LONG __fastcall _osd_raise (DWORD theCode, const char* theMsg, const char* theStack); static BOOL WINAPI _osd_ctrl_break_handler ( DWORD ); #if ! defined(OCCT_UWP) && !defined(__MINGW32__) && !defined(__CYGWIN32__) @@ -96,172 +115,208 @@ static LONG _osd_debug ( void ); # define _OSD_FPX ( _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW ) #ifdef OCC_CONVERT_SIGNALS -#define THROW_OR_JUMP(Type,Message) Type::NewInstance(Message)->Jump() +#define THROW_OR_JUMP(Type,Message,Stack) Type::NewInstance(Message,Stack)->Jump() #else -#define THROW_OR_JUMP(Type,Message) throw Type(Message) +#define THROW_OR_JUMP(Type,Message,Stack) throw Type(Message,Stack) #endif //======================================================================= //function : CallHandler //purpose : //======================================================================= -static LONG CallHandler (DWORD dwExceptionCode, - ptrdiff_t ExceptionInformation1, - ptrdiff_t ExceptionInformation0) +static LONG CallHandler (DWORD theExceptionCode, + EXCEPTION_POINTERS* theExcPtr) { + ptrdiff_t ExceptionInformation1 = 0, ExceptionInformation0 = 0; + if (theExcPtr != NULL) + { + ExceptionInformation1 = theExcPtr->ExceptionRecord->ExceptionInformation[1]; + ExceptionInformation0 = theExcPtr->ExceptionRecord->ExceptionInformation[0]; + } + Standard_Mutex::Sentry aSentry (THE_SIGNAL_MUTEX); // lock the mutex to prevent simultaneous handling + static char aBuffer[2048]; - static wchar_t buffer[2048]; - - int flterr = 0; - - buffer[0] = '\0' ; - -// std::cout << "CallHandler " << dwExceptionCode << std::endl ; - switch ( dwExceptionCode ) { + bool isFloatErr = false; + aBuffer[0] = '\0'; + switch (theExceptionCode) + { case EXCEPTION_FLT_DENORMAL_OPERAND: -// std::cout << "CallHandler : EXCEPTION_FLT_DENORMAL_OPERAND:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"FLT DENORMAL OPERAND"); - flterr = 1 ; - break ; + { + strcat_s (aBuffer, sizeof(aBuffer), "FLT DENORMAL OPERAND"); + isFloatErr = true; + break; + } case EXCEPTION_FLT_DIVIDE_BY_ZERO: -// std::cout << "CallHandler : EXCEPTION_FLT_DIVIDE_BY_ZERO:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"FLT DIVIDE BY ZERO"); - flterr = 1 ; - break ; + { + strcat_s (aBuffer, sizeof(aBuffer), "FLT DIVIDE BY ZERO"); + isFloatErr = true; + break; + } case EXCEPTION_FLT_INEXACT_RESULT: -// std::cout << "CallHandler : EXCEPTION_FLT_INEXACT_RESULT:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"FLT INEXACT RESULT"); - flterr = 1 ; - break ; + { + strcat_s (aBuffer, sizeof(aBuffer), "FLT INEXACT RESULT"); + isFloatErr = true; + break; + } case EXCEPTION_FLT_INVALID_OPERATION: -// std::cout << "CallHandler : EXCEPTION_FLT_INVALID_OPERATION:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"FLT INVALID OPERATION"); - flterr = 1 ; - break ; + { + strcat_s (aBuffer, sizeof(aBuffer), "FLT INVALID OPERATION"); + isFloatErr = true; + break; + } case EXCEPTION_FLT_OVERFLOW: -// std::cout << "CallHandler : EXCEPTION_FLT_OVERFLOW:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"FLT OVERFLOW"); - flterr = 1 ; - break ; + { + strcat_s (aBuffer, sizeof(aBuffer), "FLT OVERFLOW"); + isFloatErr = true; + break; + } case EXCEPTION_FLT_STACK_CHECK: -// std::cout << "CallHandler : EXCEPTION_FLT_STACK_CHECK:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"FLT STACK CHECK"); - flterr = 1 ; - break ; + { + strcat_s (aBuffer, sizeof(aBuffer), "FLT STACK CHECK"); + isFloatErr = true; + break; + } case EXCEPTION_FLT_UNDERFLOW: -// std::cout << "CallHandler : EXCEPTION_FLT_UNDERFLOW:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"FLT UNDERFLOW"); - flterr = 1 ; - break ; + { + strcat_s (aBuffer, sizeof(aBuffer), "FLT UNDERFLOW"); + isFloatErr = true; + break; + } case STATUS_FLOAT_MULTIPLE_TRAPS: -// std::cout << "CallHandler : EXCEPTION_FLT_UNDERFLOW:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"FLT MULTIPLE TRAPS (possible overflow in conversion of double to integer)"); - flterr = 1 ; - break ; + { + strcat_s (aBuffer, sizeof(aBuffer), "FLT MULTIPLE TRAPS (possible overflow in conversion of double to integer)"); + isFloatErr = true; + break; + } case STATUS_FLOAT_MULTIPLE_FAULTS: -// std::cout << "CallHandler : EXCEPTION_FLT_UNDERFLOW:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"FLT MULTIPLE FAULTS"); - flterr = 1 ; - break ; + { + strcat_s (aBuffer, sizeof(aBuffer), "FLT MULTIPLE FAULTS"); + isFloatErr = true; + break; + } case STATUS_NO_MEMORY: -// std::cout << "CallHandler : STATUS_NO_MEMORY:" << std::endl ; - THROW_OR_JUMP (OSD_Exception_STATUS_NO_MEMORY, "MEMORY ALLOCATION ERROR ( no room in the process heap )"); + { + THROW_OR_JUMP (OSD_Exception_STATUS_NO_MEMORY, "MEMORY ALLOCATION ERROR ( no room in the process heap )", NULL); break; + } case EXCEPTION_ACCESS_VIOLATION: -// std::cout << "CallHandler : EXCEPTION_ACCESS_VIOLATION:" << std::endl ; - StringCchPrintfW (buffer, _countof(buffer), L"%s%s%s0x%.8p%s%s%s", L"ACCESS VIOLATION", - fMsgBox ? L"\n" : L" ", L"at address ", - ExceptionInformation1 , - L" during '", - ExceptionInformation0 ? L"WRITE" : L"READ", - L"' operation"); + { + _snprintf_s (aBuffer, sizeof(aBuffer), _TRUNCATE, "%s%s%s0x%.8p%s%s%s", "ACCESS VIOLATION", + fMsgBox ? "\n" : " ", + "at address ", (void* )ExceptionInformation1, + " during '", ExceptionInformation0 ? "WRITE" : "READ", "' operation"); break; + } case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: -// std::cout << "CallHandler : EXCEPTION_ARRAY_BOUNDS_EXCEEDED:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"ARRAY BOUNDS EXCEEDED"); + { + strcat_s (aBuffer, sizeof(aBuffer), "ARRAY BOUNDS EXCEEDED"); break; + } case EXCEPTION_DATATYPE_MISALIGNMENT: -// std::cout << "CallHandler : EXCEPTION_DATATYPE_MISALIGNMENT:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"DATATYPE MISALIGNMENT"); + { + strcat_s (aBuffer, sizeof(aBuffer), "DATATYPE MISALIGNMENT"); break; - + } case EXCEPTION_ILLEGAL_INSTRUCTION: -// std::cout << "CallHandler : EXCEPTION_ILLEGAL_INSTRUCTION:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"ILLEGAL INSTRUCTION"); + { + strcat_s (aBuffer, sizeof(aBuffer), "ILLEGAL INSTRUCTION"); break; - + } case EXCEPTION_IN_PAGE_ERROR: -// std::cout << "CallHandler : EXCEPTION_IN_PAGE_ERROR:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"IN_PAGE ERROR"); + { + strcat_s (aBuffer, sizeof(aBuffer), "IN_PAGE ERROR"); break; - + } case EXCEPTION_INT_DIVIDE_BY_ZERO: -// std::cout << "CallHandler : EXCEPTION_INT_DIVIDE_BY_ZERO:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"INTEGER DIVISION BY ZERO"); + { + strcat_s (aBuffer, sizeof(aBuffer), "INTEGER DIVISION BY ZERO"); break; - + } case EXCEPTION_INT_OVERFLOW: -// std::cout << "CallHandler : EXCEPTION_INT_OVERFLOW:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"INTEGER OVERFLOW"); + { + strcat_s (aBuffer, sizeof(aBuffer), "INTEGER OVERFLOW"); break; - + } case EXCEPTION_INVALID_DISPOSITION: -// std::cout << "CallHandler : EXCEPTION_INVALID_DISPOSITION:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"INVALID DISPOSITION"); + { + strcat_s (aBuffer, sizeof(aBuffer), "INVALID DISPOSITION"); break; - + } case EXCEPTION_NONCONTINUABLE_EXCEPTION: -// std::cout << "CallHandler : EXCEPTION_NONCONTINUABLE_EXCEPTION:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"NONCONTINUABLE EXCEPTION"); + { + strcat_s (aBuffer, sizeof(aBuffer), "NONCONTINUABLE EXCEPTION"); break; - + } case EXCEPTION_PRIV_INSTRUCTION: -// std::cout << "CallHandler : EXCEPTION_PRIV_INSTRUCTION:" << std::endl ; - StringCchCopyW (buffer, _countof(buffer), L"PRIVELEGED INSTRUCTION ENCOUNTERED"); + { + strcat_s (aBuffer, sizeof(aBuffer), "PRIVELEGED INSTRUCTION ENCOUNTERED"); break; - + } case EXCEPTION_STACK_OVERFLOW: -// std::cout << "CallHandler : EXCEPTION_STACK_OVERFLOW:" << std::endl ; + { #if defined( _MSC_VER ) && ( _MSC_VER >= 1300 ) && !defined(OCCT_UWP) - // try recovering from stack overflow: available in MS VC++ 7.0 + // try recovering from stack overflow: available in MS VC++ 7.0 if (!_resetstkoflw()) - StringCchCopyW (buffer, _countof(buffer), L"Unrecoverable STACK OVERFLOW"); + { + strcat_s (aBuffer, sizeof(aBuffer), "Unrecoverable STACK OVERFLOW"); + } else #endif - StringCchCopyW (buffer, _countof(buffer), L"STACK OVERFLOW"); + { + strcat_s (aBuffer, sizeof(aBuffer), "STACK OVERFLOW"); + } break; - + } default: - StringCchPrintfW (buffer, _countof(buffer), L"unknown exception code 0x%x, params 0x%p 0x%p", - dwExceptionCode, ExceptionInformation1, ExceptionInformation0 ); - - } // end switch + { + _snprintf_s (aBuffer, sizeof(aBuffer), _TRUNCATE, "unknown exception code 0x%x, params 0x%p 0x%p", + theExceptionCode, (void* )ExceptionInformation1, (void* )ExceptionInformation0); + } + } // reset FPE state (before message box, otherwise it may fail to show up) - if ( flterr ) { + if (isFloatErr) + { OSD::SetFloatingSignal (Standard_True); } -#if ! defined(OCCT_UWP) && !defined(__MINGW32__) && !defined(__CYGWIN32__) - // provide message to the user with possibility to stop - size_t idx; - StringCchLengthW (buffer, _countof(buffer),&idx); - if ( idx && fMsgBox && dwExceptionCode != EXCEPTION_NONCONTINUABLE_EXCEPTION ) { - MessageBeep ( MB_ICONHAND ); - int aChoice = ::MessageBoxW (0, buffer, L"OCCT Exception Handler", MB_ABORTRETRYIGNORE | MB_ICONSTOP); + const int aStackLength = OSD_SignalStackTraceLength; + const int aStackBufLen = Max (aStackLength * 200, 2048); + char* aStackBuffer = aStackLength != 0 ? (char* )alloca (aStackBufLen) : NULL; + if (aStackBuffer != NULL) + { + memset (aStackBuffer, 0, aStackBufLen); + Standard::StackTrace (aStackBuffer, aStackBufLen, aStackLength, theExcPtr->ContextRecord); + } + +#if !defined(OCCT_UWP) && !defined(__MINGW32__) && !defined(__CYGWIN32__) + // provide message to the user with possibility to stop + if (aBuffer[0] != '\0' + && fMsgBox + && theExceptionCode != EXCEPTION_NONCONTINUABLE_EXCEPTION) + { + MessageBeep (MB_ICONHAND); + char aMsgBoxBuffer[2048]; + strcat_s (aMsgBoxBuffer, sizeof(aMsgBoxBuffer), aBuffer); + if (aStackBuffer != NULL) + { + strcat_s (aMsgBoxBuffer, sizeof(aMsgBoxBuffer), aStackBuffer); + } + int aChoice = ::MessageBoxA (0, aMsgBoxBuffer, "OCCT Exception Handler", MB_ABORTRETRYIGNORE | MB_ICONSTOP); if (aChoice == IDRETRY) { _osd_debug(); DebugBreak(); - } else if (aChoice == IDABORT) - exit(0xFFFF); + } + else if (aChoice == IDABORT) + { + exit (0xFFFF); + } } #endif - char aBufferA[2048]; - WideCharToMultiByte(CP_UTF8, 0, buffer, -1, aBufferA, sizeof(aBufferA), NULL, NULL); - return _osd_raise(dwExceptionCode, aBufferA); + return _osd_raise (theExceptionCode, aBuffer, aStackBuffer); } //======================================================================= @@ -279,38 +334,38 @@ static void SIGWntHandler (int signum, int sub_code) std::cout << "signal error" << std::endl ; switch( sub_code ) { case _FPE_INVALID : - CallHandler( EXCEPTION_FLT_INVALID_OPERATION ,0,0) ; + CallHandler (EXCEPTION_FLT_INVALID_OPERATION, NULL); break ; case _FPE_DENORMAL : - CallHandler( EXCEPTION_FLT_DENORMAL_OPERAND ,0,0) ; + CallHandler (EXCEPTION_FLT_DENORMAL_OPERAND, NULL); break ; case _FPE_ZERODIVIDE : - CallHandler( EXCEPTION_FLT_DIVIDE_BY_ZERO ,0,0) ; + CallHandler (EXCEPTION_FLT_DIVIDE_BY_ZERO, NULL); break ; case _FPE_OVERFLOW : - CallHandler( EXCEPTION_FLT_OVERFLOW ,0,0) ; + CallHandler (EXCEPTION_FLT_OVERFLOW, NULL); break ; case _FPE_UNDERFLOW : - CallHandler( EXCEPTION_FLT_UNDERFLOW ,0,0) ; + CallHandler (EXCEPTION_FLT_UNDERFLOW, NULL); break ; case _FPE_INEXACT : - CallHandler( EXCEPTION_FLT_INEXACT_RESULT ,0,0) ; + CallHandler (EXCEPTION_FLT_INEXACT_RESULT, NULL); break ; default: std::cout << "SIGWntHandler(default) -> throw Standard_NumericError(\"Floating Point Error\");" << std::endl; - THROW_OR_JUMP (Standard_NumericError, "Floating Point Error"); + THROW_OR_JUMP (Standard_NumericError, "Floating Point Error", NULL); break ; } break ; case SIGSEGV : if ( signal( signum, (void(*)(int))SIGWntHandler ) == SIG_ERR ) std::cout << "signal error" << std::endl ; - CallHandler( EXCEPTION_ACCESS_VIOLATION ,0,0) ; + CallHandler (EXCEPTION_ACCESS_VIOLATION, NULL); break ; case SIGILL : if ( signal( signum, (void(*)(int))SIGWntHandler ) == SIG_ERR ) std::cout << "signal error" << std::endl ; - CallHandler( EXCEPTION_ILLEGAL_INSTRUCTION ,0,0) ; + CallHandler (EXCEPTION_ILLEGAL_INSTRUCTION, NULL); break ; default: std::cout << "SIGWntHandler unexpected signal : " << signum << std::endl ; @@ -337,12 +392,7 @@ static void SIGWntHandler (int signum, int sub_code) static void TranslateSE( unsigned int theCode, EXCEPTION_POINTERS* theExcPtr ) { Standard_Mutex::Sentry aSentry (THE_SIGNAL_MUTEX); // lock the mutex to prevent simultaneous handling - ptrdiff_t info1 = 0, info0 = 0; - if ( theExcPtr ) { - info1 = theExcPtr->ExceptionRecord->ExceptionInformation[1]; - info0 = theExcPtr->ExceptionRecord->ExceptionInformation[0]; - } - CallHandler(theCode, info1, info0); + CallHandler (theCode, theExcPtr); } #endif @@ -354,11 +404,8 @@ static void TranslateSE( unsigned int theCode, EXCEPTION_POINTERS* theExcPtr ) //======================================================================= static LONG WINAPI WntHandler (EXCEPTION_POINTERS *lpXP) { - DWORD dwExceptionCode = lpXP->ExceptionRecord->ExceptionCode; - - return CallHandler (dwExceptionCode, - lpXP->ExceptionRecord->ExceptionInformation[1], - lpXP->ExceptionRecord->ExceptionInformation[0]); + DWORD dwExceptionCode = lpXP->ExceptionRecord->ExceptionCode; + return CallHandler (dwExceptionCode, lpXP); } //======================================================================= @@ -422,6 +469,11 @@ void OSD::SetSignal (OSD_SignalMode theSignalMode, { std::cout << "Environment variable CSF_DEBUG_MODE setted.\n"; fMsgBox = Standard_True; + if (OSD_SignalStackTraceLength == 0) + { + // enable stack trace if CSF_DEBUG_MODE is set + OSD_SignalStackTraceLength = 10; + } } else { @@ -508,67 +560,71 @@ static BOOL WINAPI _osd_ctrl_break_handler ( DWORD dwCode ) { //============================================================================ //==== _osd_raise //============================================================================ -static LONG __fastcall _osd_raise ( DWORD dwCode, LPSTR msg ) +static LONG __fastcall _osd_raise (DWORD theCode, const char* theMsg, const char* theStack) { - if (msg[0] == '\x03') ++msg; + const char* aMsg = theMsg; + if (aMsg[0] == '\x03') + { + ++aMsg; + } - switch (dwCode) + switch (theCode) { case EXCEPTION_ACCESS_VIOLATION: - THROW_OR_JUMP (OSD_Exception_ACCESS_VIOLATION, msg); + THROW_OR_JUMP (OSD_Exception_ACCESS_VIOLATION, aMsg, theStack); break; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - THROW_OR_JUMP (OSD_Exception_ARRAY_BOUNDS_EXCEEDED, msg); + THROW_OR_JUMP (OSD_Exception_ARRAY_BOUNDS_EXCEEDED, aMsg, theStack); break; case EXCEPTION_DATATYPE_MISALIGNMENT: - THROW_OR_JUMP (Standard_ProgramError, msg); + THROW_OR_JUMP (Standard_ProgramError, aMsg, theStack); break; case EXCEPTION_ILLEGAL_INSTRUCTION: - THROW_OR_JUMP (OSD_Exception_ILLEGAL_INSTRUCTION, msg); + THROW_OR_JUMP (OSD_Exception_ILLEGAL_INSTRUCTION, aMsg, theStack); break; case EXCEPTION_IN_PAGE_ERROR: - THROW_OR_JUMP (OSD_Exception_IN_PAGE_ERROR, msg); + THROW_OR_JUMP (OSD_Exception_IN_PAGE_ERROR, aMsg, theStack); break; case EXCEPTION_INT_DIVIDE_BY_ZERO: - THROW_OR_JUMP (Standard_DivideByZero, msg); + THROW_OR_JUMP (Standard_DivideByZero, aMsg, theStack); break; case EXCEPTION_INT_OVERFLOW: - THROW_OR_JUMP (OSD_Exception_INT_OVERFLOW, msg); + THROW_OR_JUMP (OSD_Exception_INT_OVERFLOW, aMsg, theStack); break; case EXCEPTION_INVALID_DISPOSITION: - THROW_OR_JUMP (OSD_Exception_INVALID_DISPOSITION, msg); + THROW_OR_JUMP (OSD_Exception_INVALID_DISPOSITION, aMsg, theStack); break; case EXCEPTION_NONCONTINUABLE_EXCEPTION: - THROW_OR_JUMP (OSD_Exception_NONCONTINUABLE_EXCEPTION, msg); + THROW_OR_JUMP (OSD_Exception_NONCONTINUABLE_EXCEPTION, aMsg, theStack); break; case EXCEPTION_PRIV_INSTRUCTION: - THROW_OR_JUMP (OSD_Exception_PRIV_INSTRUCTION, msg); + THROW_OR_JUMP (OSD_Exception_PRIV_INSTRUCTION, aMsg, theStack); break; case EXCEPTION_STACK_OVERFLOW: - THROW_OR_JUMP (OSD_Exception_STACK_OVERFLOW, msg); + THROW_OR_JUMP (OSD_Exception_STACK_OVERFLOW, aMsg, theStack); break; case EXCEPTION_FLT_DIVIDE_BY_ZERO: - THROW_OR_JUMP (Standard_DivideByZero, msg); + THROW_OR_JUMP (Standard_DivideByZero, aMsg, theStack); break; case EXCEPTION_FLT_STACK_CHECK: case EXCEPTION_FLT_OVERFLOW: - THROW_OR_JUMP (Standard_Overflow, msg); + THROW_OR_JUMP (Standard_Overflow, aMsg, theStack); break; case EXCEPTION_FLT_UNDERFLOW: - THROW_OR_JUMP (Standard_Underflow, msg); + THROW_OR_JUMP (Standard_Underflow, aMsg, theStack); break; case EXCEPTION_FLT_INVALID_OPERATION: case EXCEPTION_FLT_DENORMAL_OPERAND: case EXCEPTION_FLT_INEXACT_RESULT: case STATUS_FLOAT_MULTIPLE_TRAPS: case STATUS_FLOAT_MULTIPLE_FAULTS: - THROW_OR_JUMP (Standard_NumericError, msg); + THROW_OR_JUMP (Standard_NumericError, aMsg, theStack); break; default: break; - } // end switch + } return EXCEPTION_EXECUTE_HANDLER; -} // end _osd_raise +} #if ! defined(OCCT_UWP) && !defined(__MINGW32__) && !defined(__CYGWIN32__) //============================================================================ @@ -849,32 +905,41 @@ static void Handler (const int theSignal) #ifdef SA_SIGINFO static void SegvHandler(const int theSignal, - siginfo_t *ip, + siginfo_t* theSigInfo, const Standard_Address theContext) { - (void)theSignal; // silence GCC warnings + (void)theSignal; (void)theContext; + if (theSigInfo != NULL) + { + sigset_t set; + sigemptyset (&set); + sigaddset (&set, SIGSEGV); + sigprocmask (SIG_UNBLOCK, &set, NULL); + void* anAddress = theSigInfo->si_addr; + { + char aMsg[100]; + sprintf (aMsg, "SIGSEGV 'segmentation violation' detected. Address %lx.", (long )anAddress); -// std::cout << "OSD::SegvHandler activated(SA_SIGINFO)" << std::endl ; - if ( ip != NULL ) { - sigset_t set; - sigemptyset(&set); - sigaddset(&set, SIGSEGV); - sigprocmask (SIG_UNBLOCK, &set, NULL) ; - void *address = ip->si_addr ; - { - char Msg[100]; - sprintf(Msg,"SIGSEGV 'segmentation violation' detected. Address %lx", - (long ) address ) ; - OSD_SIGSEGV::NewInstance(Msg)->Jump(); - } + const int aStackLength = OSD_SignalStackTraceLength; + const int aStackBufLen = Max (aStackLength * 200, 2048); + char* aStackBuffer = aStackLength != 0 ? (char* )alloca (aStackBufLen) : NULL; + if (aStackBuffer != NULL) + { + memset (aStackBuffer, 0, aStackBufLen); + Standard::StackTrace (aStackBuffer, aStackBufLen, aStackLength); + } + + OSD_SIGSEGV::NewInstance (aMsg, aStackBuffer)->Jump(); + } } #ifdef OCCT_DEBUG - else { - std::cout << "Wrong undefined address." << std::endl ; + else + { + std::cout << "Wrong undefined address." << std::endl; } #endif - exit(SIGSEGV); + exit (SIGSEGV); } #elif defined (_hpux) || defined(HPUX) @@ -882,30 +947,26 @@ static void SegvHandler(const int theSignal, // pour version 09.07 static void SegvHandler(const int theSignal, - siginfo_t *ip, + siginfo_t* theSigInfo, const Standard_Address theContext) { - unsigned long Space ; - unsigned long Offset ; - char Msg[100] ; - - if ( theContext != NULL ) { - Space = ((struct sigcontext *)theContext)->sc_sl.sl_ss.ss_cr20 ; - Offset = ((struct sigcontext *)theContext)->sc_sl.sl_ss.ss_cr21 ; -// std::cout << "Wrong address = " << hex(Offset) << std::endl ; + if (theContext != NULL) + { + unsigned long aSpace = ((struct sigcontext *)theContext)->sc_sl.sl_ss.ss_cr20; + unsigned long anOffset = ((struct sigcontext *)theContext)->sc_sl.sl_ss.ss_cr21; { - sprintf(Msg,"SIGSEGV 'segmentation violation' detected. Address %lx",Offset) ; - OSD_SIGSEGV::Jump(Msg); -// scp->sc_pcoq_head = scp->sc_pcoq_tail ; Permettrait de continuer a -// scp->sc_pcoq_tail = scp->sc_pcoq_tail + 0x4 ; l'intruction suivant le segv. + char aMsg[100]; + sprintf (aMsg, "SIGSEGV 'segmentation violation' detected. Address %lx", anOffset); + OSD_SIGSEGV::NewInstance (aMsg)->Jump(); } } #ifdef OCCT_DEBUG - else { - std::cout << "Wrong undefined address." << std::endl ; + else + { + std::cout << "Wrong undefined address." << std::endl; } #endif - exit(SIGSEGV); + exit (SIGSEGV); } #endif diff --git a/src/QABugs/QABugs_11.cxx b/src/QABugs/QABugs_11.cxx index c8673911fa..3bf0fe0caf 100644 --- a/src/QABugs/QABugs_11.cxx +++ b/src/QABugs/QABugs_11.cxx @@ -2396,6 +2396,88 @@ static Standard_Integer OCC6143 (Draw_Interpretor& di, Standard_Integer argc, co return 0; } +//! Auxiliary functions for printing synthetic backtrace +class MyTestInterface : public Standard_Transient +{ +public: + virtual int Standard_NOINLINE testMethod3 (int* theIntPtr, bool theToPrintStack) = 0; +}; + +class MyTestClass : public MyTestInterface +{ +public: + MyTestClass() {} + virtual int Standard_NOINLINE testMethod3 (int* theIntPtr, bool theToPrintStack) + { + if (theToPrintStack) + { + char aMsg[4096] = {}; + Standard::StackTrace (aMsg, 4096, 10); + std::cout << aMsg << "\n"; + return 0; + } + *theIntPtr = 4; + return *theIntPtr; + } +}; + +static int Standard_NOINLINE myTestFunction2 (int* theIntPtr, bool theToPrintStack) +{ + Handle(MyTestInterface) aTest = new MyTestClass(); + return aTest->testMethod3 (theIntPtr, theToPrintStack); +} + +static void Standard_NOINLINE myTestFunction1 (bool theToPrintStack) +{ + int* anIntPtr = NULL; + myTestFunction2 (anIntPtr, theToPrintStack); +} + +static Standard_NOINLINE Standard_Integer OCC30762 (Draw_Interpretor& theDI, + Standard_Integer theNbArgs, + const char** ) +{ + if (theNbArgs != 1) + { + theDI << "Syntax error: wrong number of arguments"; + return 1; + } + + // just print stack + std::cout << "Test normal backtrace...\n"; + myTestFunction1 (true); + + // test access violation + { + try + { + OCC_CATCH_SIGNALS + std::cout << "Test segmentation Fault...\n"; + myTestFunction1 (false); + std::cout << "Error: writing by NULL address - no exception is raised!\n"; + } + #ifdef _WIN32 + catch (OSD_Exception_ACCESS_VIOLATION const& aSegException) + #else + catch (OSD_SIGSEGV const& aSegException) + #endif + { + theDI << " Caught ("; + theDI << aSegException.GetMessageString(); + theDI << aSegException.GetStackString(); + theDI << ")... OK\n"; + } + catch (Standard_Failure const& anException) + { + theDI << " Caught ("; + theDI << anException.GetMessageString(); + theDI << anException.GetStackString(); + theDI << ")... KO\n"; + } + } + return 0; +} + //! Auxiliary functor. struct TestParallelFunctor { @@ -4949,6 +5031,7 @@ void QABugs::Commands_11(Draw_Interpretor& theCommands) { theCommands.Add("OCC5698", "OCC5698 wire", __FILE__, OCC5698, group); theCommands.Add("OCC6143", "OCC6143 catching signals", __FILE__, OCC6143, group); theCommands.Add("OCC30775", "OCC30775 catching signals in threads", __FILE__, OCC30775, group); + theCommands.Add("OCC30762", "OCC30762 printing backtrace", __FILE__, OCC30762, group); theCommands.Add("OCC7141", "OCC7141 [nCount] aPath", __FILE__, OCC7141, group); theCommands.Add("OCC7372", "OCC7372", __FILE__, OCC7372, group); theCommands.Add("OCC8169", "OCC8169 edge1 edge2 plane", __FILE__, OCC8169, group); diff --git a/src/Standard/FILES b/src/Standard/FILES index dc0f0c9eeb..138b85b0fc 100755 --- a/src/Standard/FILES +++ b/src/Standard/FILES @@ -87,6 +87,7 @@ Standard_ShortReal.cxx Standard_ShortReal.hxx Standard_Size.hxx Standard_SStream.hxx +Standard_StackTrace.cxx Standard_Std.hxx Standard_Stream.hxx Standard_Strtod.cxx diff --git a/src/Standard/Standard.hxx b/src/Standard/Standard.hxx index 768abb964f..3b0b1aea67 100644 --- a/src/Standard/Standard.hxx +++ b/src/Standard/Standard.hxx @@ -84,6 +84,28 @@ public: //! Returns non-zero if some memory has been actually freed. Standard_EXPORT static Standard_Integer Purge(); + //! Appends backtrace to a message buffer. + //! Stack information might be incomplete in case of stripped binaries. + //! Implementation details: + //! - Not implemented for Android, iOS, QNX and UWP platforms. + //! - On non-Windows platform, this function is a wrapper to backtrace() system call. + //! - On Windows (Win32) platform, the function loads DbgHelp.dll dynamically, + //! and no stack will be provided if this or companion libraries (SymSrv.dll, SrcSrv.dll, etc.) will not be found; + //! .pdb symbols should be provided on Windows platform to retrieve a meaningful stack; + //! only x86_64 CPU architecture is currently implemented. + //! @param theBuffer [in] [out] message buffer to extend + //! @param theBufferSize [in] message buffer size + //! @param theNbTraces [in] maximum number of stack traces + //! @param theContext [in] optional platform-dependent frame context; + //! in case of DbgHelp (Windows) should be a pointer to CONTEXT + //! @param theNbTopSkip [in] number of traces on top of the stack to skip + //! @return TRUE on success + Standard_EXPORT static Standard_Boolean StackTrace (char* theBuffer, + const int theBufferSize, + const int theNbTraces, + void* theContext = NULL, + const int theNbTopSkip = 0); + }; // include definition of handle to make it always visible diff --git a/src/Standard/Standard_DefineException.hxx b/src/Standard/Standard_DefineException.hxx index cea1a19e0b..5ca3ea2e9b 100644 --- a/src/Standard/Standard_DefineException.hxx +++ b/src/Standard/Standard_DefineException.hxx @@ -32,7 +32,9 @@ class C1 : public C2 { \ void Throw () const Standard_OVERRIDE { throw *this; } \ public: \ C1() : C2() {} \ - C1(const Standard_CString theMessage) : C2(theMessage) {} \ + C1(Standard_CString theMessage) : C2(theMessage) {} \ + C1(Standard_CString theMessage, Standard_CString theStackTrace) \ + : C2 (theMessage, theStackTrace) {} \ static void Raise(const Standard_CString theMessage = "") { \ Handle(C1) _E = new C1; \ _E->Reraise(theMessage); \ @@ -41,7 +43,8 @@ public: \ Handle(C1) _E = new C1; \ _E->Reraise (theMessage); \ } \ - static Handle(C1) NewInstance(const Standard_CString theMessage = "") { return new C1(theMessage); } \ + static Handle(C1) NewInstance(Standard_CString theMessage = "") { return new C1(theMessage); } \ + static Handle(C1) NewInstance(Standard_CString theMessage, Standard_CString theStackTrace) { return new C1(theMessage, theStackTrace); } \ DEFINE_STANDARD_RTTI_INLINE(C1,C2) \ }; diff --git a/src/Standard/Standard_Failure.cxx b/src/Standard/Standard_Failure.cxx index b0b1540998..cb88d8d2e1 100644 --- a/src/Standard/Standard_Failure.cxx +++ b/src/Standard/Standard_Failure.cxx @@ -12,9 +12,9 @@ // Alternatively, this file may be used under the terms of Open CASCADE // commercial license or contractual agreement. +#include #include -#include #include #include #include @@ -22,173 +22,341 @@ #include #include + IMPLEMENT_STANDARD_RTTIEXT(Standard_Failure,Standard_Transient) -static Standard_CString allocate_message(const Standard_CString AString) +namespace { - Standard_CString aStr = 0; - if(AString) { - const Standard_Size aLen = strlen(AString); - aStr = (Standard_CString) malloc(aLen+sizeof(Standard_Integer)+1); - if (aStr) { - Standard_PCharacter pStr=(Standard_PCharacter)aStr; - strcpy(pStr+sizeof(Standard_Integer),AString); - *((Standard_Integer*)aStr) = 1; + //! Global last failure object returned by Standard_Failure::Caught(). + static Standard_THREADLOCAL Handle(Standard_Failure) Standard_Failure_RaisedError; + + //! Global parameter defining default length of stack trace. + static Standard_Integer Standard_Failure_DefaultStackTraceLength = 0; +} + +// ======================================================================= +// function : StringRef::allocate_message +// purpose : +// ======================================================================= +Standard_Failure::StringRef* Standard_Failure::StringRef::allocate_message (const Standard_CString theString) +{ + if (theString == NULL + || *theString == '\0') + { + return NULL; + } + + const Standard_Size aLen = strlen (theString); + StringRef* aStrPtr = (StringRef* )malloc (aLen + sizeof(Standard_Integer) + 1); + if (aStrPtr != NULL) + { + strcpy ((char* )&aStrPtr->Message[0], theString); + aStrPtr->Counter = 1; + } + return aStrPtr; +} + +// ======================================================================= +// function : StringRef::copy_message +// purpose : +// ======================================================================= +Standard_Failure::StringRef* Standard_Failure::StringRef::copy_message (Standard_Failure::StringRef* theString) +{ + if (theString == NULL) + { + return NULL; + } + + ++theString->Counter; + return theString; +} + +// ======================================================================= +// function : StringRef::deallocate_message +// purpose : +// ======================================================================= +void Standard_Failure::StringRef::deallocate_message (Standard_Failure::StringRef* theString) +{ + if (theString != NULL) + { + if (--theString->Counter == 0) + { + free ((void* )theString); } } - return aStr; } -static Standard_CString copy_message(Standard_CString aMessage) +// ======================================================================= +// function : Standard_Failure +// purpose : +// ======================================================================= +Standard_Failure::Standard_Failure() +: myMessage (NULL), + myStackTrace (NULL) { - Standard_CString aStr = 0; - if(aMessage) { - aStr = aMessage; - (*((Standard_Integer*)aStr))++; - } - return aStr; -} - -static void deallocate_message(Standard_CString aMessage) -{ - if(aMessage) { - (*((Standard_Integer*)aMessage))--; - if(*((Standard_Integer*)aMessage)==0) - free((void*)aMessage); + const Standard_Integer aStackLength = Standard_Failure_DefaultStackTraceLength; + if (aStackLength > 0) + { + int aStackBufLen = Max (aStackLength * 200, 2048); + char* aStackBuffer = (char* )alloca (aStackBufLen); + if (aStackBuffer != NULL) + { + memset (aStackBuffer, 0, aStackBufLen); + if (Standard::StackTrace (aStackBuffer, aStackBufLen, aStackLength, NULL, 1)) + { + myStackTrace = StringRef::allocate_message (aStackBuffer); + } + } } } -// ****************************************************************** -// Standard_Failure * -// ****************************************************************** -static Standard_THREADLOCAL Handle(Standard_Failure) RaisedError; - -// ------------------------------------------------------------------ -// -// ------------------------------------------------------------------ -Standard_Failure::Standard_Failure () -: myMessage(NULL) +// ======================================================================= +// function : Standard_Failure +// purpose : +// ======================================================================= +Standard_Failure::Standard_Failure (const Standard_CString theDesc) +: myMessage (NULL), + myStackTrace (NULL) { + myMessage = StringRef::allocate_message (theDesc); + const Standard_Integer aStackLength = Standard_Failure_DefaultStackTraceLength; + if (aStackLength > 0) + { + int aStackBufLen = Max (aStackLength * 200, 2048); + char* aStackBuffer = (char* )alloca (aStackBufLen); + if (aStackBuffer != NULL) + { + memset (aStackBuffer, 0, aStackBufLen); + Standard::StackTrace (aStackBuffer, aStackBufLen, aStackLength, NULL, 1); + myStackTrace = StringRef::allocate_message (aStackBuffer); + } + } } -// ------------------------------------------------------------------ -// Create returns mutable Failure; -// ------------------------------------------------------------------ -Standard_Failure::Standard_Failure (const Standard_CString AString) -: myMessage(NULL) +// ======================================================================= +// function : Standard_Failure +// purpose : +// ======================================================================= +Standard_Failure::Standard_Failure (const Standard_CString theDesc, + const Standard_CString theStackTrace) +: myMessage (NULL), + myStackTrace (NULL) { - myMessage = allocate_message(AString); + myMessage = StringRef::allocate_message (theDesc); + myStackTrace = StringRef::allocate_message (theStackTrace); } -Standard_Failure::Standard_Failure (const Standard_Failure& theFailure) -: Standard_Transient(theFailure) +// ======================================================================= +// function : Standard_Failure +// purpose : +// ======================================================================= +Standard_Failure::Standard_Failure (const Standard_Failure& theFailure) +: Standard_Transient (theFailure), + myMessage (NULL), + myStackTrace (NULL) { - myMessage = copy_message(theFailure.myMessage); + myMessage = StringRef::copy_message (theFailure.myMessage); + myStackTrace = StringRef::copy_message (theFailure.myStackTrace); } +// ======================================================================= +// function : ~Standard_Failure +// purpose : +// ======================================================================= Standard_Failure::~Standard_Failure() { - deallocate_message(myMessage); + StringRef::deallocate_message (myMessage); + StringRef::deallocate_message (myStackTrace); } -void Standard_Failure::SetMessageString(const Standard_CString AString) +// ======================================================================= +// function : GetMessageString +// purpose : +// ======================================================================= +Standard_CString Standard_Failure::GetMessageString() const { - if ( AString == GetMessageString() ) return; - deallocate_message(myMessage); - myMessage = allocate_message(AString); + return myMessage != NULL + ? myMessage->GetMessage() + : ""; } -// ------------------------------------------------------------------ -// Caught (myclass) returns mutable Failure raises NoSuchObject ; -// ------------------------------------------------------------------ -Handle(Standard_Failure) Standard_Failure::Caught() +// ======================================================================= +// function : SetMessageString +// purpose : +// ======================================================================= +void Standard_Failure::SetMessageString (const Standard_CString theDesc) { - return RaisedError ; + if (theDesc == GetMessageString()) + { + return; + } + + StringRef::deallocate_message (myMessage); + myMessage = StringRef::allocate_message (theDesc); } -// ------------------------------------------------------------------ -// Raise (myclass; aMessage: CString = "") ; -// ------------------------------------------------------------------ -void Standard_Failure::Raise (const Standard_CString AString) +// ======================================================================= +// function : GetStackString +// purpose : +// ======================================================================= +Standard_CString Standard_Failure::GetStackString() const +{ + return myStackTrace != NULL + ? myStackTrace->GetMessage() + : ""; +} + +// ======================================================================= +// function : SetStackString +// purpose : +// ======================================================================= +void Standard_Failure::SetStackString (const Standard_CString theStack) +{ + if (theStack == GetStackString()) + { + return; + } + + StringRef::deallocate_message (myStackTrace); + myStackTrace = StringRef::allocate_message (theStack); +} + +// ======================================================================= +// function : Caught +// purpose : +// ======================================================================= +Handle(Standard_Failure) Standard_Failure::Caught() +{ + return Standard_Failure_RaisedError; +} + +// ======================================================================= +// function : Raise +// purpose : +// ======================================================================= +void Standard_Failure::Raise (const Standard_CString theDesc) { - Handle(Standard_Failure) E = new Standard_Failure() ; - E->Reraise (AString) ; + Handle(Standard_Failure) aFailure = new Standard_Failure(); + aFailure->Reraise (theDesc); } -// ------------------------------------------------------------------ -// Raise(myclass; aReason: in out SStream) ; -// ------------------------------------------------------------------ -void Standard_Failure::Raise (const Standard_SStream& AReason) +// ======================================================================= +// function : Raise +// purpose : +// ======================================================================= +void Standard_Failure::Raise (const Standard_SStream& theReason) { - Handle(Standard_Failure) E = new Standard_Failure(); - E->Reraise (AReason); + Handle(Standard_Failure) aFailure = new Standard_Failure(); + aFailure->Reraise (theReason); } -// ------------------------------------------------------------------ -// Reraise (me: mutable; aMessage: CString) ; -// ------------------------------------------------------------------ -void Standard_Failure::Reraise (const Standard_CString AString) +// ======================================================================= +// function : Reraise +// purpose : +// ======================================================================= +void Standard_Failure::Reraise (const Standard_CString theDesc) { - SetMessageString(AString); + SetMessageString (theDesc); Reraise(); } -void Standard_Failure::Reraise (const Standard_SStream& AReason) +// ======================================================================= +// function : Reraise +// purpose : +// ======================================================================= +void Standard_Failure::Reraise (const Standard_SStream& theReason) { - SetMessageString(AReason.str().c_str()); + SetMessageString (theReason.str().c_str()); Reraise(); } -void Standard_Failure::Reraise () +// ======================================================================= +// function : Reraise +// purpose : +// ======================================================================= +void Standard_Failure::Reraise() { - RaisedError = this; + Standard_Failure_RaisedError = this; Throw(); } +// ======================================================================= +// function : Jump +// purpose : +// ======================================================================= void Standard_Failure::Jump() { #if defined (OCC_CONVERT_SIGNALS) Standard_ErrorHandler::Error (this); Standard_ErrorHandler::Abort (this); #else - RaisedError = this; + Standard_Failure_RaisedError = this; Throw(); #endif } - -// ------------------------------------------------------------------ -// Throw (me) is virtual ; -// ------------------------------------------------------------------ +// ======================================================================= +// function : Throw +// purpose : +// ======================================================================= void Standard_Failure::Throw() const { throw *this; } -// ------------------------------------------------------------------ -// Print (me; s: in out OStream) returns OStream; -// ------------------------------------------------------------------ -void Standard_Failure::Print (Standard_OStream& AStream) const +// ======================================================================= +// function : Print +// purpose : +// ======================================================================= +void Standard_Failure::Print (Standard_OStream& theStream) const { -if(myMessage){ - AStream << DynamicType() << ": " << GetMessageString(); - } - else { - AStream << DynamicType(); - } + if (myMessage != NULL) + { + theStream << DynamicType() << ": " << GetMessageString(); + } + else + { + theStream << DynamicType(); + } + if (myStackTrace != NULL) + { + theStream << GetStackString(); + } } -Handle(Standard_Failure) Standard_Failure::NewInstance(const Standard_CString AString) +// ======================================================================= +// function : NewInstance +// purpose : +// ======================================================================= +Handle(Standard_Failure) Standard_Failure::NewInstance (Standard_CString theString) { - return new Standard_Failure(AString) ; + return new Standard_Failure (theString); } -//======================================================================= -//function : GetMessageString -//purpose : Returns error message -//======================================================================= -Standard_CString Standard_Failure::GetMessageString () const +// ======================================================================= +// function : NewInstance +// purpose : +// ======================================================================= +Handle(Standard_Failure) Standard_Failure::NewInstance (Standard_CString theMessage, + Standard_CString theStackTrace) { - return (myMessage ? myMessage+sizeof(Standard_Integer) : ""); + return new Standard_Failure (theMessage, theStackTrace); } +// ======================================================================= +// function : GetNbStackTraces +// purpose : +// ======================================================================= +Standard_Integer Standard_Failure::DefaultStackTraceLength() +{ + return Standard_Failure_DefaultStackTraceLength; +} + +// ======================================================================= +// function : SetNbStackTraces +// purpose : +// ======================================================================= +void Standard_Failure::SetDefaultStackTraceLength (Standard_Integer theNbStackTraces) +{ + Standard_Failure_DefaultStackTraceLength = theNbStackTraces; +} diff --git a/src/Standard/Standard_Failure.hxx b/src/Standard/Standard_Failure.hxx index 37e8304cf8..ee443a0f94 100644 --- a/src/Standard/Standard_Failure.hxx +++ b/src/Standard/Standard_Failure.hxx @@ -17,28 +17,20 @@ #ifndef _Standard_Failure_HeaderFile #define _Standard_Failure_HeaderFile -#include #include #include #include #include #include -class Standard_NoSuchObject; - -class Standard_Failure; DEFINE_STANDARD_HANDLE(Standard_Failure, Standard_Transient) - //! Forms the root of the entire exception hierarchy. class Standard_Failure : public Standard_Transient { - public: - - //! Creates a status object of type "Failure". Standard_EXPORT Standard_Failure(); @@ -46,11 +38,18 @@ public: Standard_EXPORT Standard_Failure (const Standard_Failure& f); //! Creates a status object of type "Failure". - Standard_EXPORT Standard_Failure(const Standard_CString aString); + //! @param theDesc [in] exception description + Standard_EXPORT Standard_Failure (const Standard_CString theDesc); + + //! Creates a status object of type "Failure" with stack trace. + //! @param theDesc [in] exception description + //! @param theStackTrace [in] associated stack trace + Standard_EXPORT Standard_Failure (const Standard_CString theDesc, + const Standard_CString theStackTrace); //! Assignment operator Standard_EXPORT Standard_Failure& operator= (const Standard_Failure& f); - + //! Destructor Standard_EXPORT ~Standard_Failure(); @@ -63,15 +62,23 @@ public: Standard_EXPORT virtual Standard_CString GetMessageString() const; //! Sets error message - Standard_EXPORT virtual void SetMessageString (const Standard_CString aMessage); - + Standard_EXPORT virtual void SetMessageString (const Standard_CString theMessage); + + //! Returns the stack trace string + Standard_EXPORT virtual Standard_CString GetStackString() const; + + //! Sets the stack trace string + Standard_EXPORT virtual void SetStackString (const Standard_CString theStack); + Standard_EXPORT void Reraise(); Standard_EXPORT void Reraise (const Standard_CString aMessage); //! Reraises a caught exception and changes its error message. Standard_EXPORT void Reraise (const Standard_SStream& aReason); - + +public: + //! Raises an exception of type "Failure" and associates //! an error message to it. The message can be printed //! in an exception handler. @@ -82,13 +89,24 @@ public: //! at run-time. Standard_EXPORT static void Raise (const Standard_SStream& aReason); - //! Used to construct an instance of the exception object - //! as a handle. Shall be used to protect against possible - //! construction of exception object in C stack -- that is - //! dangerous since some of methods require that object - //! was allocated dynamically. - Standard_EXPORT static Handle(Standard_Failure) NewInstance (const Standard_CString aMessage); - + //! Used to construct an instance of the exception object as a handle. + //! Shall be used to protect against possible construction of exception object in C stack, + //! which is dangerous since some of methods require that object was allocated dynamically. + Standard_EXPORT static Handle(Standard_Failure) NewInstance (Standard_CString theMessage); + + //! Used to construct an instance of the exception object as a handle. + Standard_EXPORT static Handle(Standard_Failure) NewInstance (Standard_CString theMessage, + Standard_CString theStackTrace); + + //! Returns the default length of stack trace to be captured by Standard_Failure constructor; + //! 0 by default meaning no stack trace. + Standard_EXPORT static Standard_Integer DefaultStackTraceLength(); + + //! Sets default length of stack trace to be captured by Standard_Failure constructor. + Standard_EXPORT static void SetDefaultStackTraceLength (Standard_Integer theNbStackTraces); + +public: + //! Used to throw CASCADE exception from C signal handler. //! On platforms that do not allow throwing C++ exceptions //! from this handler (e.g. Linux), uses longjump to get to @@ -102,41 +120,66 @@ public: Standard_DEPRECATED("This method is deprecated (not thread-safe), use standard C++ mechanism instead") Standard_EXPORT static Handle(Standard_Failure) Caught(); - - DEFINE_STANDARD_RTTIEXT(Standard_Failure,Standard_Transient) protected: - //! Used only if standard C++ exceptions are used. //! Throws exception of the same type as this by C++ throw, //! and stores current object as last thrown exception, //! to be accessible by method Caught() Standard_EXPORT virtual void Throw() const; +private: + //! Reference-counted string, + //! Memory block is allocated with an extra 4-byte header (int representing number of references) + //! using low-level malloc() to avoid exceptions. + struct StringRef + { + Standard_Integer Counter; + Standard_Character Message[1]; + + //! Return message string. + Standard_CString GetMessage() const { return (Standard_CString )&Message[0]; } + + //! Allocate reference-counted message string. + static StringRef* allocate_message (Standard_CString theString); + + //! Copy reference-counted message string. + static StringRef* copy_message (StringRef* theString); + + //! Release reference-counted message string. + static void deallocate_message (StringRef* theString); + }; private: - - Standard_CString myMessage; - + StringRef* myMessage; + StringRef* myStackTrace; }; -inline Standard_OStream& operator << (Standard_OStream& AStream, - const Handle(Standard_Failure)& AFailure) +// ======================================================================= +// function : operator<< +// purpose : +// ======================================================================= +inline Standard_OStream& operator<< (Standard_OStream& theStream, + const Handle(Standard_Failure)& theFailure) { - AFailure->Print(AStream); - return AStream; + theFailure->Print (theStream); + return theStream; } -inline Standard_OStream& operator << (Standard_OStream& AStream, - const Standard_Failure& AFailure) +// ======================================================================= +// function : operator<< +// purpose : +// ======================================================================= +inline Standard_OStream& operator<< (Standard_OStream& theStream, + const Standard_Failure& theFailure) { - AFailure.Print(AStream); - return AStream; + theFailure.Print (theStream); + return theStream; } #endif // _Standard_Failure_HeaderFile diff --git a/src/Standard/Standard_Macro.hxx b/src/Standard/Standard_Macro.hxx index 430c03bd82..b510367c1e 100644 --- a/src/Standard/Standard_Macro.hxx +++ b/src/Standard/Standard_Macro.hxx @@ -90,6 +90,17 @@ #define Standard_UNUSED #endif +//! @def Standard_NOINLINE +//! Macro for disallowing function inlining. +//! Expands to "__attribute__((noinline))" on GCC and CLang. +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) + #define Standard_NOINLINE __attribute__((noinline)) +#elif defined(_MSC_VER) + #define Standard_NOINLINE __declspec(noinline) +#else + #define Standard_NOINLINE +#endif + //! @def Standard_THREADLOCAL //! Define Standard_THREADLOCAL modifier as C++11 thread_local keyword where it is available. #if defined(__clang__) diff --git a/src/Standard/Standard_OutOfMemory.cxx b/src/Standard/Standard_OutOfMemory.cxx index c651424b6a..cfb4b0e077 100644 --- a/src/Standard/Standard_OutOfMemory.cxx +++ b/src/Standard/Standard_OutOfMemory.cxx @@ -89,12 +89,20 @@ void Standard_OutOfMemory::Raise(Standard_SStream& theMessage) // global instance must be allocated at load-time static Handle(Standard_OutOfMemory) anOutOfMemInstance = new Standard_OutOfMemory; -Handle(Standard_OutOfMemory) Standard_OutOfMemory::NewInstance(const Standard_CString theMessage) +Handle(Standard_OutOfMemory) Standard_OutOfMemory::NewInstance (Standard_CString theMessage) { anOutOfMemInstance->SetMessageString (theMessage); return anOutOfMemInstance; } +Handle(Standard_OutOfMemory) Standard_OutOfMemory::NewInstance (Standard_CString theMessage, + Standard_CString theStackTrace) +{ + anOutOfMemInstance->SetMessageString (theMessage); + anOutOfMemInstance->SetStackString (theStackTrace); + return anOutOfMemInstance; +} + //======================================================================= //function : Throw //purpose : diff --git a/src/Standard/Standard_OutOfMemory.hxx b/src/Standard/Standard_OutOfMemory.hxx index c75acbe401..ad81c2674b 100644 --- a/src/Standard/Standard_OutOfMemory.hxx +++ b/src/Standard/Standard_OutOfMemory.hxx @@ -68,7 +68,11 @@ public: Standard_EXPORT static void Raise(Standard_SStream& theMessage); //! Returns global instance of exception - Standard_EXPORT static Handle(Standard_OutOfMemory) NewInstance(const Standard_CString theMessage = ""); + Standard_EXPORT static Handle(Standard_OutOfMemory) NewInstance (Standard_CString theMessage = ""); + + //! Returns global instance of exception + Standard_EXPORT static Handle(Standard_OutOfMemory) NewInstance (Standard_CString theMessage, + Standard_CString theStackTrace); DEFINE_STANDARD_RTTIEXT(Standard_OutOfMemory,Standard_ProgramError) diff --git a/src/Standard/Standard_StackTrace.cxx b/src/Standard/Standard_StackTrace.cxx new file mode 100644 index 0000000000..81e2063680 --- /dev/null +++ b/src/Standard/Standard_StackTrace.cxx @@ -0,0 +1,362 @@ +// Created on: 2020-11-30 +// Copyright (c) 2020 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include + +#if defined(__APPLE__) + #import +#endif + +#if defined(__EMSCRIPTEN__) + #include +#elif defined(__ANDROID__) + //#include +#elif defined(__QNX__) + //#include // requires linking to libbacktrace +#elif !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) + #include +#elif defined(_WIN32) && !defined(OCCT_UWP) + +#include + #include +#include + +//! This is a wrapper of DbgHelp library loaded dynamically. +//! DbgHelp is coming with Windows SDK, so that technically it is always available. +//! However, it's usage requires extra steps: +//! - .pdb files are necessary to resolve function names; +//! Normal release DLLs without PDBs will show no much useful info. +//! - DbgHelp.dll and friends (SymSrv.dll, SrcSrv.dll) should be packaged with application; +//! DbgHelp.dll coming with system might be of other incompatible version +//! (some applications load it dynamically to avoid packaging extra DLL, +//! with a extra hacks determining library version) +class Standard_DbgHelper +{ +public: // dbgHelp.dll function types + + typedef BOOL (WINAPI *SYMINITIALIZEPROC) (HANDLE, PCSTR, BOOL); + typedef BOOL (WINAPI *STACKWALK64PROC) (DWORD, HANDLE, HANDLE, LPSTACKFRAME64, + PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, + PFUNCTION_TABLE_ACCESS_ROUTINE64, + PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64); + typedef BOOL (WINAPI *SYMCLEANUPPROC) (HANDLE); + typedef BOOL (WINAPI *SYMFROMADDRPROC) (HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO); + +public: + + //! Return global instance. + static Standard_DbgHelper& GetDbgHelper() + { + static Standard_DbgHelper aDbgHelper; + return aDbgHelper; + } + + //! Return global mutex. + static Standard_Mutex& Mutex() + { + static Standard_Mutex THE_MUTEX_LOCK; + return THE_MUTEX_LOCK; + } + +public: + + SYMINITIALIZEPROC SymInitialize; + SYMCLEANUPPROC SymCleanup; + STACKWALK64PROC StackWalk64; + PFUNCTION_TABLE_ACCESS_ROUTINE64 SymFunctionTableAccess64; + PGET_MODULE_BASE_ROUTINE64 SymGetModuleBase64; + SYMFROMADDRPROC SymFromAddr; + + //! Return TRUE if library has been loaded. + bool IsLoaded() const { return myDbgHelpLib != NULL; } + + //! Return loading error message. + const char* ErrorMessage() const { return myError; } + +private: + + //! Main constructor. + Standard_DbgHelper() + : SymInitialize (NULL), + SymCleanup (NULL), + StackWalk64 (NULL), + SymFunctionTableAccess64 (NULL), + SymGetModuleBase64 (NULL), + SymFromAddr (NULL), + myDbgHelpLib (LoadLibraryW (L"DbgHelp.dll")), + myError (NULL) + { + if (myDbgHelpLib == NULL) + { + myError = "Standard_DbgHelper, Failed to load DbgHelp.dll"; + return; + } + + if ((SymInitialize = (SYMINITIALIZEPROC) GetProcAddress (myDbgHelpLib, "SymInitialize")) == NULL) + { + myError = "Standard_DbgHelper, Function not found in DbgHelp.dll: SymInitialize"; + unload(); + return; + } + if ((SymCleanup = (SYMCLEANUPPROC) GetProcAddress (myDbgHelpLib, "SymCleanup")) == NULL) + { + myError = "Standard_DbgHelper, Function not found in DbgHelp.dll: SymCleanup"; + unload(); + return; + } + if ((StackWalk64 = (STACKWALK64PROC) GetProcAddress (myDbgHelpLib, "StackWalk64")) == NULL) + { + myError = "Standard_DbgHelper, Function not found in DbgHelp.dll: StackWalk64"; + unload(); + return; + } + if ((SymFunctionTableAccess64 = (PFUNCTION_TABLE_ACCESS_ROUTINE64) GetProcAddress (myDbgHelpLib, "SymFunctionTableAccess64")) == NULL) + { + myError = "Standard_DbgHelper, Function not found in DbgHelp.dll: SymFunctionTableAccess64"; + unload(); + return; + } + if ((SymGetModuleBase64 = (PGET_MODULE_BASE_ROUTINE64) GetProcAddress (myDbgHelpLib, "SymGetModuleBase64")) == NULL) + { + myError = "Standard_DbgHelper, Function not found in DbgHelp.dll: SymGetModuleBase64"; + unload(); + return; + } + if ((SymFromAddr = (SYMFROMADDRPROC) GetProcAddress (myDbgHelpLib, "SymFromAddr")) == NULL) + { + myError = "Standard_DbgHelper, Function not found in DbgHelp.dll: SymFromAddr"; + unload(); + return; + } + } + + //! Destructor. + ~Standard_DbgHelper() + { + // we could unload library here, but don't do it as it is kept loaded + //unload(); + } + + //! Unload library. + void unload() + { + if (myDbgHelpLib != NULL) + { + FreeLibrary (myDbgHelpLib); + myDbgHelpLib = NULL; + } + } + +private: + + Standard_DbgHelper (const Standard_DbgHelper& ); + Standard_DbgHelper& operator= (const Standard_DbgHelper& ); + +private: + + HMODULE myDbgHelpLib; //!< handle to DbgHelp + const char* myError; //!< loading error message + +}; + +#endif + +//======================================================================= +//function : StackTrace +//purpose : +//======================================================================= +Standard_Boolean Standard::StackTrace (char* theBuffer, + const int theBufferSize, + const int theNbTraces = 10, + void* theContext, + const int theNbTopSkip) +{ + (void )theContext; + if (theBufferSize < 1 + || theNbTraces < 1 + || theBuffer == NULL + || theNbTopSkip < 0) + { + return false; + } + +#if defined(__EMSCRIPTEN__) + // theNbTraces is ignored + // EM_LOG_JS_STACK? + return emscripten_get_callstack (EM_LOG_C_STACK | EM_LOG_DEMANGLE | EM_LOG_NO_PATHS | EM_LOG_FUNC_PARAMS, theBuffer, theBufferSize) > 0; +#elif defined(__ANDROID__) + Message::SendTrace ("Standard::StackTrace() is not implemented for this platform"); + return false; +#elif defined(__QNX__) + // bt_get_backtrace() + Message::SendTrace ("Standard::StackTrace() is not implemented for this platform"); + return false; +#elif defined(OCCT_UWP) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) + Message::SendTrace ("Standard::StackTrace() is not implemented for this platform"); + return false; +#elif defined(_WIN32) + // Each CPU architecture requires manual stack frame setup, + // and 32-bit version requires also additional hacks to retrieve current context; + // this implementation currently covers only x86_64 architecture. +#if defined(_M_X64) + int aNbTraces = theNbTraces; + const HANDLE anHProcess = GetCurrentProcess(); + const HANDLE anHThread = GetCurrentThread(); + CONTEXT aCtx; + if (theContext != NULL) + { + memcpy (&aCtx, theContext, sizeof(aCtx)); + } + else + { + ++aNbTraces; + memset (&aCtx, 0, sizeof(aCtx)); + aCtx.ContextFlags = CONTEXT_FULL; + RtlCaptureContext (&aCtx); + } + + // DbgHelp is not thread-safe library, hence global lock is used for serial access + Standard_Mutex::Sentry aSentry (Standard_DbgHelper::Mutex()); + Standard_DbgHelper& aDbgHelp = Standard_DbgHelper::GetDbgHelper(); + if (!aDbgHelp.IsLoaded()) + { + strcat_s (theBuffer, theBufferSize, "\n==Backtrace==\n"); + strcat_s (theBuffer, theBufferSize, aDbgHelp.ErrorMessage()); + strcat_s (theBuffer, theBufferSize, "\n============="); + return false; + } + + aDbgHelp.SymInitialize (anHProcess, NULL, TRUE); + + DWORD anImage = 0; + STACKFRAME64 aStackFrame; + memset (&aStackFrame, 0, sizeof(aStackFrame)); + + anImage = IMAGE_FILE_MACHINE_AMD64; + aStackFrame.AddrPC.Offset = aCtx.Rip; + aStackFrame.AddrPC.Mode = AddrModeFlat; + aStackFrame.AddrFrame.Offset = aCtx.Rsp; + aStackFrame.AddrFrame.Mode = AddrModeFlat; + aStackFrame.AddrStack.Offset = aCtx.Rsp; + aStackFrame.AddrStack.Mode = AddrModeFlat; + + char aModBuffer[MAX_PATH] = {}; + char aSymBuffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)]; + SYMBOL_INFO* aSymbol = (SYMBOL_INFO*) aSymBuffer; + aSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); + aSymbol->MaxNameLen = MAX_SYM_NAME; + + int aTopSkip = theNbTopSkip + 1; // skip this function call and specified extra number + strcat_s (theBuffer, theBufferSize, "\n==Backtrace=="); + for (int aLineIter = 0; aLineIter < aNbTraces; ++aLineIter) + { + BOOL aRes = aDbgHelp.StackWalk64 (anImage, anHProcess, anHThread, + &aStackFrame, &aCtx, NULL, + aDbgHelp.SymFunctionTableAccess64, aDbgHelp.SymGetModuleBase64, NULL); + if (!aRes) + { + break; + } + + if (theContext == NULL && aTopSkip > 0) + { + --aTopSkip; + continue; + } + if (aStackFrame.AddrPC.Offset == 0) + { + break; + } + + strcat_s (theBuffer, theBufferSize, "\n"); + + const DWORD64 aModuleBase = aDbgHelp.SymGetModuleBase64 (anHProcess, aStackFrame.AddrPC.Offset); + if (aModuleBase != 0 + && GetModuleFileNameA ((HINSTANCE) aModuleBase, aModBuffer, MAX_PATH)) + { + strcat_s (theBuffer, theBufferSize, aModBuffer); + } + + DWORD64 aDisp = 0; + strcat_s (theBuffer, theBufferSize, "("); + if (aDbgHelp.SymFromAddr (anHProcess, aStackFrame.AddrPC.Offset, &aDisp, aSymbol)) + { + strcat_s (theBuffer, theBufferSize, aSymbol->Name); + } + else + { + strcat_s (theBuffer, theBufferSize, "???"); + } + strcat_s (theBuffer, theBufferSize, ")"); + } + strcat_s (theBuffer, theBufferSize, "\n============="); + + aDbgHelp.SymCleanup (anHProcess); + return true; +#else + Message::SendTrace ("Standard::StackTrace() is not implemented for this CPU architecture"); + return false; +#endif +#else + const int aTopSkip = theNbTopSkip + 1; // skip this function call and specified extra number + int aNbTraces = theNbTraces + aTopSkip; + void** aStackArr = (void** )alloca (sizeof(void*) * aNbTraces); + if (aStackArr == NULL) + { + return false; + } + + aNbTraces = ::backtrace (aStackArr, aNbTraces); + if (aNbTraces <= 1) + { + return false; + } + + aNbTraces -= aTopSkip; + char** aStrings = ::backtrace_symbols (aStackArr + aTopSkip, aNbTraces); + if (aStrings == NULL) + { + return false; + } + + const size_t aLenInit = strlen (theBuffer); + size_t aLimit = (size_t) theBufferSize - aLenInit - 1; + if (aLimit > 14) + { + strcat (theBuffer, "\n==Backtrace=="); + aLimit -= 14; + } + for (int aLineIter = 0; aLineIter < aNbTraces; ++aLineIter) + { + const size_t aLen = strlen (aStrings[aLineIter]); + if (aLen + 1 >= aLimit) + { + break; + } + + strcat (theBuffer, "\n"); + strcat (theBuffer, aStrings[aLineIter]); + aLimit -= aLen + 1; + } + free (aStrings); + if (aLimit > 14) + { + strcat (theBuffer, "\n============="); + } + return true; +#endif +} diff --git a/tests/bugs/fclasses/bug30762 b/tests/bugs/fclasses/bug30762 new file mode 100644 index 0000000000..4948ec960f --- /dev/null +++ b/tests/bugs/fclasses/bug30762 @@ -0,0 +1,22 @@ +puts "================" +puts "0030762: Foundation Classes - include backtrace within OSD_SIGSEGV on Linux" +puts "================" +puts "" + +pload QAcommands + +dsetsignal set -strackTraceLength 10 +set IsDone [catch {set aResult [OCC30762]} result] + +if { ${IsDone} != 0 } { + puts "result = ${result}" + puts "Error: command raised exception" +} else { + if { [string first "testMethod3" $aResult] != -1 } { + puts "OK test case" + } else { + # stack trace might be missing due to stripped symbols or optimized code + #puts "Error: backtrace is not printed" + puts "Warning: backtrace is not printed" + } +}