Electroneum
Loading...
Searching...
No Matches
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
76extern "C"
77__attribute__((noreturn))
78void __real___cxa_throw(void *ex, CXA_THROW_INFO_T *info, void (*dest)(void*));
79#else // !STATICLIB
80#define CXA_THROW __cxa_throw
81extern "C"
82typedef
83#ifdef __clang__ // only clang, not GCC, lets apply the attr in typedef
84__attribute__((noreturn))
85#endif // __clang__
86void (cxa_throw_t)(void *ex, CXA_THROW_INFO_T *info, void (*dest)(void*));
87#endif // !STATICLIB
88
89extern "C"
90__attribute__((noreturn))
91void 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
108namespace
109{
110 std::string stack_trace_log;
111}
112
113namespace tools
114{
115
116void set_stack_trace_log(const std::string &log)
117{
118 stack_trace_log = log;
119}
120
121void 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
Various Tools.
Definition tools.cpp:31
void log_stack_trace(const char *msg)
void set_stack_trace_log(const std::string &log)
__attribute__((noreturn)) void CXA_THROW(void *ex
CXA_THROW_INFO_T void(* dest)(void *))
CXA_THROW_INFO_T * info
#define CXA_THROW_INFO_T
#define ST_LOG(x)
void cxa_throw_t(void *ex, CXA_THROW_INFO_T *info, void(*dest)(void *))
#define CXA_THROW