/proc/self/cwd/source/server/backtrace.h
Line | Count | Source (jump to first uncovered line) |
1 | | #pragma once |
2 | | |
3 | | #include <functional> |
4 | | |
5 | | #include "source/common/common/logger.h" |
6 | | #include "source/common/version/version.h" |
7 | | |
8 | | #include "absl/debugging/stacktrace.h" |
9 | | #include "absl/debugging/symbolize.h" |
10 | | |
11 | | namespace Envoy { |
12 | | #define BACKTRACE_LOG() \ |
13 | | do { \ |
14 | | ::Envoy::BackwardsTrace t; \ |
15 | | t.capture(); \ |
16 | | t.logTrace(); \ |
17 | | } while (0) |
18 | | |
19 | | /** |
20 | | * Use absl::Stacktrace and absl::Symbolize to log resolved symbols |
21 | | * stack traces on demand. To use this just do: |
22 | | * |
23 | | * BackwardsTrace tracer; |
24 | | * tracer.capture(); // Trace is captured as of here. |
25 | | * tracer.logTrace(); // Output the captured trace to the log. |
26 | | * |
27 | | * The capture and log steps are separated to enable debugging in the case where |
28 | | * you want to capture a stack trace from inside some logic but don't know whether |
29 | | * you want to bother logging it until later. |
30 | | * |
31 | | * For convenience a macro is provided BACKTRACE_LOG() which performs the |
32 | | * construction, capture, and log in one shot. |
33 | | * |
34 | | * If the symbols cannot be resolved by absl::Symbolize then the raw address |
35 | | * will be printed instead. |
36 | | */ |
37 | | class BackwardsTrace : Logger::Loggable<Logger::Id::backtrace> { |
38 | | public: |
39 | 0 | BackwardsTrace() = default; |
40 | | |
41 | | /** |
42 | | * Attempts to get the memory offsets of the current process, so the |
43 | | * stack trace addresses can be mapped to line numbers even after the |
44 | | * process is not running. |
45 | | * |
46 | | * This acts as a global singleton since it will be the same values for |
47 | | * the duration of an execution - as such, it should be called once |
48 | | * during startup, to avoid performing the lookup during the |
49 | | * crash process. |
50 | | * |
51 | | * @return a string representing the memory offset from `ASLR` of the |
52 | | * current process, or an empty string if the information is not |
53 | | * available. |
54 | | * The format of this line is |
55 | | * `[start_addr]-[end_addr] [path_to_binary]` |
56 | | * e.g. |
57 | | * `7d34c0e28000-7d34c1e0d000 /build/foo/bar/source/exe/envoy-static` |
58 | | */ |
59 | | static const std::string& addrMapping(bool setup = false); |
60 | | |
61 | | /** |
62 | | * Directs the output of logTrace() to directly stderr rather than the |
63 | | * logging infrastructure. |
64 | | * |
65 | | * This is intended for coverage tests, where we enable trace logs, but send |
66 | | * them to /dev/null to avoid accumulating too much data in CI. |
67 | | * |
68 | | * @param log_to_stderr Whether to log to stderr or the logging system. |
69 | | */ |
70 | | static void setLogToStderr(bool log_to_stderr); |
71 | | |
72 | | /** |
73 | | * @return whether the system directing backtraces directly to stderr. |
74 | | */ |
75 | 0 | static bool logToStderr() { return log_to_stderr_; } |
76 | | |
77 | | /** |
78 | | * Capture a stack trace. |
79 | | * |
80 | | * The trace will begin with the call to capture(). |
81 | | */ |
82 | 0 | void capture() { |
83 | | // Skip of one means we exclude the last call, which must be to capture(). |
84 | 0 | stack_depth_ = absl::GetStackTrace(stack_trace_, MaxStackDepth, /* skip_count = */ 1); |
85 | 0 | } |
86 | | |
87 | | /** |
88 | | * Capture a stack trace from a particular context. |
89 | | * |
90 | | * This can be used to capture a useful stack trace from a fatal signal |
91 | | * handler. The context argument should be a pointer to the context passed |
92 | | * to a signal handler registered via a sigaction struct. |
93 | | * |
94 | | * @param context A pointer to ucontext_t obtained from a sigaction handler. |
95 | | */ |
96 | 0 | void captureFrom(const void* context) { |
97 | 0 | stack_depth_ = |
98 | 0 | absl::GetStackTraceWithContext(stack_trace_, MaxStackDepth, /* skip_count = */ 1, context, |
99 | 0 | /* min_dropped_frames = */ nullptr); |
100 | 0 | } |
101 | | |
102 | | /** |
103 | | * Log the stack trace. |
104 | | */ |
105 | 0 | void logTrace() { |
106 | 0 | if (log_to_stderr_) { |
107 | 0 | printTrace(std::cerr); |
108 | 0 | return; |
109 | 0 | } |
110 | 0 |
|
111 | 0 | ENVOY_LOG(critical, "Backtrace (use tools/stack_decode.py to get line numbers):"); |
112 | 0 | ENVOY_LOG(critical, "Envoy version: {}", VersionInfo::version()); |
113 | 0 | if (!addrMapping().empty()) { |
114 | 0 | ENVOY_LOG(critical, "Address mapping: {}", addrMapping()); |
115 | 0 | } |
116 | 0 |
|
117 | 0 | visitTrace([](int index, const char* symbol, void* address) { |
118 | 0 | if (symbol != nullptr) { |
119 | 0 | ENVOY_LOG(critical, "#{}: {} [{}]", index, symbol, address); |
120 | 0 | } else { |
121 | 0 | ENVOY_LOG(critical, "#{}: [{}]", index, address); |
122 | 0 | } |
123 | 0 | }); |
124 | 0 | } |
125 | | |
126 | 0 | void logFault(const char* signame, const void* addr) { |
127 | 0 | ENVOY_LOG(critical, "Caught {}, suspect faulting address {}", signame, addr); |
128 | 0 | } |
129 | | |
130 | 0 | void printTrace(std::ostream& os) { |
131 | 0 | visitTrace([&](int index, const char* symbol, void* address) { |
132 | 0 | if (symbol != nullptr) { |
133 | 0 | os << "#" << index << " " << symbol << " [" << address << "]\n"; |
134 | 0 | } else { |
135 | 0 | os << "#" << index << " [" << address << "]\n"; |
136 | 0 | } |
137 | 0 | }); |
138 | 0 | } |
139 | | |
140 | | private: |
141 | | static bool log_to_stderr_; |
142 | | |
143 | | /** |
144 | | * Visit the previously captured stack trace. |
145 | | * |
146 | | * The visitor function is called once per frame, with 3 parameters: |
147 | | * 1. (int) The index of the current frame. |
148 | | * 2. (const char*) The symbol name for the address of the current frame. nullptr means |
149 | | * symbolization failed. |
150 | | * 3. (void*) The address of the current frame. |
151 | | */ |
152 | 0 | void visitTrace(const std::function<void(int, const char*, void*)>& visitor) { |
153 | 0 | for (int i = 0; i < stack_depth_; ++i) { |
154 | 0 | char out[1024]; |
155 | 0 | const bool success = absl::Symbolize(stack_trace_[i], out, sizeof(out)); |
156 | 0 | if (success) { |
157 | 0 | visitor(i, out, stack_trace_[i]); |
158 | 0 | } else { |
159 | 0 | visitor(i, nullptr, stack_trace_[i]); |
160 | 0 | } |
161 | 0 | } |
162 | 0 | } |
163 | | |
164 | | static constexpr int MaxStackDepth = 64; |
165 | | void* stack_trace_[MaxStackDepth]; |
166 | | int stack_depth_{0}; |
167 | | }; |
168 | | } // namespace Envoy |