Coverage Report

Created: 2023-11-12 09:30

/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