diff --git a/src/coreComponents/common/CMakeLists.txt b/src/coreComponents/common/CMakeLists.txt index d0143b9564b..25dd41d776e 100644 --- a/src/coreComponents/common/CMakeLists.txt +++ b/src/coreComponents/common/CMakeLists.txt @@ -41,7 +41,8 @@ set( common_headers MemoryInfos.hpp logger/GeosExceptions.hpp logger/Logger.hpp - logger/ErrorHandling.hpp + logger/ErrorHandler.hpp + logger/ErrorLogger.hpp logger/ExternalErrorHandler.hpp MpiWrapper.hpp Path.hpp @@ -80,7 +81,8 @@ set( common_sources format/StringUtilities.cpp logger/GeosExceptions.cpp logger/Logger.cpp - logger/ErrorHandling.cpp + logger/ErrorHandler.cpp + logger/ErrorLogger.cpp logger/ExternalErrorHandler.cpp BufferAllocator.cpp MemoryInfos.cpp diff --git a/src/coreComponents/common/initializeEnvironment.cpp b/src/coreComponents/common/initializeEnvironment.cpp index e43263c8528..11520246bd9 100644 --- a/src/coreComponents/common/initializeEnvironment.cpp +++ b/src/coreComponents/common/initializeEnvironment.cpp @@ -19,8 +19,7 @@ #include "Path.hpp" #include "LvArray/src/system.hpp" #include "common/LifoStorageCommon.hpp" -#include "logger/ErrorHandling.hpp" -#include "logger/ExternalErrorHandler.hpp" +#include "logger/ErrorHandler.hpp" #include // TPL includes #include @@ -68,73 +67,13 @@ void setupLogger() logger::InitializeLogger(); #endif - { // setup error handling (using LvArray helper system functions) - - ExternalErrorHandler::instance().enableStderrPipeDeviation( true ); - - ///// set external error handling behaviour ///// - ExternalErrorHandler::instance().setErrorHandling( []( string_view errorMsg, - string_view detectionLocation ) - { - // Filter out INFO level messages from external libraries (e.g., VTK) - // ( error / signal lambda would calls either an error function or an info function, depending on a filtering function ) - if( ExternalErrorHandler::isNotAnErrorMsg( errorMsg ) ) - { - // Just print the message without error formatting - GEOS_LOG( errorMsg ); - return; - } - else - { - std::string const stackHistory = LvArray::system::stackTrace( true ); - DiagnosticMsg diagnosticMsg; - ErrorLogger::global().flushErrorMsg( DiagnosticMsgBuilder::init( diagnosticMsg, - MsgType::Error, errorMsg, - ::geos::logger::internal::g_rank ) - .addCallStackInfo( stackHistory ) - .addDetectionLocation( detectionLocation ) - .getDiagnosticMsg() ); - - // we do not terminate the program as 1. the error could be non-fatal, 2. there may be more messages to output. - } - } ); - - ///// set signal handling behaviour ///// - LvArray::system::setSignalHandling( []( int const signal ) - { - // Disable signal handling to prevent catching exit signal (infinite loop) - LvArray::system::setSignalHandling( nullptr ); - - // first of all, external error can await to be output, we must output them - ExternalErrorHandler::instance().flush( "before signal error output" ); - - // error message output - std::string const stackHistory = LvArray::system::stackTrace( true ); - DiagnosticMsg diagnosticMsg; - ErrorLogger::global().flushErrorMsg( DiagnosticMsgBuilder::init( diagnosticMsg, - MsgType::ExternalError, "", - ::geos::logger::internal::g_rank ) - .addSignal( signal ) - .addCallStackInfo( stackHistory ) - .getDiagnosticMsg() ); - - // call program termination - LvArray::system::callErrorHandler(); - } ); - - ///// set Post-Handled Error behaviour ///// - LvArray::system::setErrorHandler( []() - { - #if defined( GEOS_USE_MPI ) - int mpi = 0; - MPI_Initialized( &mpi ); - if( mpi ) - { - MPI_Abort( MPI_COMM_WORLD, EXIT_FAILURE ); - } - #endif - std::abort(); - } ); + { + ErrorHandler defaultErrorHandler; + defaultErrorHandler.setLogger( &ErrorLogger::global() ); + defaultErrorHandler.enableExternalErrorPipeDeviation( true ); + defaultErrorHandler.setProgramAborter( abortGeos ); + + ErrorHandler::setupErrorHandlingStrategy( std::move( defaultErrorHandler ) ); } } @@ -339,4 +278,17 @@ void cleanupEnvironment( bool inError ) finalizeMPI( inError ); } +void abortGeos() +{ + #if defined( GEOS_USE_MPI ) + int mpi = 0; + MPI_Initialized( &mpi ); + if( mpi ) + { + MPI_Abort( MPI_COMM_WORLD, EXIT_FAILURE ); + } + #endif + std::abort(); +} + } // namespace geos diff --git a/src/coreComponents/common/initializeEnvironment.hpp b/src/coreComponents/common/initializeEnvironment.hpp index f01c1e7df2b..6ff05ff7c26 100644 --- a/src/coreComponents/common/initializeEnvironment.hpp +++ b/src/coreComponents/common/initializeEnvironment.hpp @@ -195,6 +195,11 @@ void pushStatsIntoAdiak( string const & name, T const value ) #endif } +/** + * @brief Post-Handling error behaviour + */ +void abortGeos(); + } // namespace geos #endif // GEOS_COMMON_INITIALIZEENVIRONMENT_HPP_ diff --git a/src/coreComponents/common/logger/ErrorHandler.cpp b/src/coreComponents/common/logger/ErrorHandler.cpp new file mode 100644 index 00000000000..00918d2b341 --- /dev/null +++ b/src/coreComponents/common/logger/ErrorHandler.cpp @@ -0,0 +1,142 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file ErrorHandler.cpp + */ + +#include "ErrorHandler.hpp" + +#include "ExternalErrorHandler.hpp" +#include "Logger.hpp" + +namespace geos +{ + +ErrorHandler ErrorHandler::s_errorHandlerInstance; + +ErrorHandler::ErrorHandler() + : m_logger( &ErrorLogger::global() ) + , m_abortingFunctor( []() { std::abort(); } ) +{} + +ErrorHandler const & ErrorHandler::getInstance() +{ + return s_errorHandlerInstance; +} + +void ErrorHandler::setupErrorHandlingStrategy( ErrorHandler && instance ) +{ + s_errorHandlerInstance = instance; + s_errorHandlerInstance.setupExternalErrorManagment(); + s_errorHandlerInstance.setupSignalHandling(); + s_errorHandlerInstance.setupLvArrayErrorHandling(); +} + +void ErrorHandler::setupExternalErrorManagment() +{ + ExternalErrorHandler::instance().enableStderrPipeDeviation( true ); + + ExternalErrorHandler::instance().setErrorHandling( [&]( string_view errorMsg, + string_view detectionLocation ) + { + // Filter out INFO level messages from external libraries (e.g., VTK) + // ( error / signal lambda would calls either an error function or an info function, depending on a filtering function ) + if( ExternalErrorHandler::isNotAnErrorMsg( errorMsg ) ) + { + // Just print the message without error formatting + GEOS_LOG( errorMsg ); + return; + } + else + { + std::string const stackHistory = LvArray::system::stackTrace( true ); + DiagnosticMsg diagnosticMsg; + m_logger->flushErrorMsg( DiagnosticMsgBuilder::init( diagnosticMsg, + MsgType::Error, errorMsg, + ::geos::logger::internal::g_rank ) + .addCallStackInfo( stackHistory ) + .addDetectionLocation( detectionLocation ) + .setCause( "Error pipe output from a dependency" ) + .getDiagnosticMsg() ); + + // we do not terminate the program as 1. the error could be non-fatal, 2. there may be more messages to output. + } + } ); +} + +void ErrorHandler::setupSignalHandling() +{ + LvArray::system::setSignalHandling( []( int const signal ) + { + // setSignalHandling() cannot take capturing lambda, so we get the instance with the static function. + ErrorHandler const & instance = ErrorHandler::getInstance(); + + // Disable signal handling to prevent catching exit signal (infinite loop) + LvArray::system::setSignalHandling( nullptr ); + + // first of all, there can be external error that await to be output + ExternalErrorHandler::instance().flush( "before signal error output" ); + + // error message output + std::string const stackHistory = LvArray::system::stackTrace( true ); + DiagnosticMsg diagnosticMsg; + DiagnosticMsgBuilder::init( diagnosticMsg, + MsgType::ExternalError, "", + ::geos::logger::internal::g_rank ) + .addSignal( signal ) + .addCallStackInfo( stackHistory ); + instance.m_logger->flushErrorMsg( diagnosticMsg ); + + // call program termination + instance.abortProgram(); + } ); +} + +void ErrorHandler::setupLvArrayErrorHandling() +{ + LvArray::system::setErrorHandler( [&]() + { + // first of all, there can be external error that await to be output + ExternalErrorHandler::instance().flush( "before LvArray error handling" ); + + // default error message output + DiagnosticMsg diagnosticMsg; + DiagnosticMsgBuilder::init( diagnosticMsg, + MsgType::ExternalError, + "LvArray Runtime Error", + ::geos::logger::internal::g_rank ) + .addCallStackInfo( LvArray::system::stackTrace( true ) ) + .addDetectionLocation( "LvArray Error Handler" ); + m_logger->flushErrorMsg( diagnosticMsg ); + + // call program termination + abortProgram(); + } ); +} + +void ErrorHandler::manageException( std::exception const & e, + string_view causeMessage, + string_view stackTrace ) const +{ + m_logger->flushErrorMsg( m_logger->initCurrentExceptionMessage( MsgType::Exception, + e.what(), + ::geos::logger::internal::g_rank ) + .setCause( causeMessage ) + .addCallStackInfo( stackTrace ) + .getDiagnosticMsg()); +} + +} /* namespace geos */ diff --git a/src/coreComponents/common/logger/ErrorHandler.hpp b/src/coreComponents/common/logger/ErrorHandler.hpp new file mode 100644 index 00000000000..27a31204c02 --- /dev/null +++ b/src/coreComponents/common/logger/ErrorHandler.hpp @@ -0,0 +1,123 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file ErrorHandler.hpp + */ + +#ifndef INITIALIZATION_ERROR_HANDLER_HPP +#define INITIALIZATION_ERROR_HANDLER_HPP + +#include "ErrorLogger.hpp" + +namespace geos +{ + +class ErrorHandler +{ +public: + + /** + * @brief Singleton pattern (as we can only have one strategy of handling at the same time). + * Update the current error handling strategy. Once setup, the instance is not meant to be mutable. + * @return ErrorHandler const& + */ + static void setupErrorHandlingStrategy( ErrorHandler && instance ); + + /** + * @brief Singleton pattern (as we can only have one strategy of handling at the same time). + * @return Handler instance currently setup. + */ + static ErrorHandler const & getInstance(); + + /** + * @brief Construct a new Error Handler object. To use it as the current error handling strategy, + * setupErrorHandlingStrategy() must be called on this instance after setting it up. + */ + ErrorHandler(); + + /** + * @brief Enable pipe deviation of external stderr messages, + * @see ExternalErrorHandler + */ + void enableExternalErrorPipeDeviation( bool enable ) + { m_isPipeDeviationEnabled = enable; } + + /** + * @brief Set the callback to call when post-error program abortion is requested. + * @param abortingFunctor a void() functor that terminates the program in a safe way. + */ + void setProgramAborter( std::function< void() > const & abortingFunctor ) + { m_abortingFunctor = abortingFunctor; } + + /** + * @brief the ErrorLogger where error messages are rooted + * @param logger the instance of the logger, ErrorLogger::instance() or a local one. + */ + void setLogger( ErrorLogger * logger ) + { m_logger = logger; } + + /** + * @return the ErrorLogger where error messages are rooted + */ + ErrorLogger & getLogger() const + { return *m_logger; } + + /** + * @brief Output the error message for a std exception. Std exception are likely thrown from a + * dependency, as geos::exception is the standard exception type to throw from GEOS. + * @param e + * @param stack_trace + */ + void manageException( std::exception const & e, + string_view causeMessage, + string_view stackTrace ) const; + + /** + * @brief Post error-handling function that terminates the program. + */ + void abortProgram() const + { m_abortingFunctor(); } + +private: + + static ErrorHandler s_errorHandlerInstance; + + ErrorLogger * m_logger; + + bool m_isPipeDeviationEnabled = false; + + std::function< void() > m_abortingFunctor; + + /** + * @brief set external error handling behaviour + */ + void setupExternalErrorManagment(); + + /** + * @brief set signal handling behaviour + */ + void setupSignalHandling(); + + /** + * @brief Set LvArray to use the GEOS error behaviour in a way that can be traced + */ + void setupLvArrayErrorHandling(); + +}; + +} /* namespace geos */ + +#endif diff --git a/src/coreComponents/common/logger/ErrorHandling.cpp b/src/coreComponents/common/logger/ErrorLogger.cpp similarity index 96% rename from src/coreComponents/common/logger/ErrorHandling.cpp rename to src/coreComponents/common/logger/ErrorLogger.cpp index cd9a2748302..3b845df4c0e 100644 --- a/src/coreComponents/common/logger/ErrorHandling.cpp +++ b/src/coreComponents/common/logger/ErrorLogger.cpp @@ -14,10 +14,10 @@ */ /** - * @file ErrorHandling.cpp + * @file ErrorLogger.cpp */ -#include "ErrorHandling.hpp" +#include "ErrorLogger.hpp" #include "common/DataTypes.hpp" #include "common/logger/Logger.hpp" #include "common/format/StringUtilities.hpp" @@ -62,12 +62,10 @@ DiagnosticMsgBuilder ErrorLogger::initCurrentExceptionMessage( MsgType msgType, std::string_view msgContent, integer rank ) { - DiagnosticMsg diagnosticMsg; - m_getCurrentExceptionMsg = DiagnosticMsgBuilder::init( diagnosticMsg, - msgType, msgContent, - rank ) - .addCallStackInfo( LvArray::system::stackTrace( true ) ) - .getDiagnosticMsg(); + m_getCurrentExceptionMsg = DiagnosticMsg(); + DiagnosticMsgBuilder::init( m_getCurrentExceptionMsg, + msgType, msgContent, + rank ); return DiagnosticMsgBuilder::modify( m_getCurrentExceptionMsg ); } diff --git a/src/coreComponents/common/logger/ErrorHandling.hpp b/src/coreComponents/common/logger/ErrorLogger.hpp similarity index 99% rename from src/coreComponents/common/logger/ErrorHandling.hpp rename to src/coreComponents/common/logger/ErrorLogger.hpp index 095cdfc1789..4f17ce8d0bb 100644 --- a/src/coreComponents/common/logger/ErrorHandling.hpp +++ b/src/coreComponents/common/logger/ErrorLogger.hpp @@ -14,7 +14,7 @@ */ /** - * @file ErrorHandling.hpp + * @file ErrorLogger.hpp */ #ifndef INITIALIZATION_ERROR_LOGGER_HPP @@ -424,8 +424,6 @@ class ErrorLogger std::string_view indent ); }; - - } /* namespace geos */ #endif diff --git a/src/coreComponents/common/logger/ExternalErrorHandler.cpp b/src/coreComponents/common/logger/ExternalErrorHandler.cpp index 903d461b78c..2603e2f02c5 100644 --- a/src/coreComponents/common/logger/ExternalErrorHandler.cpp +++ b/src/coreComponents/common/logger/ExternalErrorHandler.cpp @@ -14,7 +14,7 @@ */ /** - * @file ErrorHandling.cpp + * @file ErrorHandler.cpp */ #include "ExternalErrorHandler.hpp" diff --git a/src/coreComponents/common/logger/ExternalErrorHandler.hpp b/src/coreComponents/common/logger/ExternalErrorHandler.hpp index fc641de9db9..cc0e687ce72 100644 --- a/src/coreComponents/common/logger/ExternalErrorHandler.hpp +++ b/src/coreComponents/common/logger/ExternalErrorHandler.hpp @@ -14,7 +14,7 @@ */ /** - * @file ErrorHandling.hpp + * @file ExternalErrorHandler.hpp * @brief This file provides the infrastructure to capture external errors. * @note Below is the architecture of the external error managment, in the scenario of a problematic * infrastructure which deviates (thus breaks) stderr. @@ -52,7 +52,7 @@ #ifndef LOGGER_EXTERNALERRORHANDLER_HPP #define LOGGER_EXTERNALERRORHANDLER_HPP -#include "ErrorHandling.hpp" +#include "ErrorLogger.hpp" #include #include diff --git a/src/coreComponents/common/logger/GeosExceptions.hpp b/src/coreComponents/common/logger/GeosExceptions.hpp index f3934bebbc3..08999997b7a 100644 --- a/src/coreComponents/common/logger/GeosExceptions.hpp +++ b/src/coreComponents/common/logger/GeosExceptions.hpp @@ -18,7 +18,7 @@ */ -#include "common/logger/ErrorHandling.hpp" +#include "common/logger/ErrorHandler.hpp" namespace geos { diff --git a/src/coreComponents/common/logger/Logger.hpp b/src/coreComponents/common/logger/Logger.hpp index b23ba87a39d..6c4e263a9d6 100644 --- a/src/coreComponents/common/logger/Logger.hpp +++ b/src/coreComponents/common/logger/Logger.hpp @@ -164,7 +164,7 @@ .addCallStackInfo( LvArray::system::stackTrace( true ) ) \ .addContextInfo( GEOS_DETAIL_REST_ARGS( __VA_ARGS__ )); \ GEOS_GLOBAL_LOGGER.flushCurrentExceptionMessage(); \ - LvArray::system::callErrorHandler(); \ + ErrorHandler::getInstance().abortProgram(); \ } \ }while( false ) #elif __CUDA_ARCH__ diff --git a/src/coreComponents/constitutive/fluid/multifluid/blackOil/BlackOilFluidBase.cpp b/src/coreComponents/constitutive/fluid/multifluid/blackOil/BlackOilFluidBase.cpp index 098fcefacad..f4c2f0c6bd2 100644 --- a/src/coreComponents/constitutive/fluid/multifluid/blackOil/BlackOilFluidBase.cpp +++ b/src/coreComponents/constitutive/fluid/multifluid/blackOil/BlackOilFluidBase.cpp @@ -15,7 +15,7 @@ #include "BlackOilFluidBase.hpp" -#include "common/logger/ErrorHandling.hpp" +#include "common/logger/ErrorLogger.hpp" #include "constitutive/fluid/multifluid/MultiFluidUtils.hpp" #include "constitutive/fluid/multifluid/CO2Brine/functions/PVTFunctionHelpers.hpp" #include "functions/FunctionManager.hpp" diff --git a/src/coreComponents/dataRepository/DataContext.hpp b/src/coreComponents/dataRepository/DataContext.hpp index 8a43381b438..20b48d13bc5 100644 --- a/src/coreComponents/dataRepository/DataContext.hpp +++ b/src/coreComponents/dataRepository/DataContext.hpp @@ -24,7 +24,7 @@ #include "common/logger/Logger.hpp" #include "xmlWrapper.hpp" #include "common/format/Format.hpp" -#include "common/logger/ErrorHandling.hpp" +#include "common/logger/ErrorLogger.hpp" namespace geos { diff --git a/src/coreComponents/dataRepository/unitTests/testErrorHandling.cpp b/src/coreComponents/dataRepository/unitTests/testErrorHandling.cpp index d2a2d16257a..e80a25c2a1e 100644 --- a/src/coreComponents/dataRepository/unitTests/testErrorHandling.cpp +++ b/src/coreComponents/dataRepository/unitTests/testErrorHandling.cpp @@ -15,11 +15,10 @@ // forcefully enable asserts macros for this unit test #include "LvArray/src/system.hpp" -#include "common/logger/ExternalErrorHandler.hpp" #include "gtest/gtest.h" #define GEOS_ASSERT_ENABLED -#include "common/logger/ErrorHandling.hpp" +#include "common/logger/ErrorHandler.hpp" #include "common/logger/Logger.hpp" #include "dataRepository/DataContext.hpp" #include "common/initializeEnvironment.hpp" @@ -48,6 +47,14 @@ DataFileContext const context = DataFileContext( "Base Test Class", "/path/to/fi DataFileContext const additionalContext = DataFileContext( "Additional Test Class", "/path/to/file.xml", 32 ); DataFileContext const importantAdditionalContext = DataFileContext( "Important Additional Test Class", "/path/to/file.xml", 64 ); +// original error handler to backup to +ErrorHandler defaultErrorhandler; + +/** + * @name Helper functions + */ +///@{ + /** * @brief begin a test with a local logger * @param errorLogger local error logger instance @@ -96,7 +103,7 @@ void endLocalLoggerTest( ErrorLogger & errorLogger, << "-----------------------\n"; testFailed |= !foundFileBit; } - EXPECT_FALSE( testFailed ) << "Generated error file content:\n" + EXPECT_FALSE( testFailed ) << "Generated error file content ("< +void beginLocalErrorHandlerTest( ErrorLogger & logger, LAMBDA && abortCustomFunc ) +{ + ErrorHandler testErrorHandler; + testErrorHandler.setProgramAborter( abortCustomFunc ); + testErrorHandler.setLogger( &logger ); + + // backup and change + defaultErrorhandler = ErrorHandler::getInstance(); + ErrorHandler::setupErrorHandlingStrategy( std::move( testErrorHandler ) ); +} + +void endLocalErrorHandlerTest() +{ + // restore backup + ErrorHandler defaultErrorHandlerBackup = defaultErrorhandler; + ErrorHandler::setupErrorHandlingStrategy( std::move( defaultErrorHandlerBackup ) ); +} + +///@} + TEST( ErrorHandling, testYamlFileWarningOutput ) { ErrorLogger testErrorLogger; - beginLocalLoggerTest( testErrorLogger, "warningTestOutput.yaml" ); + beginLocalLoggerTest( testErrorLogger, "testYamlFileWarningOutput.yaml" ); GET_LINE( line1 ); GEOS_WARNING( "Conflicting pressure boundary conditions" ); GET_LINE( line2 ); GEOS_WARNING_IF_GT_MSG( testValue, testMaxPrecision, "Pressure value is too high." ); @@ -170,7 +198,7 @@ TEST( ErrorHandling, testYamlFileWarningOutput ) TEST( ErrorHandling, testYamlFileExceptionOutput ) { ErrorLogger testErrorLogger; - string const file = "exceptionTestOutput.yaml"; + string const file = "testYamlFileExceptionOutput.yaml"; beginLocalLoggerTest( testErrorLogger, file ); size_t line1; @@ -241,7 +269,7 @@ TEST( ErrorHandling, testYamlFileErrorOutput ) { ErrorLogger testErrorLogger; - beginLocalLoggerTest( testErrorLogger, "errorTestOutput.yaml" ); + beginLocalLoggerTest( testErrorLogger, "testYamlFileErrorOutput.yaml" ); EXPECT_EXIT( GEOS_ERROR_IF_GT_MSG( testValue, testMaxPrecision, GEOS_FMT( "{}: option should be lower than {}.", @@ -287,7 +315,6 @@ TEST( ErrorHandling, testYamlFileErrorOutput ) } ); } - TEST( ErrorHandling, testLogFileExceptionOutput ) { ErrorLogger testErrorLogger; @@ -327,7 +354,6 @@ TEST( ErrorHandling, testLogFileExceptionOutput ) ErrorLogger::formatMsgForLog( testErrorLogger.getCurrentExceptionMsg(), oss ); GEOS_ERROR_IF_EQ_MSG( oss.str().find( streamExpected ), string::npos, GEOS_FMT( "The error message was not containing the expected sequence.\n" - "The error message was not containing the expected sequence.\n" " Error message :\n{}" " expected sequence :\n{}", oss.str(), @@ -335,45 +361,113 @@ TEST( ErrorHandling, testLogFileExceptionOutput ) } } -TEST( ErrorHandling, testStdException ) +// testing the capture & processing of lvarray exceptions, also testing what happens in case of an +// std::exception (as this is what lvarray throws). +TEST( ErrorHandling, testLvArrayStdException ) { ErrorLogger testErrorLogger; + bool exceptionHappened = false; + + beginLocalLoggerTest( testErrorLogger, "testLvArrayAndStdException.yaml" ); + beginLocalErrorHandlerTest( testErrorLogger, abortGeos ); + // Standard exception thrown by LvArray try { - - throw std::invalid_argument( "received negative value" ); + // no ',' in between numbers will throw an exception + array1d< localIndex > dummy; + LvArray::input::stringToArray( dummy, "{123 456}" ); + } + catch( geos::Exception & e ) + { + EXPECT_FALSE( true ) << "Exception not correctly handled."; + } + catch( std::exception const & e ) + { + exceptionHappened = true; + + // mimic "main()" exception logging behaviour + { + string_view constexpr causeMessage = "A dependency has thrown an exception"; + auto const stackTrace = LvArray::system::stackTrace( true ); // auto const for compatibility with stacktrace library + ErrorHandler::getInstance().manageException( e, causeMessage, stackTrace ); + } + // we continue without aborting to check the yaml results } - catch( std::exception & e ) + catch( ... ) { + EXPECT_FALSE( true ) << "Unexpected exception."; + } + EXPECT_TRUE( exceptionHappened ) << "Exception has not been thrown"; + + // we have to inherint the LvArray formatting here + endLocalErrorHandlerTest(); + endLocalLoggerTest( testErrorLogger, { + R"(errors:)", - testErrorLogger.initCurrentExceptionMessage( MsgType::Exception, e.what(), - ::geos::logger::internal::g_rank ) - .addCallStackInfo( LvArray::system::stackTrace( true ) ); + // LvArray Exception + R"(- type: Exception + rank: 0 + message: >-)", - std::ostringstream oss; - ErrorLogger::formatMsgForLog( testErrorLogger.getCurrentExceptionMsg(), oss ); - string const streamExpected = GEOS_FMT( - "***** Exception\n" - "***** Rank 0\n" - "***** Message :\n" - "{}\n", - testErrorLogger.getCurrentExceptionMsg().m_msg ); - GEOS_ERROR_IF_EQ_MSG( oss.str().find( streamExpected ), string::npos, - GEOS_FMT( "The error message was not containing the expected sequence.\n" - "The error message was not containing the expected sequence.\n" - " Error message :\n{}" - " expected sequence :\n{}", - oss.str(), - streamExpected ) ); - } + "Array value sequence specified without ',' delimiter: {123 456}", + + R"(cause: >- + A dependency has thrown an exception)", + "sourceCallStack:", + "- frame0: ", + "- frame1: ", + "- frame2: " + } ); +} + +// testing the capture & processing of lvarray error, also testing what happens when a dependency terminates +TEST( ErrorHandling, testLvArrayError ) +{ + ErrorLogger testErrorLogger; + + beginLocalLoggerTest( testErrorLogger, "testLvArrayAndStdError.yaml" ); + beginLocalErrorHandlerTest( testErrorLogger, abortGeos ); + + // Standard error caused by LvArray: not existing MemorySpace will cause an error with defined behaviour. + LvArray::MemorySpace const badValue = LvArray::MemorySpace( -2 ); + EXPECT_EXIT( LvArray::operator<<( std::cout, badValue ), ::testing::ExitedWithCode( 1 ), ".*" ); + + endLocalErrorHandlerTest(); + + // we have to inherint the LvArray formatting here + endLocalLoggerTest( testErrorLogger, { + R"(errors:)", + + R"( - type: ExternalError + rank: 0 + message: >- + LvArray Runtime Error + contexts: + - priority: 0 + description: LvArray Error Handler + detectionLocation: LvArray Error Handler)", + + // LvArray Error + // Exact reason is deactivated: For now, we cannot capture the lvarray error reason, because the lvarray error + // handler does not communicate the error message (excepted directly in std::cout). + // "Unrecognized memory space -2", + + // LvArray also does not communicate the error line. + // GEOS_FMT( "{}:{}", __FILE__, line1 ), + + "sourceCallStack:", + "- frame0: ", + "- frame1: ", + "- frame2: " + } ); } TEST( ErrorHandling, testYamlFileAssertOutput ) { ErrorLogger testErrorLogger; - beginLocalLoggerTest( testErrorLogger, "assertTestOutput.yaml" ); + beginLocalLoggerTest( testErrorLogger, "testYamlFileAssertOutput.yaml" ); EXPECT_EXIT( GEOS_ASSERT_MSG( testValue > testMinPrecision && testValue < testMaxPrecision, GEOS_FMT( "{}: value should be between {} and {}, but is {}.", @@ -412,44 +506,38 @@ TEST( ErrorHandling, testYamlFileAssertOutput ) } ); } - -TEST( ErrorHandling, VerifySignalHandlerLogs ) +TEST( ErrorHandling, testSignalHandling ) { ErrorLogger testErrorLogger; + bool signalHappened = false; - beginLocalLoggerTest( testErrorLogger, "errors.yaml" ); + beginLocalLoggerTest( testErrorLogger, "VerifySignalHandlerLogs.yaml" ); + beginLocalErrorHandlerTest( testErrorLogger, [&]() + { + signalHappened = true; + // we do not really abort, we want to test signal handling behaviour + } ); - setupLogger(); + raise( SIGFPE ); + EXPECT_TRUE( signalHappened ); - bool signalHappened = false; - LvArray::system::setErrorHandler( [&]() - { - endLocalLoggerTest( testErrorLogger, { - R"(- type: ExternalError + endLocalErrorHandlerTest(); + endLocalLoggerTest( testErrorLogger, { + R"(- type: ExternalError rank: 0 message: >- - Signal encountered (no. 2): Interrupt + Floating point error encountered: + Unknown reason. contexts: - priority: 0 description: Signal (detected from Signal Handler) detectionLocation: Signal handler - signal: 2 + signal: 8 sourceCallStack:)", - "- frame0: ", - "- frame1: ", - "- frame2: " - } ); - - signalHappened = true; + "- frame0: ", + "- frame1: ", + "- frame2: " } ); - - ErrorLogger::global().enableFileOutput( true ); - - raise( SIGINT ); - - EXPECT_TRUE( signalHappened ); - - setupLogger(); } int main( int ac, char * av[] ) diff --git a/src/coreComponents/integrationTests/dataRepositoryTests/testGroupPath.cpp b/src/coreComponents/integrationTests/dataRepositoryTests/testGroupPath.cpp index c177254e825..b5bd092e8c5 100644 --- a/src/coreComponents/integrationTests/dataRepositoryTests/testGroupPath.cpp +++ b/src/coreComponents/integrationTests/dataRepositoryTests/testGroupPath.cpp @@ -14,7 +14,7 @@ */ // Source includes -#include "common/logger/ErrorHandling.hpp" +#include "common/logger/ErrorLogger.hpp" #include "mainInterface/ProblemManager.hpp" #include "mainInterface/initialization.hpp" #include "mainInterface/GeosxState.hpp" diff --git a/src/coreComponents/mainInterface/initialization.cpp b/src/coreComponents/mainInterface/initialization.cpp index d371259f990..32218d27d71 100644 --- a/src/coreComponents/mainInterface/initialization.cpp +++ b/src/coreComponents/mainInterface/initialization.cpp @@ -129,7 +129,7 @@ std::unique_ptr< CommandLineOptions > parseCommandLineOptions( int argc, char * { TRACE_DATA_MIGRATION, 0, "", "trace-data-migration", Arg::None, "\t--trace-data-migration, \t Trace host-device data migration" }, { MEMORY_USAGE, 0, "m", "memory-usage", Arg::nonEmpty, "\t-m, --memory-usage, \t Minimum threshold for printing out memory allocations in a member of the data repository." }, { PAUSE_FOR, 0, "", "pause-for", Arg::numeric, "\t--pause-for, \t Pause geosx for a given number of seconds before starting execution" }, - { ERRORSOUTPUT, 0, "e", "errorsOutput", Arg::nonEmpty, "\t-e, --errors-output, \t Output path for the errors file (\".yaml\" supported)" }, + { ERRORSOUTPUT, 0, "e", "errors-output", Arg::nonEmpty, "\t-e, --errors-output, \t Output path for the errors file (\".yaml\" supported)" }, { 0, 0, nullptr, nullptr, nullptr, nullptr } }; diff --git a/src/main/main.cpp b/src/main/main.cpp index 00c7a560ef6..633c452eb92 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -14,7 +14,7 @@ */ // Source includes -#include "common/logger/ErrorHandling.hpp" +#include "common/logger/ErrorLogger.hpp" #include "common/logger/Logger.hpp" #include "common/MemoryInfos.hpp" #include "common/TimingMacros.hpp" @@ -81,17 +81,15 @@ int main( int argc, char *argv[] ) { // GEOS generated exceptions management ErrorLogger::global().flushCurrentExceptionMessage(); basicCleanup( true ); - LvArray::system::callErrorHandler(); + ErrorHandler::getInstance().abortProgram(); } catch( std::exception const & e ) { // native exceptions management - ErrorLogger::global().flushErrorMsg( ErrorLogger::global().initCurrentExceptionMessage( - MsgType::Exception, e.what(), - ::geos::logger::internal::g_rank ) - .addCallStackInfo( LvArray::system::stackTrace( true ) ) - .getDiagnosticMsg()); + string_view constexpr causeMessage = "A dependency has thrown an exception"; + auto const stackTrace = LvArray::system::stackTrace( true ); // auto const for compatibility with stacktrace library + ErrorHandler::getInstance().manageException( e, causeMessage, stackTrace ); basicCleanup( true ); - LvArray::system::callErrorHandler(); + ErrorHandler::getInstance().abortProgram(); } return 0; }