#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:
Kristian Bendiksen
2026-06-22 14:18:05 +02:00
committed by Magne Sjaastad
parent 6e6f29b86d
commit 42d8a4b14a
2 changed files with 49 additions and 12 deletions
+10 -12
View File
@@ -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();
+39
View File
@@ -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)
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------