Electroneum
stack_trace.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017-Present, Electroneum
2 // Copyright (c) 2016-2019, The Monero Project
3 //
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without modification, are
7 // permitted provided that the following conditions are met:
8 //
9 // 1. Redistributions of source code must retain the above copyright notice, this list of
10 // conditions and the following disclaimer.
11 //
12 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
13 // of conditions and the following disclaimer in the documentation and/or other
14 // materials provided with the distribution.
15 //
16 // 3. Neither the name of the copyright holder nor the names of its contributors may be
17 // used to endorse or promote products derived from this software without specific
18 // prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #if !defined __GNUC__ || defined __MINGW32__ || defined __MINGW64__ || defined __ANDROID__
31 #define USE_UNWIND
32 #else
33 #define ELPP_FEATURE_CRASH_LOG 1
34 #endif
36 
37 #include <stdexcept>
38 #ifdef USE_UNWIND
39 #define UNW_LOCAL_ONLY
40 #include <libunwind.h>
41 #include <cxxabi.h>
42 #endif
43 #ifndef STATICLIB
44 #include <dlfcn.h>
45 #endif
46 #include <boost/algorithm/string.hpp>
47 #include "common/stack_trace.h"
48 #include "misc_log_ex.h"
49 
50 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
51 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "stacktrace"
52 
53 #define ST_LOG(x) \
54  do { \
55  auto elpp = ELPP; \
56  if (elpp) { \
57  CINFO(el::base::Writer,el::base::DispatchAction::FileOnlyLog,ELECTRONEUM_DEFAULT_LOG_CATEGORY) << x; \
58  } \
59  else { \
60  std::cout << x << std::endl; \
61  } \
62  } while(0)
63 
64 // from https://stackoverflow.com/questions/11665829/how-can-i-print-stack-trace-for-caught-exceptions-in-c-code-injection-in-c
65 
66 // The decl of __cxa_throw in /usr/include/.../cxxabi.h uses
67 // 'std::type_info *', but GCC's built-in protype uses 'void *'.
68 #ifdef __clang__
69 #define CXA_THROW_INFO_T std::type_info
70 #else // !__clang__
71 #define CXA_THROW_INFO_T void
72 #endif // !__clang__
73 
74 #ifdef STATICLIB
75 #define CXA_THROW __wrap___cxa_throw
76 extern "C"
77 __attribute__((noreturn))
78 void __real___cxa_throw(void *ex, CXA_THROW_INFO_T *info, void (*dest)(void*));
79 #else // !STATICLIB
80 #define CXA_THROW __cxa_throw
81 extern "C"
82 typedef
83 #ifdef __clang__ // only clang, not GCC, lets apply the attr in typedef
84 __attribute__((noreturn))
85 #endif // __clang__
86 void (cxa_throw_t)(void *ex, CXA_THROW_INFO_T *info, void (*dest)(void*));
87 #endif // !STATICLIB
88 
89 extern "C"
90 __attribute__((noreturn))
91 void CXA_THROW(void *ex, CXA_THROW_INFO_T *info, void (*dest)(void*))
92 {
93 
94  int status;
95  char *dsym = abi::__cxa_demangle(((const std::type_info*)info)->name(), NULL, NULL, &status);
96  tools::log_stack_trace((std::string("Exception: ")+((!status && dsym) ? dsym : (const char*)info)).c_str());
97  free(dsym);
98 
99 #ifndef STATICLIB
100 #ifndef __clang__ // for GCC the attr can't be applied in typedef like for clang
101  __attribute__((noreturn))
102 #endif // !__clang__
103  cxa_throw_t *__real___cxa_throw = (cxa_throw_t*)dlsym(RTLD_NEXT, "__cxa_throw");
104 #endif // !STATICLIB
105  __real___cxa_throw(ex, info, dest);
106 }
107 
108 namespace
109 {
110  std::string stack_trace_log;
111 }
112 
113 namespace tools
114 {
115 
117 {
118  stack_trace_log = log;
119 }
120 
121 void log_stack_trace(const char *msg)
122 {
123 #ifdef USE_UNWIND
124  unw_context_t ctx;
125  unw_cursor_t cur;
126  unw_word_t ip, off;
127  unsigned level;
128  char sym[512], *dsym;
129  int status;
130  const char *log = stack_trace_log.empty() ? NULL : stack_trace_log.c_str();
131 #endif
132 
133  if (msg)
134  ST_LOG(msg);
135  ST_LOG("Unwound call stack:");
136 
137 #ifdef USE_UNWIND
138  if (unw_getcontext(&ctx) < 0) {
139  ST_LOG("Failed to create unwind context");
140  return;
141  }
142  if (unw_init_local(&cur, &ctx) < 0) {
143  ST_LOG("Failed to find the first unwind frame");
144  return;
145  }
146  for (level = 1; level < 999; ++level) { // 999 for safety
147  int ret = unw_step(&cur);
148  if (ret < 0) {
149  ST_LOG("Failed to find the next frame");
150  return;
151  }
152  if (ret == 0)
153  break;
154  if (unw_get_reg(&cur, UNW_REG_IP, &ip) < 0) {
155  ST_LOG(" " << std::setw(4) << level);
156  continue;
157  }
158  if (unw_get_proc_name(&cur, sym, sizeof(sym), &off) < 0) {
159  ST_LOG(" " << std::setw(4) << level << std::setbase(16) << std::setw(20) << "0x" << ip);
160  continue;
161  }
162  dsym = abi::__cxa_demangle(sym, NULL, NULL, &status);
163  ST_LOG(" " << std::setw(4) << level << std::setbase(16) << std::setw(20) << "0x" << ip << " " << (!status && dsym ? dsym : sym) << " + " << "0x" << off);
164  free(dsym);
165  }
166 #else
167  std::stringstream ss;
168  ss << el::base::debug::StackTrace();
169  std::vector<std::string> lines;
170  std::string s = ss.str();
171  boost::split(lines, s, boost::is_any_of("\n"));
172  for (const auto &line: lines)
173  ST_LOG(line);
174 #endif
175 }
176 
177 } // namespace tools
void set_stack_trace_log(const std::string &log)
#define CXA_THROW_INFO_T
Definition: stack_trace.cpp:71
#define ST_LOG(x)
Definition: stack_trace.cpp:53
#define CXA_THROW
Definition: stack_trace.cpp:80
::std::string string
Definition: gtest-port.h:1097
CXA_THROW_INFO_T void(* dest)(void *))
Definition: stack_trace.cpp:91
void() cxa_throw_t(void *ex, CXA_THROW_INFO_T *info, void(*dest)(void *))
Definition: stack_trace.cpp:86
const char * name
Various Tools.
Definition: tools.cpp:31
boost::endian::big_uint32_t ip
Definition: socks.cpp:61
CXA_THROW_INFO_T * info
Definition: stack_trace.cpp:91
__attribute__((noreturn)) void CXA_THROW(void *ex
void log_stack_trace(const char *msg)