Branch data Line data Source code
1 : : // ***************************************************************************** 2 : : /*! 3 : : \file src/Base/Exception.cpp 4 : : \copyright 2012-2015 J. Bakosi, 5 : : 2016-2018 Los Alamos National Security, LLC., 6 : : 2019-2021 Triad National Security, LLC. 7 : : All rights reserved. See the LICENSE file for details. 8 : : \brief Exception class definition 9 : : \details Exception class definition 10 : : */ 11 : : // ***************************************************************************** 12 : : 13 : : #include <type_traits> 14 : : #include <cstdio> 15 : : #include <cxxabi.h> 16 : : #include <execinfo.h> 17 : : #include <sstream> 18 : : #include <iostream> 19 : : 20 : : #include "QuinoaBuildConfig.hpp" 21 : : #include "Exception.hpp" 22 : : 23 : : #ifdef HAS_BACKWARD 24 : : #include "NoWarning/backward.hpp" 25 : : #endif 26 : : 27 : : extern bool g_trace; 28 : : 29 : : using tk::Exception; 30 : : 31 : 117 : Exception::Exception( std::string&& message, 32 : : std::string&& file, 33 : : std::string&& function, 34 : 117 : unsigned int line ) noexcept 35 : : // ***************************************************************************** 36 : : // Constructor: generate error message 37 : : //! \param[in] message String (moved from) with an error message 38 : : //! \param[in] file String (moved from) with the file name in which the 39 : : //! exception ocurred 40 : : //! \param[in] function String (moved from) containing the name of the function 41 : : //! in which the exception ocurred 42 : : //! \param[in] line Source code line number at which the exception ocurred 43 : : //! \details While throwing exceptions is possible calling this constructor, the 44 : : //! easiest and recommend way is to use the Assert, ErrChk, and Throw macros. 45 : : //! Exception safety: no-throw guarantee: this member function never throws 46 : : //! exceptions. 47 : : //! \see Assert, ErrChk, Throw 48 : : // ***************************************************************************** 49 : : try : 50 : : m_file( std::move(file) ), 51 : : m_func( std::move(function) ), 52 : : m_line( std::move(line) ), 53 : : m_message( std::move(message) ), 54 : : m_addrLength( 0 ), 55 [ + + ][ + + ]: 234 : m_symbolList( nullptr ) 56 : : { 57 : : 58 : : // Construct exception message 59 [ + - ]: 234 : std::stringstream s; 60 : : s << m_message; 61 [ + + ]: 117 : if (line) 62 [ + - ]: 116 : s << "\n>>> Exception at " << m_file << ":" << m_line << ": " << m_func; 63 [ + - ]: 234 : m_message = s.str(); 64 : : 65 : : #ifdef EXCEPTIONS_WRITE_TO_CERR 66 : : // Uses streams (std::cerr) so it can be redirected 67 : : std::cerr << ">>> Exception: " << m_message << std::endl; 68 : : #endif 69 : : 70 : : // Save call-trace 71 : 117 : saveTrace(); 72 : : 73 : : } // Catch std::exception 74 : 0 : catch (exception& se) { 75 : : // Emit warning and continue 76 : 0 : fprintf( stderr, "RUNTIME ERROR in Exception constructor: %s\n", se.what() ); 77 : 0 : } 78 : : // Catch uncaught exceptions 79 : 0 : catch (...) { 80 : : // Emit warning and continue 81 : 0 : fprintf( stderr, "UNKNOWN EXCEPTION in Exception constructor\n" ); 82 : 117 : } 83 : : 84 : 117 : Exception::~Exception() noexcept 85 : : // ***************************************************************************** 86 : : // Destructor 87 : : //! \details Exception safety: no-throw guarantee: this member function never 88 : : //! throws exceptions. 89 : : // ***************************************************************************** 90 : : { 91 : : // allocated by execinfo.h's backtrace_symbols() in Exception::saveTrace() 92 [ + + ]: 117 : free(m_symbolList); 93 : 117 : } 94 : : 95 : : void 96 : 117 : Exception::saveTrace() noexcept 97 : : // ***************************************************************************** 98 : : // Save call-trace 99 : : //! \details Exception safety: no-throw guarantee: this member function never 100 : : //! throws exceptions. For more information see the libc manual at 101 : : //! http://www.gnu.org/software/libc/manual/html_node/Backtraces.html. 102 : : //! Requires stdio.h, execinfo.h. 103 : : // ***************************************************************************** 104 : : { 105 : : #ifndef HOST_OS_ALPINE 106 : : // Retrieve current stack addresses 107 : 117 : m_addrLength = backtrace(m_addrList, sizeof(m_addrList)/sizeof(void*)); 108 : : #endif 109 : : 110 : : // Resolve addresses into strings containing "filename(function+address)" 111 [ + - ]: 117 : if (m_addrLength > 0) 112 : 117 : m_symbolList = backtrace_symbols(m_addrList, m_addrLength); 113 : 117 : } 114 : : 115 : : void 116 : 0 : Exception::echoTrace() noexcept 117 : : // ***************************************************************************** 118 : : // Demangle and echo call trace 119 : : //! \details Exception safety: no-throw guarantee: this member function never 120 : : //! throws exceptions. Credit goes to Timo Bingmann at http://idlebox.net, 121 : : //! published under the WTFPL v2.0. For more information see 122 : : //! * http://panthema.net/2008/0901-stacktrace-demangled 123 : : //! * http://panthema.net/2008/0901-stacktrace-demangled/cxa_demangle.html 124 : : //! * http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen 125 : : // ***************************************************************************** 126 : : { 127 : : // Allocate string which will be filled with the demangled function name 128 : 0 : size_t funcnamesize = 256; 129 : 0 : char* funcname = static_cast< char* >( malloc(funcnamesize) ); 130 : : 131 : : // Iterate over the returned symbol lines. skip the first two, these are the 132 : : // addresses of Exception::saveTrace() and the Exception constructor 133 [ - - ]: 0 : for (int i=2; i<m_addrLength; ++i) { 134 : : char *begin_name = nullptr, *begin_offset = nullptr, *end_offset = nullptr; 135 : : 136 : : // Find parentheses and +address offset surrounding the mangled name: 137 : : // ./module(function+0x15c) [0x8048a6d] 138 [ - - ]: 0 : for (char *p = m_symbolList[i]; *p; ++p) { 139 [ - - ]: 0 : if (*p == '(') 140 : : begin_name = p; 141 [ - - ]: 0 : else if (*p == '+') 142 : : begin_offset = p; 143 [ - - ][ - - ]: 0 : else if (*p == ')' && begin_offset) { 144 : : end_offset = p; 145 : : break; 146 : : } 147 : : } 148 : : 149 [ - - ][ - - ]: 0 : if (begin_name && begin_offset && end_offset && begin_name < begin_offset) { 150 : 0 : *begin_name++ = '\0'; 151 : 0 : *begin_offset++ = '\0'; 152 : 0 : *end_offset = '\0'; 153 : : 154 : : // Mangled name is now in [begin_name, begin_offset) and caller 155 : : // offset in [begin_offset, end_offset). now apply __cxa_demangle() 156 : : int status; 157 : 0 : char* ret = abi::__cxa_demangle(begin_name, 158 : : funcname, &funcnamesize, &status); 159 : : 160 [ - - ]: 0 : if (status == 0) { 161 : : funcname = ret; // use possibly realloc()-ed string 162 : 0 : fprintf( stderr, ">>> %s : %s+%s\n", m_symbolList[i], funcname, 163 : : begin_offset ); 164 : : } else { 165 : : // Demangling failed. Output function name as a C function with no 166 : : // arguments 167 : 0 : fprintf( stderr, ">>> %s : %s()+%s\n", m_symbolList[i], begin_name, 168 : : begin_offset); 169 : 0 : } 170 : : } else { 171 : : // Couldn't parse the line? Print the whole line 172 : 0 : fprintf( stderr, ">>> %s\n", m_symbolList[i] ); 173 : : } 174 : : } 175 : : 176 : 0 : free(funcname); 177 : 0 : } 178 : : 179 : : tk::ErrCode 180 : 0 : Exception::handleException() noexcept 181 : : // ***************************************************************************** 182 : : // Handle Exception: Print cumulative message 183 : : //! \return Error code, as defined in stdlib.h, i.e., cstdlib 184 : : //! \details Exception safety: no-throw guarantee: this member function never 185 : : //! throws exceptions. 186 : : // ***************************************************************************** 187 : : { 188 [ - - ][ - - ]: 0 : if (m_addrLength > 0 && g_trace) { 189 : 0 : fprintf( stderr, ">>>\n>>> =========== CALL TRACE ===========\n>>>\n" ); 190 : 0 : echoTrace(); 191 : 0 : fprintf( stderr, ">>>\n>>> ======= END OF CALL TRACE ========\n>>>\n" ); 192 : : } 193 : : 194 : : #ifdef HAS_BACKWARD 195 [ - - ]: 0 : if (g_trace) { 196 : 0 : fprintf( stderr, ">>>\n>>> =========== STACK TRACE ==========\n>>>\n" ); 197 : : using namespace backward; 198 : 0 : StackTrace st; st.load_here(64); 199 : 0 : Printer p; p.print( st, stderr ); 200 : 0 : fprintf( stderr, ">>>\n>>> ======= END OF STACK TRACE =======\n>>>\n" ); 201 : : } 202 : : #endif 203 : : 204 : 0 : return tk::ErrCode::FAILURE; 205 : : }