Line data Source code
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 0 : // 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 : 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 : 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