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 : 167 : Exception::Exception( std::string&& message,
27 : : std::string&& file,
28 : : std::string&& function,
29 : 167 : 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 : 167 : m_file( std::move(file) ),
47 : 167 : m_func( std::move(function) ),
48 : 167 : m_line( std::move(line) ),
49 : 167 : m_message( std::move(message) ),
50 : 167 : m_addrLength( 0 ),
51 : 167 : m_symbolList( nullptr )
52 : : {
53 : :
54 : : // Construct exception message
55 [ + - ]: 167 : std::stringstream s;
56 [ + - ]: 167 : s << m_message;
57 [ + + ]: 167 : if (line)
58 [ + - ][ + - ]: 164 : s << "\n>>> Exception at " << m_file << ":" << m_line << ": " << m_func;
[ + - ][ + - ]
[ + - ][ + - ]
59 [ + - ]: 167 : m_message = s.str();
60 : :
61 : : #ifdef EXCEPTIONS_WRITE_TO_CERR
62 : : // Uses streams (std::cerr) so it can be redirected
63 [ + - ][ + - ]: 167 : std::cerr << ">>> Exception: " << m_message << std::endl;
[ + - ]
64 : : #endif
65 : :
66 : : // Save call-trace
67 : 167 : saveTrace();
68 : :
69 : 167 : } // 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 : 167 : }
79 : :
80 : 495 : 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 : 165 : free(m_symbolList);
89 : 165 : }
90 : :
91 : : void
92 : 167 : 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 : 167 : m_addrLength = backtrace(m_addrList, sizeof(m_addrList)/sizeof(void*));
103 : :
104 : : // Resolve addresses into strings containing "filename(function+address)"
105 [ + - ]: 167 : if (m_addrLength > 0)
106 : 167 : m_symbolList = backtrace_symbols(m_addrList, m_addrLength);
107 : 167 : }
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 [ + + ]: 47 : for (int i=2; i<m_addrLength; ++i) {
128 : 44 : 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 [ + - ]: 3222 : for (char *p = m_symbolList[i]; *p; ++p) {
133 [ + + ]: 3222 : if (*p == '(')
134 : 44 : begin_name = p;
135 [ + + ]: 3178 : else if (*p == '+')
136 : 50 : begin_offset = p;
137 [ + + ][ + - ]: 3128 : else if (*p == ')' && begin_offset) {
138 : 44 : end_offset = p;
139 : 44 : break;
140 : : }
141 : : }
142 : :
143 [ + - ][ + - ]: 44 : if (begin_name && begin_offset && end_offset && begin_name < begin_offset) {
[ + - ][ + - ]
144 : 44 : *begin_name++ = '\0';
145 : 44 : *begin_offset++ = '\0';
146 : 44 : *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 : 44 : char* ret = abi::__cxa_demangle(begin_name,
152 : : funcname, &funcnamesize, &status);
153 : :
154 [ + + ]: 44 : if (status == 0) {
155 : 15 : funcname = ret; // use possibly realloc()-ed string
156 : 15 : 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 : 29 : fprintf( stderr, ">>> %s : %s()+%s\n", m_symbolList[i], begin_name,
162 : : begin_offset);
163 : : }
164 : 44 : } 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 : : }
|