// Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. #ifndef MFEM_ERROR_HPP #define MFEM_ERROR_HPP #include "../config/config.hpp" #include #include #ifdef MFEM_USE_HIP #include #endif namespace mfem { /// Action to take when MFEM encounters an error. enum ErrorAction { MFEM_ERROR_ABORT = 0, /**< Abort execution using abort() or MPI_Abort(). This is the default error action when the build option MFEM_USE_EXCEPTIONS is set to NO. */ MFEM_ERROR_THROW /**< Throw an ErrorException. Requires the build option MFEM_USE_EXCEPTIONS=YES in which case it is also the default error action. */ }; /// Set the action MFEM takes when an error is encountered. void set_error_action(ErrorAction action); /// Get the action MFEM takes when an error is encountered. ErrorAction get_error_action(); #ifdef MFEM_USE_EXCEPTIONS /** @brief Exception class thrown when MFEM encounters an error and the current ErrorAction is set to MFEM_ERROR_THROW. */ class ErrorException: public std::exception { private: std::string msg; public: explicit ErrorException(const std::string & in_msg) : msg(in_msg) { } virtual ~ErrorException() throw() { } virtual const char* what() const throw(); }; #endif void mfem_backtrace(int mode = 0, int depth = -1); /** @brief Function called when an error is encountered. Used by the macros MFEM_ABORT, MFEM_ASSERT, MFEM_VERIFY. */ void mfem_error(const char *msg = NULL); /// Function called by the macro MFEM_WARNING. void mfem_warning(const char *msg = NULL); #ifdef MFEM_USE_ENZYME static void* __enzyme_inactive_global_err = (void*)mfem_error; static void* __enzyme_inactive_global_warn = (void*)mfem_warning; #endif } #ifndef _MFEM_FUNC_NAME #ifndef _MSC_VER // This is nice because it shows the class and method name #define _MFEM_FUNC_NAME __PRETTY_FUNCTION__ // This one is C99 standard. //#define _MFEM_FUNC_NAME __func__ #else // for Visual Studio C++ #define _MFEM_FUNC_NAME __FUNCSIG__ #endif #endif #define MFEM_LOCATION \ "\n ... in function: " << _MFEM_FUNC_NAME << \ "\n ... in file: " << __FILE__ << ':' << __LINE__ << '\n' // Common error message and abort macro #define _MFEM_MESSAGE(msg, warn) \ { \ std::ostringstream mfemMsgStream; \ mfemMsgStream << std::setprecision(16); \ mfemMsgStream << std::setiosflags(std::ios_base::scientific); \ mfemMsgStream << msg << MFEM_LOCATION; \ if (!(warn)) \ mfem::mfem_error(mfemMsgStream.str().c_str()); \ else \ mfem::mfem_warning(mfemMsgStream.str().c_str()); \ } // Outputs lots of useful information and aborts. // For all of these functions, "msg" is pushed to an ostream, so you can // write useful (if complicated) error messages instead of writing // out to the screen first, then calling abort. For example: // MFEM_ABORT( "Unknown geometry type: " << type ); #define MFEM_ABORT(msg) _MFEM_MESSAGE("MFEM abort: " << msg, 0) // Does a check, and then outputs lots of useful information if the test fails #define MFEM_VERIFY(x, msg) \ if (!(x)) \ { \ _MFEM_MESSAGE("Verification failed: (" \ << #x << ") is false:\n --> " << msg, 0); \ } // Use this if the only place your variable is used is in ASSERTs // For example, this code snippet: // int err = MPI_Reduce(ldata, maxdata, 5, MPI_INT, MPI_MAX, 0, MyComm); // MFEM_CONTRACT_VAR(err); // MFEM_ASSERT( err == 0, "MPI_Reduce gave an error with length " // << ldata ); #define MFEM_CONTRACT_VAR(x) (void)(x) // Now set up some optional checks, but only if the right flags are on #ifdef MFEM_DEBUG #define MFEM_ASSERT(x, msg) \ if (!(x)) \ { \ _MFEM_MESSAGE("Assertion failed: (" \ << #x << ") is false:\n --> " << msg, 0); \ } // A macro that exposes its argument in debug mode only. #define MFEM_DEBUG_DO(x) x #else // Get rid of all this code, since we're not checking. #define MFEM_ASSERT(x, msg) // A macro that exposes its argument in debug mode only. #define MFEM_DEBUG_DO(x) #endif // Generate a warning message - always generated, regardless of MFEM_DEBUG. #define MFEM_WARNING(msg) _MFEM_MESSAGE("MFEM Warning: " << msg, 1) // Macro that checks (in MFEM_DEBUG mode) that i is in the range [imin,imax). #define MFEM_ASSERT_INDEX_IN_RANGE(i,imin,imax) \ MFEM_ASSERT((imin) <= (i) && (i) < (imax), \ "invalid index " #i << " = " << (i) << \ ", valid range is [" << (imin) << ',' << (imax) << ')') // Additional abort functions for HIP #if defined(MFEM_USE_HIP) template __host__ void abort_msg(T & msg) { MFEM_ABORT(msg); } template __device__ void abort_msg(T & msg) { abort(); } #endif // Abort inside a device kernel #if defined(__CUDA_ARCH__) #define MFEM_ABORT_KERNEL(...) \ { \ printf(__VA_ARGS__); \ asm("trap;"); \ } #elif defined(MFEM_USE_HIP) #define MFEM_ABORT_KERNEL(...) \ { \ printf(__VA_ARGS__); \ abort_msg(""); \ } #else #define MFEM_ABORT_KERNEL(...) \ { \ printf(__VA_ARGS__); \ MFEM_ABORT(""); \ } #endif // Verify inside a device kernel #define MFEM_VERIFY_KERNEL(x,...) \ if (!(x)) \ { \ MFEM_ABORT_KERNEL(__VA_ARGS__) \ } // Assert inside a device kernel #ifdef MFEM_DEBUG #define MFEM_ASSERT_KERNEL(x,...) \ if (!(x)) \ { \ MFEM_ABORT_KERNEL(__VA_ARGS__) \ } #else #define MFEM_ASSERT_KERNEL(x,...) #endif #endif