mirror of
https://github.com/OPM/ResInsight.git
synced 2026-08-27 05:37:21 -05:00
#14270 Capture uncaught exception throw site in crash reports
Add a std::set_terminate handler (manageTerminate) that runs before the stack unwinds when a C++ exception escapes the Qt event loop, so the captured stack trace points at the original throw site instead of the crash-logging plumbing reached via the SIGABRT signal handler. The handler also recovers the exception type and message via std::current_exception and reports them as crash attributes. Remove the catch-and-rethrow around the event loop in RiaMain: that handler unwound the stack to main before anything captured it, which defeated the new terminate handler. The SIGABRT signal handler remains as a fallback for direct abort()/assert() failures that do not go through std::terminate.
This commit is contained in:
committed by
Magne Sjaastad
parent
6e6f29b86d
commit
42d8a4b14a
@@ -47,7 +47,10 @@
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <exception>
|
||||
|
||||
void manageSegFailure( int signalCode );
|
||||
void manageTerminate();
|
||||
#ifndef WIN32
|
||||
void manageSegFailureSA( int signalCode, siginfo_t* info, void* ucontext );
|
||||
#endif
|
||||
@@ -193,6 +196,11 @@ int main( int argc, char* argv[] )
|
||||
signal( SIGABRT, manageSegFailure );
|
||||
#endif
|
||||
|
||||
// Capture uncaught C++ exceptions at the throw site (before the stack unwinds) with full type,
|
||||
// message and stack trace. The SIGABRT signal handler above remains a fallback for direct
|
||||
// abort()/assert() failures that do not go through std::terminate.
|
||||
std::set_terminate( manageTerminate );
|
||||
|
||||
// Handle the command line arguments.
|
||||
// Todo: Move to a one-shot timer, delaying the execution until we are inside the event loop.
|
||||
// The complete handling of the resulting ApplicationStatus must be moved along.
|
||||
@@ -221,7 +229,8 @@ int main( int argc, char* argv[] )
|
||||
else if ( status == RiaApplication::ApplicationStatus::KEEP_GOING )
|
||||
{
|
||||
int exitCode = 0;
|
||||
try
|
||||
// No try/catch around the event loop: an uncaught exception must reach std::set_terminate
|
||||
// (manageTerminate) with the stack still intact so the crash report captures the throw site.
|
||||
{
|
||||
#ifdef ENABLE_GRPC
|
||||
auto grpcInterface = dynamic_cast<RiaGrpcApplicationInterface*>( app.get() );
|
||||
@@ -257,17 +266,6 @@ int main( int argc, char* argv[] )
|
||||
|
||||
exitCode = QCoreApplication::instance()->exec();
|
||||
}
|
||||
catch ( std::exception& exep )
|
||||
{
|
||||
std::cout << "A standard c++ exception that terminated ResInsight caught in RiaMain.cpp: " << exep.what()
|
||||
<< std::endl;
|
||||
throw;
|
||||
}
|
||||
catch ( ... )
|
||||
{
|
||||
std::cout << "An unknown exception that terminated ResInsight caught in RiaMain.cpp. " << std::endl;
|
||||
throw;
|
||||
}
|
||||
|
||||
app.reset();
|
||||
RiaMainTools::releaseSingletonAndFactoryObjects();
|
||||
|
||||
@@ -32,8 +32,11 @@
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#include <csignal>
|
||||
#include <exception>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <typeinfo>
|
||||
#include <version>
|
||||
|
||||
// std::stacktrace is C++23; libc++ in Homebrew llvm@19 (and older) does not
|
||||
@@ -202,6 +205,42 @@ static void performCrashLogging( int signalCode, const std::map<std::string, std
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Terminate handler for uncaught C++ exceptions. Unlike the signal handler, this runs BEFORE the
|
||||
/// stack is unwound: when no handler is found, libstdc++ calls std::terminate from the throw site,
|
||||
/// so the stack trace captured here points at where the exception was originally thrown rather than
|
||||
/// at the crash-logging plumbing. Also recovers the exception type and message, which a bare SIGABRT
|
||||
/// trace cannot provide. Runs outside any signal handler, so logging here is safe (no async-signal
|
||||
/// constraints, unlike performCrashLogging when reached via the signal path).
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void manageTerminate()
|
||||
{
|
||||
std::map<std::string, std::string> extraAttrs;
|
||||
|
||||
if ( auto ex = std::current_exception() )
|
||||
{
|
||||
try
|
||||
{
|
||||
std::rethrow_exception( ex );
|
||||
}
|
||||
catch ( const std::exception& e )
|
||||
{
|
||||
extraAttrs["crash.exception_type"] = typeid( e ).name();
|
||||
extraAttrs["crash.exception_what"] = e.what();
|
||||
}
|
||||
catch ( ... )
|
||||
{
|
||||
extraAttrs["crash.exception_type"] = "non-std exception";
|
||||
}
|
||||
}
|
||||
|
||||
// performCrashLogging captures std::stacktrace::current() itself; called here (no signal, no
|
||||
// prior unwind) that trace still includes the original throw site.
|
||||
performCrashLogging( SIGABRT, extraAttrs );
|
||||
|
||||
std::abort(); // preserve default terminate behavior (core dump, exit status)
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user