mirror of
https://github.com/OPM/ResInsight.git
synced 2026-08-27 05:37:21 -05:00
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.
279 lines
10 KiB
C++
279 lines
10 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2011-2012 Statoil ASA, Ceetron AS
|
|
//
|
|
// ResInsight is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
// FITNESS FOR A PARTICULAR PURPOSE.
|
|
//
|
|
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
|
// for more details.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "RiaApplication.h"
|
|
#include "RiaArgumentParser.h"
|
|
#include "RiaMainTools.h"
|
|
#include "RiaOpenTelemetryManager.h"
|
|
#include "RiaPreferences.h"
|
|
#include "RiaQuantityInfoTools.h"
|
|
#include "RiaStdStringTools.h"
|
|
|
|
#ifdef ENABLE_GRPC
|
|
#include "RiaGrpcConsoleApplication.h"
|
|
#include "RiaGrpcGuiApplication.h"
|
|
#else
|
|
#include "RiaConsoleApplication.h"
|
|
#include "RiaGuiApplication.h"
|
|
#endif
|
|
|
|
#include "cafUiAppearanceSettings.h"
|
|
|
|
#include "cvfProgramOptions.h"
|
|
#include "cvfqtUtils.h"
|
|
|
|
#include <QFile>
|
|
#include <QtGlobal>
|
|
|
|
#ifndef WIN32
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <signal.h>
|
|
|
|
#include <exception>
|
|
|
|
void manageSegFailure( int signalCode );
|
|
void manageTerminate();
|
|
#ifndef WIN32
|
|
void manageSegFailureSA( int signalCode, siginfo_t* info, void* ucontext );
|
|
#endif
|
|
|
|
namespace
|
|
{
|
|
QtMessageHandler g_previousMessageHandler = nullptr;
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Filter out a harmless Qt6 warning seen on some Linux platforms (e.g. RHEL8) where Qt validates an
|
|
/// empty (all-zero) QColorSpacePrimaries and prints:
|
|
/// "QColorSpace attempted constructed from invalid primaries: QPointF(0,0) ..."
|
|
/// The message has no functional impact, so suppress it while forwarding everything else unchanged.
|
|
/// See https://github.com/OPM/ResInsight/issues/11802
|
|
//--------------------------------------------------------------------------------------------------
|
|
void riaFilteredMessageHandler( QtMsgType type, const QMessageLogContext& context, const QString& msg )
|
|
{
|
|
if ( msg.startsWith( QLatin1String( "QColorSpace attempted constructed from invalid primaries" ) ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( g_previousMessageHandler )
|
|
{
|
|
g_previousMessageHandler( type, context, msg );
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
RiaApplication* createApplication( int& argc, char* argv[] )
|
|
{
|
|
for ( int i = 1; i < argc; ++i )
|
|
{
|
|
if ( !qstrcmp( argv[i], "--console" ) || !qstrcmp( argv[i], "--unittest" ) )
|
|
{
|
|
#ifdef ENABLE_GRPC
|
|
return new RiaGrpcConsoleApplication( argc, argv );
|
|
#else
|
|
return new RiaConsoleApplication( argc, argv );
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef ENABLE_GRPC
|
|
return new RiaGrpcGuiApplication( argc, argv );
|
|
#else
|
|
return new RiaGuiApplication( argc, argv );
|
|
#endif
|
|
}
|
|
|
|
int main( int argc, char* argv[] )
|
|
{
|
|
#ifndef WIN32
|
|
// From Qt 5.3 and onwards Qt has a mechanism for checking this automatically
|
|
// But it only checks user id not group id, so better to do it ourselves.
|
|
if ( getuid() != geteuid() || getgid() != getegid() )
|
|
{
|
|
std::cerr << "FATAL: The application binary appears to be running setuid or setgid, this is a security hole."
|
|
<< std::endl;
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
// Install a message handler to filter out a harmless Qt6 color-space warning (see #11802)
|
|
g_previousMessageHandler = qInstallMessageHandler( riaFilteredMessageHandler );
|
|
|
|
RiaMainTools::deleteStaleSettingsLockFiles();
|
|
|
|
// The Qt::AA_ShareOpenGLContexts setting is needed when we have multiple viz widgets in flight
|
|
// and we have a setup where these widgets belong to different top-level windows, or end up
|
|
// belonging to different top-level windows through re-parenting.
|
|
// See test application QtTestBenchOpenGLWidget
|
|
QApplication::setAttribute( Qt::AA_ShareOpenGLContexts );
|
|
|
|
// Create feature manager before the application object is created
|
|
RiaMainTools::initializeSingletons();
|
|
RiaQuantityInfoTools::initializeSummaryKeywords();
|
|
|
|
// https://www.w3.org/wiki/CSS/Properties/color/keywords
|
|
caf::UiAppearanceSettings::instance()->setAutoValueEditorColor( "moccasin" );
|
|
|
|
std::unique_ptr<RiaApplication> app( createApplication( argc, argv ) );
|
|
|
|
cvf::ProgramOptions progOpt;
|
|
bool result = RiaArgumentParser::parseArguments( &progOpt );
|
|
|
|
const cvf::String usageText = progOpt.usageText( 110, 30 );
|
|
app->initialize();
|
|
app->setCommandLineHelpText( cvfqt::Utils::toQString( usageText ) );
|
|
|
|
// Report application startup to OpenTelemetry
|
|
{
|
|
std::map<std::string, std::string> attributes;
|
|
bool isConsoleMode = ( dynamic_cast<RiaGuiApplication*>( app.get() ) == nullptr );
|
|
attributes["app.console_mode"] = RiaStdStringTools::boolToString( isConsoleMode );
|
|
attributes["app.version"] = RiaApplication::getVersionStringApp( false );
|
|
RiaOpenTelemetryManager::instance().reportEventAsync( "app.started", attributes );
|
|
}
|
|
|
|
if ( !result )
|
|
{
|
|
std::vector<cvf::String> unknownOptions = progOpt.unknownOptions();
|
|
QString unknownOptionsText;
|
|
for ( cvf::String option : unknownOptions )
|
|
{
|
|
unknownOptionsText += QString( "\tUnknown option: %1\n" ).arg( cvfqt::Utils::toQString( option ) );
|
|
}
|
|
|
|
app->showFormattedTextInMessageBoxOrConsole(
|
|
"ERROR: Unknown command line options detected ! \n" + unknownOptionsText + "\n\n" +
|
|
"The current command line options in ResInsight are:\n" + app->commandLineParameterHelp() );
|
|
|
|
if ( dynamic_cast<RiaGuiApplication*>( app.get() ) == nullptr )
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
QLocale::setDefault( QLocale( QLocale::English, QLocale::UnitedStates ) );
|
|
setlocale( LC_NUMERIC, "C" );
|
|
|
|
// Set up signal handlers for genuine fault/abort signals only.
|
|
// SIGINT and SIGTERM are graceful termination requests (Ctrl-C, kill, or a
|
|
// controlling process such as the Python rips client). They must NOT be routed
|
|
// through the crash handler: doing so captures the stack trace of wherever the
|
|
// main thread happens to be parked - e.g. inside the multi-second gRPC server
|
|
// EventEngine teardown during normal shutdown - and reports it as a spurious
|
|
// "Crash". Leaving them with their default disposition lets the process
|
|
// terminate cleanly.
|
|
#ifndef WIN32
|
|
// Use SA_SIGINFO on Linux to capture fault address, signal code, and program counter
|
|
struct sigaction sa{};
|
|
sa.sa_sigaction = manageSegFailureSA;
|
|
sa.sa_flags = SA_SIGINFO;
|
|
sigemptyset( &sa.sa_mask );
|
|
sigaction( SIGILL, &sa, nullptr );
|
|
sigaction( SIGFPE, &sa, nullptr );
|
|
sigaction( SIGSEGV, &sa, nullptr );
|
|
sigaction( SIGABRT, &sa, nullptr );
|
|
#else
|
|
signal( SIGILL, manageSegFailure );
|
|
signal( SIGFPE, manageSegFailure );
|
|
signal( SIGSEGV, manageSegFailure );
|
|
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.
|
|
// The reason for this is: deleteLater() does not work outside the event loop
|
|
// Make execution of command line stuff operate in identical conditions as interactive operation.
|
|
|
|
RiaApplication::ApplicationStatus status = app->handleArguments( &progOpt );
|
|
|
|
if ( status == RiaApplication::ApplicationStatus::EXIT_COMPLETED )
|
|
{
|
|
// Make sure project is closed to avoid assert and crash in destruction of widgets
|
|
app->closeProject();
|
|
|
|
app.reset();
|
|
RiaMainTools::releaseSingletonAndFactoryObjects();
|
|
|
|
return 0;
|
|
}
|
|
else if ( status == RiaApplication::ApplicationStatus::EXIT_WITH_ERROR )
|
|
{
|
|
// Make sure project is closed to avoid assert and crash in destruction of widgets
|
|
app->closeProject();
|
|
|
|
return 2;
|
|
}
|
|
else if ( status == RiaApplication::ApplicationStatus::KEEP_GOING )
|
|
{
|
|
int exitCode = 0;
|
|
// 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() );
|
|
if ( grpcInterface && grpcInterface->initializeGrpcServer( progOpt ) )
|
|
{
|
|
grpcInterface->launchGrpcServer();
|
|
|
|
if ( cvf::Option o = progOpt.option( "portnumberfile" ) )
|
|
{
|
|
if ( o.valueCount() == 1 )
|
|
{
|
|
int portNumber = grpcInterface->portNumber();
|
|
QString fileName = QString::fromStdString( o.value( 0 ).toStdString() );
|
|
|
|
// Write port number to the file given file.
|
|
// Temp file is used to avoid incomplete reads.
|
|
QString tempFilePath = fileName + ".tmp";
|
|
QFile file( tempFilePath );
|
|
if ( file.open( QIODevice::WriteOnly | QIODevice::Text ) )
|
|
{
|
|
QTextStream out( &file );
|
|
out << portNumber << "\n";
|
|
}
|
|
file.close();
|
|
|
|
QFile::rename( tempFilePath, fileName );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
// Check if max thread count is set either from command line or preferences
|
|
app->setThreadCount();
|
|
|
|
exitCode = QCoreApplication::instance()->exec();
|
|
}
|
|
|
|
app.reset();
|
|
RiaMainTools::releaseSingletonAndFactoryObjects();
|
|
|
|
return exitCode;
|
|
}
|
|
|
|
CVF_ASSERT( false && "Unknown ApplicationStatus" );
|
|
return -1;
|
|
}
|