1 // Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #ifndef MFEM_ERROR_HPP
13 #define MFEM_ERROR_HPP
14 
15 #include "../config/config.hpp"
16 #include <iomanip>
17 #include <sstream>
18 #ifdef MFEM_USE_HIP
19 #include <hip/hip_runtime.h>
20 #endif
21 
22 namespace mfem
23 {
24 
25 /// Action to take when MFEM encounters an error.
26 enum ErrorAction
27 {
28    MFEM_ERROR_ABORT = 0, /**<
29       Abort execution using abort() or MPI_Abort(). This is the default error
30       action when the build option MFEM_USE_EXCEPTIONS is set to NO. */
31    MFEM_ERROR_THROW      /**<
32       Throw an ErrorException. Requires the build option MFEM_USE_EXCEPTIONS=YES
33       in which case it is also the default error action. */
34 };
35 
36 /// Set the action MFEM takes when an error is encountered.
37 void set_error_action(ErrorAction action);
38 /// Get the action MFEM takes when an error is encountered.
39 ErrorAction get_error_action();
40 
41 #ifdef MFEM_USE_EXCEPTIONS
42 /** @brief Exception class thrown when MFEM encounters an error and the current
43     ErrorAction is set to MFEM_ERROR_THROW. */
44 class ErrorException: public std::exception
45 {
46 private:
47    std::string msg;
48 public:
ErrorException(const std::string & in_msg)49    explicit ErrorException(const std::string & in_msg) : msg(in_msg) { }
~ErrorException()50    virtual ~ErrorException() throw() { }
51    virtual const char* what() const throw();
52 };
53 #endif
54 
55 void mfem_backtrace(int mode = 0, int depth = -1);
56 
57 /** @brief Function called when an error is encountered. Used by the macros
58     MFEM_ABORT, MFEM_ASSERT, MFEM_VERIFY. */
59 void mfem_error(const char *msg = NULL);
60 
61 /// Function called by the macro MFEM_WARNING.
62 void mfem_warning(const char *msg = NULL);
63 
64 }
65 
66 #ifndef _MFEM_FUNC_NAME
67 #ifndef _MSC_VER
68 // This is nice because it shows the class and method name
69 #define _MFEM_FUNC_NAME __PRETTY_FUNCTION__
70 // This one is C99 standard.
71 //#define _MFEM_FUNC_NAME __func__
72 #else
73 // for Visual Studio C++
74 #define _MFEM_FUNC_NAME __FUNCSIG__
75 #endif
76 #endif
77 
78 #define MFEM_LOCATION \
79    "\n ... in function: " << _MFEM_FUNC_NAME << \
80    "\n ... in file: " << __FILE__ << ':' << __LINE__ << '\n'
81 
82 // Common error message and abort macro
83 #define _MFEM_MESSAGE(msg, warn)                                        \
84    {                                                                    \
85       std::ostringstream mfemMsgStream;                                 \
86       mfemMsgStream << std::setprecision(16);                           \
87       mfemMsgStream << std::setiosflags(std::ios_base::scientific);     \
88       mfemMsgStream << msg << MFEM_LOCATION;                            \
89       if (!(warn))                                                      \
90          mfem::mfem_error(mfemMsgStream.str().c_str());                 \
91       else                                                              \
92          mfem::mfem_warning(mfemMsgStream.str().c_str());               \
93    }
94 
95 // Outputs lots of useful information and aborts.
96 // For all of these functions, "msg" is pushed to an ostream, so you can
97 // write useful (if complicated) error messages instead of writing
98 // out to the screen first, then calling abort.  For example:
99 // MFEM_ABORT( "Unknown geometry type: " << type );
100 #define MFEM_ABORT(msg) _MFEM_MESSAGE("MFEM abort: " << msg, 0)
101 
102 // Does a check, and then outputs lots of useful information if the test fails
103 #define MFEM_VERIFY(x, msg)                             \
104    if (!(x))                                            \
105    {                                                    \
106       _MFEM_MESSAGE("Verification failed: ("            \
107                     << #x << ") is false:\n --> " << msg, 0); \
108    }
109 
110 // Use this if the only place your variable is used is in ASSERTs
111 // For example, this code snippet:
112 //   int err = MPI_Reduce(ldata, maxdata, 5, MPI_INT, MPI_MAX, 0, MyComm);
113 //   MFEM_CONTRACT_VAR(err);
114 //   MFEM_ASSERT( err == 0, "MPI_Reduce gave an error with length "
115 //                       << ldata );
116 #define MFEM_CONTRACT_VAR(x) if (false && (&x)+1){}
117 
118 // Now set up some optional checks, but only if the right flags are on
119 #ifdef MFEM_DEBUG
120 
121 #define MFEM_ASSERT(x, msg)                             \
122    if (!(x))                                            \
123    {                                                    \
124       _MFEM_MESSAGE("Assertion failed: ("               \
125                     << #x << ") is false:\n --> " << msg, 0); \
126    }
127 
128 // A macro that exposes its argument in debug mode only.
129 #define MFEM_DEBUG_DO(x) x
130 
131 #else
132 
133 // Get rid of all this code, since we're not checking.
134 #define MFEM_ASSERT(x, msg)
135 
136 // A macro that exposes its argument in debug mode only.
137 #define MFEM_DEBUG_DO(x)
138 
139 #endif
140 
141 // Generate a warning message - always generated, regardless of MFEM_DEBUG.
142 #define MFEM_WARNING(msg) _MFEM_MESSAGE("MFEM Warning: " << msg, 1)
143 
144 // Macro that checks (in MFEM_DEBUG mode) that i is in the range [imin,imax).
145 #define MFEM_ASSERT_INDEX_IN_RANGE(i,imin,imax) \
146    MFEM_ASSERT((imin) <= (i) && (i) < (imax), \
147    "invalid index " #i << " = " << (i) << \
148    ", valid range is [" << (imin) << ',' << (imax) << ')')
149 
150 
151 // Additional abort functions for HIP
152 #if defined(MFEM_USE_HIP)
153 template<typename T>
abort_msg(T & msg)154 __host__ void abort_msg(T & msg)
155 {
156    MFEM_ABORT(msg);
157 }
158 
159 template<typename T>
abort_msg(T & msg)160 __device__ void abort_msg(T & msg)
161 {
162    abort();
163 }
164 #endif
165 
166 // Abort inside a device kernel
167 #if defined(__CUDA_ARCH__)
168 #define MFEM_ABORT_KERNEL(msg) \
169    {                           \
170       printf(msg);             \
171       asm("trap;");            \
172    }
173 #elif defined(MFEM_USE_HIP)
174 #define MFEM_ABORT_KERNEL(msg) \
175    {                           \
176       abort_msg(msg);          \
177    }
178 #else
179 #define MFEM_ABORT_KERNEL(msg) MFEM_ABORT(msg)
180 #endif
181 
182 #endif
183