artdaq_core  v3_06_01
ExceptionStackTrace.cc
1 #include "TRACE/trace.h"
2 #define TRACE_NAME "ExceptionStackTrace"
3 
4 #include <cxxabi.h>
5 #include <dlfcn.h>
6 #include <execinfo.h>
7 #include <libgen.h>
8 #include <iostream>
9 #include <regex>
10 #include <sstream>
11 
12 #include "ExceptionStackTrace.hh"
13 
14 namespace artdaq::debug {
15 #define SKIP_HEAD 2
16 #define SKIP_TAIL 3
17 
18 StackTraceCollector& getStackTraceCollector()
19 {
20  static StackTraceCollector collector = StackTraceCollector();
21  return collector;
22 }
23 
24 std::string StackTrace::demangle(std::string const& symbol)
25 {
26  int status;
27  std::unique_ptr<char, void (*)(void*)> demangled_symbol(abi::__cxa_demangle(symbol.c_str(), nullptr, nullptr, &status), &std::free);
28  return status != 0 ? symbol : &*demangled_symbol;
29 }
30 
31 std::string Trace::print() const
32 {
33  std::ostringstream os;
34  os << '#' << index_ << " "
35  // << ' ' << std::setw(16) << address_ << " in "
36  << filename_ << " : " << function_ << " + "
37  << "0x" << std::hex << offset_;
38  return os.str();
39 }
40 
42 {
43  auto m = std::smatch();
44 
45  if (std::regex_search(symbol_, m, std::regex{"(\\S*)\\((|\\S+)\\)\\s\\[(0x\\S+)\\]"}) && m.size() == 4)
46  {
47  filename_ = m[1];
48  function_ = m[2];
49  address_ = static_cast<uintptr_t>(stoull(m[3], 0, 16));
50  if (std::regex_search(function_, m, std::regex{"(\\S+)\\+(\\S+)"}) && m.size() == 3)
51  {
52  std::string offstr = m[2];
53  function_ = StackTrace::demangle(m[1]);
54  offset_ = static_cast<uintptr_t>(stoull(offstr, 0, 16));
55  }
56  }
57  else //slow parse
58  {
59  if (std::regex_search(symbol_, m, std::regex{"(.*)\\("}) && m.size() == 2)
60  filename_ = m[1];
61 
62  if (std::regex_search(symbol_, m, std::regex{"[(](.*)[+]"}) && m.size() == 2)
63  function_ = StackTrace::demangle(m[1]);
64 
65  if (std::regex_search(symbol_, m, std::regex{"[+](0x\\S+)\\)"}) && m.size() == 2)
66  offset_ = static_cast<uintptr_t>(stoull(m[1], 0, 16));
67 
68  if (std::regex_search(symbol_, m, std::regex{"\\[(0x\\S+)\\]"}) && m.size() == 2)
69  address_ = static_cast<uintptr_t>(stoull(m[1], 0, 16));
70  }
71 }
72 
73 StackTrace::StackTrace(std::string type_name)
74  : type_name_{std::move(type_name)}, traces_uptr_{nullptr}, size_{::backtrace(frames_, sizeof frames_ / sizeof(void*))}
75 {
76 }
77 
79 {
80  traces_uptr_ = std::make_unique<traces_t>();
81 
82  if (0 == size_)
83  return;
84 
85  traces_uptr_->reserve(size_);
86 
87  char** symbols = ::backtrace_symbols(frames_, size_);
88 
89  for (auto i = SKIP_HEAD; i < size_ - SKIP_TAIL; i++)
90  traces_uptr_->emplace_back(size_ - SKIP_TAIL - i, symbols[size_ - i - 1]);
91 
92  free(symbols);
93 
94  for (auto& trace : *traces_uptr_)
95  trace.resolve();
96 }
97 
98 std::string StackTrace::print() const
99 {
100  if (!traces_uptr_)
101  return "Error: Unresolved StackTrace, call resolve() first.";
102 
103  if (0 == size_)
104  {
105  std::cout << "Error: possibly corrupt stack.";
106  }
107  std::ostringstream os;
108  os << "Caught a \"" << StackTrace::demangle(type_name_) << "\" exception.\n";
109 
110  os << "Stack Trace: \n";
111 
112  for (auto const& trace : *traces_uptr_)
113  os << trace << "\n";
114 
115  return os.str();
116 }
117 
118 } // namespace artdaq::debug
119 
120 extern "C" {
121 #ifndef __clang__
122 void __cxa_throw(void* ex, void* info, void (*dest)(void*))
123 {
124  artdaq::debug::getStackTraceCollector().collect_stacktrace(static_cast<std::type_info*>(info)->name());
125 
126  __cxa_throw_t* rethrow __attribute__((noreturn)) = (__cxa_throw_t*)dlsym(RTLD_NEXT, "__cxa_throw");
127 
128  rethrow(ex, info, dest);
129 }
130 #else
131 __attribute__((noreturn)) void __cxa_throw(void* ex, std::type_info* info, void (*dest)(void*))
132 {
133  artdaq::debug::getStackTraceCollector().collect_stacktrace(info->name());
134 
135  auto* rethrow = (__cxa_throw_t*)dlsym(RTLD_NEXT, "__cxa_throw");
136 
137  rethrow(ex, info, dest);
138 }
139 #endif
140 }
static std::string demangle(std::string const &symbol)
Demangles backtrace symbols.
void resolve()
Reads and demangles backtrace symbols.
StackTrace(std::string type_name)
Constructor.
void resolve()
Reads and demangles backtrace symbols.
std::string print() const
Produces a stack trace summary.
void collect_stacktrace(Args &&...args)
Adds a stacktrace to the stack_traces_ map.
std::string print() const
Produces a one-line summary.