|
| 1 | +// exceptions_impl.h: Rcpp R/C++ interface class library -- exceptions |
| 2 | +// |
| 3 | +// Copyright (C) 2020 Dirk Eddelbuettel, Romain Francois, and Joshua N. Pritikin |
| 4 | +// |
| 5 | +// This file is part of Rcpp. |
| 6 | +// |
| 7 | +// Rcpp is free software: you can redistribute it and/or modify it |
| 8 | +// under the terms of the GNU General Public License as published by |
| 9 | +// the Free Software Foundation, either version 2 of the License, or |
| 10 | +// (at your option) any later version. |
| 11 | +// |
| 12 | +// Rcpp is distributed in the hope that it will be useful, but |
| 13 | +// WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | +// GNU General Public License for more details. |
| 16 | +// |
| 17 | +// You should have received a copy of the GNU General Public License |
| 18 | +// along with Rcpp. If not, see <http://www.gnu.org/licenses/>. |
| 19 | + |
| 20 | +namespace Rcpp { |
| 21 | +#if defined(__GNUC__) || defined(__clang__) |
| 22 | + #if defined(_WIN32) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) || defined(__sun) || defined(_AIX) || defined(__MUSL__) || defined(__HAIKU__) || defined(__ANDROID__) |
| 23 | + // do nothing |
| 24 | + #else |
| 25 | + #include <execinfo.h> |
| 26 | + |
| 27 | + // Extract mangled name e.g. ./test(baz+0x14)[0x400962] |
| 28 | + static std::string demangler_one(const char* input) { |
| 29 | + static std::string buffer; |
| 30 | + buffer = input; |
| 31 | + size_t last_open = buffer.find_last_of('('); |
| 32 | + size_t last_close = buffer.find_last_of(')'); |
| 33 | + if (last_open == std::string::npos || |
| 34 | + last_close == std::string::npos) { |
| 35 | + return input; // #nocov |
| 36 | + } |
| 37 | + std::string function_name = buffer.substr(last_open + 1, last_close - last_open - 1); |
| 38 | + // Strip the +0x14 (if it exists, which it does not in earlier versions of gcc) |
| 39 | + size_t function_plus = function_name.find_last_of('+'); |
| 40 | + if (function_plus != std::string::npos) { |
| 41 | + function_name.resize(function_plus); |
| 42 | + } |
| 43 | + buffer.replace(last_open + 1, function_name.size(), demangle(function_name)); |
| 44 | + return buffer; |
| 45 | + } |
| 46 | + #endif |
| 47 | +#endif |
| 48 | + |
| 49 | + // thread-safe; invoked prior to throwing the exception |
| 50 | + inline void exception::record_stack_trace() |
| 51 | + { |
| 52 | + #if defined(__GNUC__) || defined(__clang__) |
| 53 | + #if defined(_WIN32) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) || defined(__sun) || defined(_AIX) || defined(__MUSL__) || defined(__HAIKU__) || defined(__ANDROID__) |
| 54 | + // C++ stack not available on this system |
| 55 | + #else // ! (defined(_WIN32) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) || defined(__sun) || defined(_AIX) || defined(__ANDROID__) |
| 56 | + |
| 57 | + /* inspired from http://tombarta.wordpress.com/2008/08/01/c-stack-traces-with-gcc/ */ |
| 58 | + const size_t max_depth = 100; |
| 59 | + int stack_depth; |
| 60 | + void *stack_addrs[max_depth]; |
| 61 | + |
| 62 | + stack_depth = backtrace(stack_addrs, max_depth); |
| 63 | + char **stack_strings = backtrace_symbols(stack_addrs, stack_depth); |
| 64 | + |
| 65 | + std::transform(stack_strings + 1, stack_strings + stack_depth, |
| 66 | + std::back_inserter(stack), demangler_one); |
| 67 | + free(stack_strings); // malloc()ed by backtrace_symbols |
| 68 | + #endif |
| 69 | + #endif |
| 70 | + } |
| 71 | + |
| 72 | + // not thread-safe; invoked after catching the exception |
| 73 | + inline void exception::copy_stack_trace_to_r() const |
| 74 | + { |
| 75 | + if (!stack.size()) { |
| 76 | + rcpp_set_stack_trace(R_NilValue); |
| 77 | + return; |
| 78 | + } |
| 79 | + |
| 80 | + CharacterVector res(stack.size()); |
| 81 | + std::copy(stack.begin(), stack.end(), res.begin()); |
| 82 | + List trace = List::create(_["file" ] = "", |
| 83 | + _["line" ] = -1, |
| 84 | + _["stack"] = res); |
| 85 | + trace.attr("class") = "Rcpp_stack_trace"; |
| 86 | + rcpp_set_stack_trace(trace); |
| 87 | + } |
| 88 | +}; |
0 commit comments