]> OCCT Git - occt.git/commitdiff
0030762: Foundation Classes - include backtrace within OSD_SIGSEGV
authorkgv <kgv@opencascade.com>
Mon, 3 Jun 2019 05:06:24 +0000 (08:06 +0300)
committerbugmaster <bugmaster@opencascade.com>
Thu, 17 Dec 2020 18:17:43 +0000 (21:17 +0300)
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).

16 files changed:
src/Draw/Draw.cxx
src/Draw/Draw_BasicCommands.cxx
src/OSD/OSD.hxx
src/OSD/OSD_ThreadPool.cxx
src/OSD/OSD_signal.cxx
src/QABugs/QABugs_11.cxx
src/Standard/FILES
src/Standard/Standard.hxx
src/Standard/Standard_DefineException.hxx
src/Standard/Standard_Failure.cxx
src/Standard/Standard_Failure.hxx
src/Standard/Standard_Macro.hxx
src/Standard/Standard_OutOfMemory.cxx
src/Standard/Standard_OutOfMemory.hxx
src/Standard/Standard_StackTrace.cxx [new file with mode: 0644]
tests/bugs/fclasses/bug30762 [new file with mode: 0644]

index 3a77a71c6a94f4ee99d5e47d8c4cff0dbb50a4a0..3b1a1650727398d300c80bdea3061a4fc122d561 100644 (file)
@@ -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
index 4a0bb7b49bf165c86f3ac7d2f5fb3f4190418bc8..7d6b37e66ab88c0605e2793df4c9d2cd4d30d018 100644 (file)
@@ -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);
index 78cfb38d5abbe99e359c3f692363e6ff6cee5fbb..7ce019500c53065afd3f823c775bb6e18eac2b67 100644 (file)
@@ -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
index 0d3bc2bb0e9ce970a3c05f762c82ea42fa1a5bec..04a65bba563e0cf36866bad34c4ec221f00c39f1 100644 (file)
@@ -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);
   }
 }
 
index 9b5225fb1fa78c5934d0539c282417f57fcc7c39..5c4d7b262947ef117100e29a79663f2a8b866ff3 100644 (file)
@@ -21,6 +21,7 @@
 #include <Standard_WarningDisableFunctionCast.hxx>
 
 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)
 {
-  Standard_Mutex::Sentry aSentry (THE_SIGNAL_MUTEX); // lock the mutex to prevent simultaneous handling
-
-  static wchar_t         buffer[2048];
-
-  int                  flterr = 0;
+  ptrdiff_t ExceptionInformation1 = 0, ExceptionInformation0 = 0;
+  if (theExcPtr != NULL)
+  {
+    ExceptionInformation1 = theExcPtr->ExceptionRecord->ExceptionInformation[1];
+    ExceptionInformation0 = theExcPtr->ExceptionRecord->ExceptionInformation[0];
+  }
 
-  buffer[0] = '\0' ;
+  Standard_Mutex::Sentry aSentry (THE_SIGNAL_MUTEX); // lock the mutex to prevent simultaneous handling
+  static char aBuffer[2048];
 
-// 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);
+
+      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);
+      }
 
-//  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();
-     }
+      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
index c8673911fa30522334d8e321ef4ac28c420d6a17..3bf0fe0cafc0343b42be20474543fea90af6954f 100644 (file)
@@ -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);
index dc0f0c9eebad51fbaa2eea44ac46d1e13e92e2dc..138b85b0fcb0fbf780786f99ccf3fb42b4e6516b 100755 (executable)
@@ -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
index 768abb964f848281437647c7394f73503b52e5f2..3b0b1aea6729a64aa53ec68e07ab3bf97deec5bd 100644 (file)
@@ -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
index cea1a19e0b151ac914a638add6f4b87fa1077929..5ca3ea2e9bda34c75d6c4a62285b9294f4d924ea 100644 (file)
@@ -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) \
 };
 
index b0b15409984c8e5440e7bfb54e5931d4e0cda155..cb88d8d2e14991a5cd59b8735401047aaee2b353 100644 (file)
@@ -12,9 +12,9 @@
 // Alternatively, this file may be used under the terms of Open CASCADE
 // commercial license or contractual agreement.
 
+#include <Standard_Failure.hxx>
 
 #include <Standard_ErrorHandler.hxx>
-#include <Standard_Failure.hxx>
 #include <Standard_Macro.hxx>
 #include <Standard_NoSuchObject.hxx>
 #include <Standard_PCharacter.hxx>
 #include <Standard_TypeMismatch.hxx>
 
 #include <string.h>
+
 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;
   }
-  return aStr;
+
+  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;
 }
 
