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 : 262 : Exception::Exception( std::string&& message,
32 : : std::string&& file,
33 : : std::string&& function,
34 : 262 : 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 : 262 : m_file( std::move(file) ),
51 : 262 : m_func( std::move(function) ),
52 : 262 : m_line( std::move(line) ),
53 : 262 : m_message( std::move(message) ),
54 : : m_addrLength( 0 ),
55 : 524 : m_symbolList( nullptr )
56 : : {
57 : :
58 : : // Construct exception message
59 [ + - ]: 524 : std::stringstream s;
60 [ + - ]: 262 : s << m_message;
61 [ + + ]: 262 : if (line)
62 [ + - ][ + - ]: 261 : s << "\n>>> Exception at " << m_file << ":" << m_line << ": " << m_func;
[ + - ][ + - ]
[ + - ][ + - ]
63 [ + - ]: 262 : m_message = s.str();
64 : :
65 : : #ifdef EXCEPTIONS_WRITE_TO_CERR
66 : : // Uses streams (std::cerr) so it can be redirected
67 [ + - ][ + - ]: 262 : std::cerr << ">>> Exception: " << m_message << std::endl;
[ + - ]
68 : : #endif
69 : :
70 : : // Save call-trace
71 : 262 : 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 : 262 : }
83 : :
84 : 262 : 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 : 262 : free(m_symbolList);
93 : 262 : }
94 : :
95 : : void
96 : 262 : 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 : 262 : m_addrLength = backtrace(m_addrList, sizeof(m_addrList)/sizeof(void*));
108 : : #endif
109 : :
110 : : // Resolve addresses into strings containing "filename(function+address)"
111 [ + - ]: 262 : if (m_addrLength > 0)
112 : 262 : m_symbolList = backtrace_symbols(m_addrList, m_addrLength);
113 : 262 : }
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 : 0 : 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 : 0 : begin_name = p;
141 [ - - ]: 0 : else if (*p == '+')
142 : 0 : begin_offset = p;
143 [ - - ][ - - ]: 0 : else if (*p == ')' && begin_offset) {
144 : 0 : end_offset = p;
145 : 0 : 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 : 0 : 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 : : }
|