/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 | | 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 | | * Directs the output of logTrace() to directly stderr rather than the |
43 | | * logging infrastructure. |
44 | | * |
45 | | * This is intended for coverage tests, where we enable trace logs, but send |
46 | | * them to /dev/null to avoid accumulating too much data in CI. |
47 | | * |
48 | | * @param log_to_stderr Whether to log to stderr or the logging system. |
49 | | */ |
50 | | static void setLogToStderr(bool log_to_stderr); |
51 | | |
52 | | /** |
53 | | * @return whether the system directing backtraces directly to stderr. |
54 | | */ |
55 | 0 | static bool logToStderr() { return log_to_stderr_; } |
56 | | |
57 | | /** |
58 | | * Capture a stack trace. |
59 | | * |
60 | | * The trace will begin with the call to capture(). |
61 | | */ |
62 | 0 | void capture() { |
63 | | // Skip of one means we exclude the last call, which must be to capture(). |
64 | 0 | stack_depth_ = absl::GetStackTrace(stack_trace_, MaxStackDepth, /* skip_count = */ 1); |
65 | 0 | } |
66 | | |
67 | | /** |
68 | | * Capture a stack trace from a particular context. |
69 | | * |
70 | | * This can be used to capture a useful stack trace from a fatal signal |
71 | | * handler. The context argument should be a pointer to the context passed |
72 | | * to a signal handler registered via a sigaction struct. |
73 | | * |
74 | | * @param context A pointer to ucontext_t obtained from a sigaction handler. |
75 | | */ |
76 | 0 | void captureFrom(const void* context) { |
77 | 0 | stack_depth_ = |
78 | 0 | absl::GetStackTraceWithContext(stack_trace_, MaxStackDepth, /* skip_count = */ 1, context, |
79 | 0 | /* min_dropped_frames = */ nullptr); |
80 | 0 | } |
81 | | |
82 | | /** |
83 | | * Log the stack trace. |
84 | | */ |
85 | 0 | void logTrace() { |
86 | 0 | if (log_to_stderr_) { |
87 | 0 | printTrace(std::cerr); |
88 | 0 | return; |
89 | 0 | } |
90 | 0 |
|
91 | 0 | ENVOY_LOG(critical, "Backtrace (use tools/stack_decode.py to get line numbers):"); |
92 | 0 | ENVOY_LOG(critical, "Envoy version: {}", VersionInfo::version()); |
93 | 0 |
|
94 | 0 | visitTrace([](int index, const char* symbol, void* address) { |
95 | 0 | if (symbol != nullptr) { |
96 | 0 | ENVOY_LOG(critical, "#{}: {} [{}]", index, symbol, address); |
97 | 0 | } else { |
98 | 0 | ENVOY_LOG(critical, "#{}: [{}]", index, address); |
99 | 0 | } |
100 | 0 | }); |
101 | 0 | } |
102 | | |
103 | 0 | void logFault(const char* signame, const void* addr) { |
104 | 0 | ENVOY_LOG(critical, "Caught {}, suspect faulting address {}", signame, addr); |
105 | 0 | } |
106 | | |
107 | 0 | void printTrace(std::ostream& os) { |
108 | 0 | visitTrace([&](int index, const char* symbol, void* address) { |
109 | 0 | if (symbol != nullptr) { |
110 | 0 | os << "#" << index << " " << symbol << " [" << address << "]\n"; |
111 | 0 | } else { |
112 | 0 | os << "#" << index << " [" << address << "]\n"; |
113 | 0 | } |
114 | 0 | }); |
115 | 0 | } |
116 | | |
117 | | private: |
118 | | static bool log_to_stderr_; |
119 | | |
120 | | /** |
121 | | * Visit the previously captured stack trace. |
122 | | * |
123 | | * The visitor function is called once per frame, with 3 parameters: |
124 | | * 1. (int) The index of the current frame. |
125 | | * 2. (const char*) The symbol name for the address of the current frame. nullptr means |
126 | | * symbolization failed. |
127 | | * 3. (void*) The address of the current frame. |
128 | | */ |
129 | 0 | void visitTrace(const std::function<void(int, const char*, void*)>& visitor) { |
130 | 0 | for (int i = 0; i < stack_depth_; ++i) { |
131 | 0 | char out[1024]; |
132 | 0 | const bool success = absl::Symbolize(stack_trace_[i], out, sizeof(out)); |
133 | 0 | if (success) { |
134 | 0 | visitor(i, out, stack_trace_[i]); |
135 | 0 | } else { |
136 | 0 | visitor(i, nullptr, stack_trace_[i]); |
137 | 0 | } |
138 | 0 | } |
139 | 0 | } |
140 | | |
141 | | static constexpr int MaxStackDepth = 64; |
142 | | void* stack_trace_[MaxStackDepth]; |
143 | | int stack_depth_{0}; |
144 | | }; |
145 | | } // namespace Envoy |