1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// *****************************************************************************
/*!
  \file      src/Base/Exception.cpp
  \copyright 2012-2015 J. Bakosi,
             2016-2018 Los Alamos National Security, LLC.,
             2019-2021 Triad National Security, LLC.
             All rights reserved. See the LICENSE file for details.
  \brief     Exception class definition
  \details   Exception class definition
*/
// *****************************************************************************

#include <type_traits>
#include <cstdio>
#include <cxxabi.h>
#include <execinfo.h>
#include <sstream>
#include <iostream>

#include "QuinoaBuildConfig.hpp"
#include "Exception.hpp"

#ifdef HAS_BACKWARD
  #include "NoWarning/backward.hpp"
#endif

extern bool g_trace;

using tk::Exception;

Exception::Exception( std::string&& message,
                      std::string&& file,
                      std::string&& function,
                      unsigned int line ) noexcept
// *****************************************************************************
//  Constructor: generate error message
//! \param[in] message String (moved from) with an error message
//! \param[in] file String (moved from) with the file name in which the
//!   exception ocurred
//! \param[in] function String (moved from) containing the name of the function
//!   in which the exception ocurred
//! \param[in] line Source code line number at which the exception ocurred
//! \details While throwing exceptions is possible calling this constructor, the
//!   easiest and recommend way is to use the Assert, ErrChk, and Throw macros.
//!   Exception safety: no-throw guarantee: this member function never throws
//!   exceptions.
//! \see Assert, ErrChk, Throw
// *****************************************************************************
try :<--- syntax error: keyword 'try' is not allowed in global scope
  m_file( std::move(file) ),
  m_func( std::move(function) ),
  m_line( std::move(line) ),
  m_message( std::move(message) ),
  m_addrLength( 0 ),
  m_symbolList( nullptr )
{

  // Construct exception message
  std::stringstream s;
  s << m_message;
  if (line)
    s << "\n>>> Exception at " << m_file << ":" << m_line << ": " << m_func;
  m_message = s.str();

  #ifdef EXCEPTIONS_WRITE_TO_CERR
  // Uses streams (std::cerr) so it can be redirected
  std::cerr << ">>> Exception: " << m_message << std::endl;
  #endif

  // Save call-trace
  saveTrace();

} // Catch std::exception
  catch (exception& se) {
    // Emit warning and continue
    fprintf( stderr, "RUNTIME ERROR in Exception constructor: %s\n", se.what() );
  }
  // Catch uncaught exceptions
  catch (...) {
    // Emit warning and continue
    fprintf( stderr, "UNKNOWN EXCEPTION in Exception constructor\n" );
  }

Exception::~Exception() noexcept
// *****************************************************************************
//  Destructor
//! \details Exception safety: no-throw guarantee: this member function never
//!   throws exceptions.
// *****************************************************************************
{
  // allocated by execinfo.h's backtrace_symbols() in Exception::saveTrace()
  free(m_symbolList);
}

void
Exception::saveTrace() noexcept
// *****************************************************************************
//  Save call-trace
//! \details Exception safety: no-throw guarantee: this member function never
//!   throws exceptions. For more information see the libc manual at
//!   http://www.gnu.org/software/libc/manual/html_node/Backtraces.html.
//!   Requires stdio.h, execinfo.h.
// *****************************************************************************
{
#ifndef HOST_OS_ALPINE
  // Retrieve current stack addresses
  m_addrLength = backtrace(m_addrList, sizeof(m_addrList)/sizeof(void*));
#endif

  // Resolve addresses into strings containing "filename(function+address)"
  if (m_addrLength > 0)
    m_symbolList = backtrace_symbols(m_addrList, m_addrLength);
}

void
Exception::echoTrace() noexcept
// *****************************************************************************
//  Demangle and echo call trace
//! \details Exception safety: no-throw guarantee: this member function never
//!   throws exceptions. Credit goes to Timo Bingmann at http://idlebox.net,
//!   published under the WTFPL v2.0. For more information see
//!   * http://panthema.net/2008/0901-stacktrace-demangled
//!   * http://panthema.net/2008/0901-stacktrace-demangled/cxa_demangle.html
//!   * http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen
// *****************************************************************************
{
  // Allocate string which will be filled with the demangled function name
  size_t funcnamesize = 256;
  char* funcname = static_cast< char* >( malloc(funcnamesize) );

  // Iterate over the returned symbol lines. skip the first two, these are the
  // addresses of Exception::saveTrace() and the Exception constructor
  for (int i=2; i<m_addrLength; ++i) {
    char *begin_name = nullptr, *begin_offset = nullptr, *end_offset = nullptr;

    // Find parentheses and +address offset surrounding the mangled name:
    // ./module(function+0x15c) [0x8048a6d]
    for (char *p = m_symbolList[i]; *p; ++p) {
      if (*p == '(')
        begin_name = p;
      else if (*p == '+')
        begin_offset = p;
      else if (*p == ')' && begin_offset) {
        end_offset = p;
        break;
      }
    }

    if (begin_name && begin_offset && end_offset && begin_name < begin_offset) {
      *begin_name++ = '\0';
      *begin_offset++ = '\0';
      *end_offset = '\0';

      // Mangled name is now in [begin_name, begin_offset) and caller
      // offset in [begin_offset, end_offset). now apply __cxa_demangle()
      int status;
      char* ret = abi::__cxa_demangle(begin_name,
                                      funcname, &funcnamesize, &status);

      if (status == 0) {
        funcname = ret; // use possibly realloc()-ed string
        fprintf( stderr, ">>> %s : %s+%s\n", m_symbolList[i], funcname,
                         begin_offset );
      } else {
        // Demangling failed. Output function name as a C function with no
        // arguments
        fprintf( stderr, ">>> %s : %s()+%s\n", m_symbolList[i], begin_name,
                         begin_offset);
      }
    } else {
      // Couldn't parse the line? Print the whole line
      fprintf( stderr, ">>> %s\n", m_symbolList[i] );
    }
  }

  free(funcname);
}

tk::ErrCode
Exception::handleException() noexcept
// *****************************************************************************
//  Handle Exception: Print cumulative message
//! \return Error code, as defined in stdlib.h, i.e., cstdlib
//! \details Exception safety: no-throw guarantee: this member function never
//!   throws exceptions.
// *****************************************************************************
{
  if (m_addrLength > 0 && g_trace) {
    fprintf( stderr, ">>>\n>>> =========== CALL TRACE ===========\n>>>\n" );
    echoTrace();
    fprintf( stderr, ">>>\n>>> ======= END OF CALL TRACE ========\n>>>\n" );
  }

  #ifdef HAS_BACKWARD
  if (g_trace) {
    fprintf( stderr, ">>>\n>>> =========== STACK TRACE ==========\n>>>\n" );
    using namespace backward;
    StackTrace st; st.load_here(64);
    Printer p; p.print( st, stderr );
    fprintf( stderr, ">>>\n>>> ======= END OF STACK TRACE =======\n>>>\n" );
  }
  #endif
 
  return tk::ErrCode::FAILURE;
}