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