Crash: use SA_SIGINFO on Linux for enhanced crash diagnostics

Switch signal handler registration from signal() to sigaction(SA_SIGINFO)
on Linux to capture fault address, signal code description, and program
counter at crash time. These are logged to the file logger and included
in OpenTelemetry crash reports.
This commit is contained in:
Magne Sjaastad
2026-03-23 10:30:14 +01:00
parent 20052771ad
commit 8df24c5bd3
4 changed files with 176 additions and 14 deletions
+151 -12
View File
@@ -32,8 +32,15 @@
#include <QDir>
#include <map>
#include <sstream>
#include <stacktrace>
#ifndef WIN32
#include <signal.h>
#include <ucontext.h>
#endif
namespace internal
{
// Custom formatter for stacktrace
@@ -48,17 +55,96 @@ std::string formatStacktrace( const std::stacktrace& st )
}
return ss.str();
}
#ifndef WIN32
static std::string signalCodeDescription( int signo, int siCode )
{
switch ( signo )
{
case SIGSEGV:
switch ( siCode )
{
case SEGV_MAPERR:
return "address not mapped (SEGV_MAPERR)";
case SEGV_ACCERR:
return "invalid access permissions (SEGV_ACCERR)";
default:
return "unknown SIGSEGV code";
}
case SIGFPE:
switch ( siCode )
{
case FPE_INTDIV:
return "integer divide by zero (FPE_INTDIV)";
case FPE_INTOVF:
return "integer overflow (FPE_INTOVF)";
case FPE_FLTDIV:
return "floating-point divide by zero (FPE_FLTDIV)";
case FPE_FLTOVF:
return "floating-point overflow (FPE_FLTOVF)";
case FPE_FLTUND:
return "floating-point underflow (FPE_FLTUND)";
case FPE_FLTRES:
return "floating-point inexact result (FPE_FLTRES)";
case FPE_FLTINV:
return "invalid floating-point operation (FPE_FLTINV)";
case FPE_FLTSUB:
return "subscript out of range (FPE_FLTSUB)";
default:
return "unknown SIGFPE code";
}
case SIGILL:
switch ( siCode )
{
case ILL_ILLOPC:
return "illegal opcode (ILL_ILLOPC)";
case ILL_ILLOPN:
return "illegal operand (ILL_ILLOPN)";
case ILL_ILLADR:
return "illegal addressing mode (ILL_ILLADR)";
case ILL_ILLTRP:
return "illegal trap (ILL_ILLTRP)";
case ILL_PRVOPC:
return "privileged opcode (ILL_PRVOPC)";
case ILL_PRVREG:
return "privileged register (ILL_PRVREG)";
case ILL_COPROC:
return "coprocessor error (ILL_COPROC)";
case ILL_BADSTK:
return "internal stack error (ILL_BADSTK)";
default:
return "unknown SIGILL code";
}
case SIGBUS:
switch ( siCode )
{
case BUS_ADRALN:
return "invalid address alignment (BUS_ADRALN)";
case BUS_ADRERR:
return "nonexistent physical address (BUS_ADRERR)";
case BUS_OBJERR:
return "object-specific hardware error (BUS_OBJERR)";
default:
return "unknown SIGBUS code";
}
default:
return "";
}
}
#endif
} // namespace internal
//--------------------------------------------------------------------------------------------------
/// Shared crash logging: writes to file logger and OpenTelemetry.
/// extraAttrs may include platform-specific context like fault address and signal code description.
///
/// Note: Executing logging functions from a signal handler is not async-signal-safe, but works as
/// expected on Windows. Behavior on Linux is undefined, but will work in most cases.
/// https://github.com/gabime/spdlog/issues/1607
//--------------------------------------------------------------------------------------------------
void manageSegFailure( int signalCode )
static void performCrashLogging( int signalCode, const std::map<std::string, std::string>& extraAttrs )
{
// Executing function here is not safe, but works as expected on Windows. Behavior on Linux is undefined, but will
// work in some cases.
// https://github.com/gabime/spdlog/issues/1607
auto st = std::stacktrace::current();
auto loggers = RiaLogging::loggerInstances();
for ( auto logger : loggers )
@@ -66,12 +152,15 @@ void manageSegFailure( int signalCode )
if ( auto fileLogger = dynamic_cast<RiaFileLogger*>( logger ) )
{
auto versionText = QString( STRPRODUCTVER );
auto str =
QString( "Segmentation fault (signal code: %1) - ResInsight version %2" ).arg( signalCode ).arg( versionText );
auto str = QString( "Crash (signal: %1) - ResInsight version %2" ).arg( signalCode ).arg( versionText );
fileLogger->error( str.toStdString().data() );
auto st = std::stacktrace::current();
for ( const auto& [key, value] : extraAttrs )
{
std::string line = key + ": " + value;
fileLogger->error( line.data() );
}
std::string message = "Stack trace:\n" + internal::formatStacktrace( st );
logger->error( message.data() );
@@ -79,17 +168,67 @@ void manageSegFailure( int signalCode )
}
}
// Report crash to OpenTelemetry if enabled and initialized
auto& otelManager = RiaOpenTelemetryManager::instance();
if ( otelManager.isEnabled() )
{
auto st = std::stacktrace::current();
otelManager.reportCrash( signalCode, st );
otelManager.reportCrash( signalCode, st, extraAttrs );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void manageSegFailure( int signalCode )
{
performCrashLogging( signalCode, {} );
exit( 1 );
}
#ifndef WIN32
//--------------------------------------------------------------------------------------------------
/// SA_SIGINFO-compatible handler: captures fault address, signal code, and program counter
/// for enhanced crash diagnostics on Linux.
//--------------------------------------------------------------------------------------------------
void manageSegFailureSA( int signalCode, siginfo_t* info, void* ucontext )
{
std::map<std::string, std::string> extraAttrs;
if ( info )
{
std::ostringstream addrStr;
addrStr << "0x" << std::hex << reinterpret_cast<uintptr_t>( info->si_addr );
extraAttrs["crash.fault_address"] = addrStr.str();
std::string codeDesc = internal::signalCodeDescription( signalCode, info->si_code );
if ( !codeDesc.empty() )
{
extraAttrs["crash.signal_code"] = codeDesc;
}
}
#if defined( __x86_64__ )
if ( ucontext )
{
auto* ctx = static_cast<ucontext_t*>( ucontext );
std::ostringstream pcStr;
pcStr << "0x" << std::hex << static_cast<uintptr_t>( ctx->uc_mcontext.gregs[REG_RIP] );
extraAttrs["crash.program_counter"] = pcStr.str();
}
#elif defined( __aarch64__ )
if ( ucontext )
{
auto* ctx = static_cast<ucontext_t*>( ucontext );
std::ostringstream pcStr;
pcStr << "0x" << std::hex << ctx->uc_mcontext.pc;
extraAttrs["crash.program_counter"] = pcStr.str();
}
#endif
performCrashLogging( signalCode, extraAttrs );
exit( 1 );
}
#endif
namespace RiaMainTools
{
//--------------------------------------------------------------------------------------------------