forked from envoyproxy/envoy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
backtrace.h
168 lines (150 loc) · 5.65 KB
/
backtrace.h
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
#pragma once
#include <functional>
#include "source/common/common/logger.h"
#include "source/common/version/version.h"
#include "absl/debugging/stacktrace.h"
#include "absl/debugging/symbolize.h"
namespace Envoy {
#define BACKTRACE_LOG() \
do { \
::Envoy::BackwardsTrace t; \
t.capture(); \
t.logTrace(); \
} while (0)
/**
* Use absl::Stacktrace and absl::Symbolize to log resolved symbols
* stack traces on demand. To use this just do:
*
* BackwardsTrace tracer;
* tracer.capture(); // Trace is captured as of here.
* tracer.logTrace(); // Output the captured trace to the log.
*
* The capture and log steps are separated to enable debugging in the case where
* you want to capture a stack trace from inside some logic but don't know whether
* you want to bother logging it until later.
*
* For convenience a macro is provided BACKTRACE_LOG() which performs the
* construction, capture, and log in one shot.
*
* If the symbols cannot be resolved by absl::Symbolize then the raw address
* will be printed instead.
*/
class BackwardsTrace : Logger::Loggable<Logger::Id::backtrace> {
public:
BackwardsTrace() = default;
/**
* Attempts to get the memory offsets of the current process, so the
* stack trace addresses can be mapped to line numbers even after the
* process is not running.
*
* This acts as a global singleton since it will be the same values for
* the duration of an execution - as such, it should be called once
* during startup, to avoid performing the lookup during the
* crash process.
*
* @return a string representing the memory offset from `ASLR` of the
* current process, or an empty string if the information is not
* available.
* The format of this line is
* `[start_addr]-[end_addr] [path_to_binary]`
* e.g.
* `7d34c0e28000-7d34c1e0d000 /build/foo/bar/source/exe/envoy-static`
*/
static const std::string& addrMapping(bool setup = false);
/**
* Directs the output of logTrace() to directly stderr rather than the
* logging infrastructure.
*
* This is intended for coverage tests, where we enable trace logs, but send
* them to /dev/null to avoid accumulating too much data in CI.
*
* @param log_to_stderr Whether to log to stderr or the logging system.
*/
static void setLogToStderr(bool log_to_stderr);
/**
* @return whether the system directing backtraces directly to stderr.
*/
static bool logToStderr() { return log_to_stderr_; }
/**
* Capture a stack trace.
*
* The trace will begin with the call to capture().
*/
void capture() {
// Skip of one means we exclude the last call, which must be to capture().
stack_depth_ = absl::GetStackTrace(stack_trace_, MaxStackDepth, /* skip_count = */ 1);
}
/**
* Capture a stack trace from a particular context.
*
* This can be used to capture a useful stack trace from a fatal signal
* handler. The context argument should be a pointer to the context passed
* to a signal handler registered via a sigaction struct.
*
* @param context A pointer to ucontext_t obtained from a sigaction handler.
*/
void captureFrom(const void* context) {
stack_depth_ =
absl::GetStackTraceWithContext(stack_trace_, MaxStackDepth, /* skip_count = */ 1, context,
/* min_dropped_frames = */ nullptr);
}
/**
* Log the stack trace.
*/
void logTrace() {
if (log_to_stderr_) {
printTrace(std::cerr);
return;
}
ENVOY_LOG(critical, "Backtrace (use tools/stack_decode.py to get line numbers):");
ENVOY_LOG(critical, "Envoy version: {}", VersionInfo::version());
if (!addrMapping().empty()) {
ENVOY_LOG(critical, "Address mapping: {}", addrMapping());
}
visitTrace([](int index, const char* symbol, void* address) {
if (symbol != nullptr) {
ENVOY_LOG(critical, "#{}: {} [{}]", index, symbol, address);
} else {
ENVOY_LOG(critical, "#{}: [{}]", index, address);
}
});
}
void logFault(const char* signame, const void* addr) {
ENVOY_LOG(critical, "Caught {}, suspect faulting address {}", signame, addr);
}
void printTrace(std::ostream& os) {
visitTrace([&](int index, const char* symbol, void* address) {
if (symbol != nullptr) {
os << "#" << index << " " << symbol << " [" << address << "]\n";
} else {
os << "#" << index << " [" << address << "]\n";
}
});
}
private:
static bool log_to_stderr_;
/**
* Visit the previously captured stack trace.
*
* The visitor function is called once per frame, with 3 parameters:
* 1. (int) The index of the current frame.
* 2. (const char*) The symbol name for the address of the current frame. nullptr means
* symbolization failed.
* 3. (void*) The address of the current frame.
*/
void visitTrace(const std::function<void(int, const char*, void*)>& visitor) {
for (int i = 0; i < stack_depth_; ++i) {
char out[1024];
const bool success = absl::Symbolize(stack_trace_[i], out, sizeof(out));
if (success) {
visitor(i, out, stack_trace_[i]);
} else {
visitor(i, nullptr, stack_trace_[i]);
}
}
}
static constexpr int MaxStackDepth = 64;
void* stack_trace_[MaxStackDepth];
int stack_depth_{0};
};
} // namespace Envoy