Lines
73.2 %
Functions
90 %
#pragma once
#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;
* 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_; }
* 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) {
os << "#" << index << " " << symbol << " [" << address << "]\n";
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]);
visitor(i, nullptr, stack_trace_[i]);
static constexpr int MaxStackDepth = 64;
void* stack_trace_[MaxStackDepth];
int stack_depth_{0};
};
} // namespace Envoy