Coverage Report

Created: 2024-09-19 09:45

/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