-static Standard_CString copy_message(Standard_CString aMessage)
+// =======================================================================
+// function : StringRef::copy_message
+// purpose  :
+// =======================================================================
+Standard_Failure::StringRef* Standard_Failure::StringRef::copy_message (Standard_Failure::StringRef* theString)
 {
-  Standard_CString aStr = 0;
-  if(aMessage) {
-    aStr = aMessage;
-    (*((Standard_Integer*)aStr))++;
+  if (theString == NULL)
+  {
+    return NULL;
   }
-  return aStr;
+
+  ++theString->Counter;
+  return theString;
 }
 
-static void deallocate_message(Standard_CString aMessage)
+// =======================================================================
+// function : StringRef::deallocate_message
+// purpose  :
+// =======================================================================
+void Standard_Failure::StringRef::deallocate_message (Standard_Failure::StringRef* theString)
 {
-  if(aMessage) {
-    (*((Standard_Integer*)aMessage))--;
-    if(*((Standard_Integer*)aMessage)==0)
-      free((void*)aMessage);
+  if (theString != NULL)
+  {
+    if (--theString->Counter == 0)
+    {
+      free ((void* )theString);
+    }
   }
 }
 
-// ******************************************************************
-//                           Standard_Failure                       *
-// ******************************************************************
-static Standard_THREADLOCAL Handle(Standard_Failure) RaisedError;
+// =======================================================================
+// function : Standard_Failure
+// purpose  :
+// =======================================================================
+Standard_Failure::Standard_Failure()
+: myMessage (NULL),
+  myStackTrace (NULL)
+{
+  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::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 != NULL)
+  {
+    theStream << DynamicType() << ": " << GetMessageString();
+  }
+  else
+  {
+    theStream << DynamicType();
+  }
+  if (myStackTrace != NULL)
+  {
+    theStream << GetStackString();
+  }
+}
+
+// =======================================================================
+// function : NewInstance
+// purpose  :
+// =======================================================================
+Handle(Standard_Failure) Standard_Failure::NewInstance (Standard_CString theString)
 {
-if(myMessage){ 
-    AStream << DynamicType() << ": " << GetMessageString(); 
- } 
- else { 
-    AStream << DynamicType();
- }
+  return new Standard_Failure (theString);
 }
 
-Handle(Standard_Failure) Standard_Failure::NewInstance(const Standard_CString AString)
+// =======================================================================
+// function : NewInstance
+// purpose  :
+// =======================================================================
+Handle(Standard_Failure) Standard_Failure::NewInstance (Standard_CString theMessage,
+                                                        Standard_CString theStackTrace)
 {
-  return new Standard_Failure(AString)  ;
+  return new Standard_Failure (theMessage, theStackTrace);
 }
 
-//=======================================================================
-//function : GetMessageString
-//purpose  : Returns error message
-//=======================================================================
-Standard_CString Standard_Failure::GetMessageString () const
+// =======================================================================
+// function : GetNbStackTraces
+// purpose  :
+// =======================================================================
+Standard_Integer Standard_Failure::DefaultStackTraceLength()
 {
-  return (myMessage ? myMessage+sizeof(Standard_Integer) : "");
+  return Standard_Failure_DefaultStackTraceLength;
 }
 
+// =======================================================================
+// function : SetNbStackTraces
+// purpose  :
+// =======================================================================
+void Standard_Failure::SetDefaultStackTraceLength (Standard_Integer theNbStackTraces)
+{
+  Standard_Failure_DefaultStackTraceLength = theNbStackTraces;
+}
index 37e8304cf8a67f5c13f4920ac9c797c283a4ff8d..ee443a0f94f19fa3348a6ee9d55ed4b9caadd790 100644 (file)
 #ifndef _Standard_Failure_HeaderFile
 #define _Standard_Failure_HeaderFile
 
-#include <Standard.hxx>
 #include <Standard_Type.hxx>
 
 #include <Standard_CString.hxx>
 #include <Standard_Transient.hxx>
 #include <Standard_OStream.hxx>
 #include <Standard_SStream.hxx>
-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];
 
-private:
+    //! Return message string.
+    Standard_CString GetMessage() const { return (Standard_CString )&Message[0]; }
 
+    //! Allocate reference-counted message string.
+    static StringRef* allocate_message (Standard_CString theString);
 
-  Standard_CString myMessage;
+    //! Copy reference-counted message string.
+    static StringRef* copy_message (StringRef* theString);
+
+    //! Release reference-counted message string.
+    static void deallocate_message (StringRef* theString);
+  };
+
+private:
 
+  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
index 430c03bd82d7b3dc6cdf2626e2a3efaec1dd498d..b510367c1e4549966fce04857558d899a817bc09 100644 (file)
   #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__)
index c651424b6af0b147ccd9b4a2c9bb12da6afb3025..cfb4b0e077becd778879066ed2996f5fd85df255 100644 (file)
@@ -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  :
index c75acbe40156b4d5ff903b5b78836a269a0711d2..ad81c2674b15d0686c885f8752a988c34e50d93e 100644 (file)
@@ -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 (file)
index 0000000..81e2063
--- /dev/null
@@ -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 <Standard.hxx>
+
+#include <Message.hxx>
+#include <Standard_Mutex.hxx>
+
+#if defined(__APPLE__)
+  #import <TargetConditionals.h>
+#endif
+
+#if defined(__EMSCRIPTEN__)
+  #include <emscripten/emscripten.h>
+#elif defined(__ANDROID__)
+  //#include <unwind.h>
+#elif defined(__QNX__)
+  //#include <backtrace.h> // requires linking to libbacktrace
+#elif !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
+  #include <execinfo.h>
+#elif defined(_WIN32) && !defined(OCCT_UWP)
+
+#include <Standard_WarningsDisable.hxx>
+  #include <dbghelp.h>
+#include <Standard_WarningsRestore.hxx>
+
+//! 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 (file)
index 0000000..4948ec9
--- /dev/null
@@ -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"
+  }
+}