forked from envoyproxy/envoy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbacktrace.h
More file actions
216 lines (194 loc) · 7.31 KB
/
Copy pathbacktrace.h
File metadata and controls
216 lines (194 loc) · 7.31 KB
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#pragma once
#include <algorithm>
#include <functional>
#include <iostream>
#include <ostream>
#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;
/**
* Construct a trace directly from raw frame pointers that were captured
* elsewhere. The number of frames copied is clamped to MaxStackDepth.
*
* @param frames Pointer to an array of captured frame addresses.
* @param depth Number of valid entries in @p frames.
*/
BackwardsTrace(void* const* frames, int depth) {
stack_depth_ = std::min(depth, MaxStackDepth);
std::copy(frames, frames + stack_depth_, stack_trace_);
}
/**
* 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 absl::string_view 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_; }
/**
* Directs all stack trace output to be formatted as a single log line
* rather than one line per frame. This makes stack traces easier to
* consume in log aggregation systems.
*
* @param single_line Whether to log the entire stack trace on a single line.
*/
static void setSingleLine(bool single_line);
/**
* @return whether stack traces are formatted as a single line.
*/
static bool singleLine() { return single_line_; }
/**
* 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;
}
if (single_line_) {
std::string buf = fmt::format(
"Backtrace (use tools/stack_decode.py to get line numbers):\nEnvoy version: {}",
VersionInfo::version());
if (!addrMapping().empty()) {
fmt::format_to(std::back_inserter(buf), "\nAddress mapping: {}", addrMapping());
}
visitTrace([&buf](int index, const char* symbol, void* address) {
if (symbol != nullptr) {
fmt::format_to(std::back_inserter(buf), "\n#{}: {} [{}]", index, symbol, address);
} else {
fmt::format_to(std::back_inserter(buf), "\n#{}: [{}]", index, address);
}
});
ENVOY_LOG(critical, "{}", buf);
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_;
static bool single_line_;
/**
* 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