1
#pragma once
2

            
3
#include <functional>
4
#include <iostream>
5
#include <ostream>
6

            
7
#include "source/common/common/logger.h"
8
#include "source/common/version/version.h"
9

            
10
#include "absl/debugging/stacktrace.h"
11
#include "absl/debugging/symbolize.h"
12

            
13
namespace Envoy {
14
#define BACKTRACE_LOG()                                                                            \
15
  do {                                                                                             \
16
    ::Envoy::BackwardsTrace t;                                                                     \
17
    t.capture();                                                                                   \
18
    t.logTrace();                                                                                  \
19
  } while (0)
20

            
21
/**
22
 * Use absl::Stacktrace and absl::Symbolize to log resolved symbols
23
 * stack traces on demand. To use this just do:
24
 *
25
 * BackwardsTrace tracer;
26
 * tracer.capture(); // Trace is captured as of here.
27
 * tracer.logTrace(); // Output the captured trace to the log.
28
 *
29
 * The capture and log steps are separated to enable debugging in the case where
30
 * you want to capture a stack trace from inside some logic but don't know whether
31
 * you want to bother logging it until later.
32
 *
33
 * For convenience a macro is provided BACKTRACE_LOG() which performs the
34
 * construction, capture, and log in one shot.
35
 *
36
 * If the symbols cannot be resolved by absl::Symbolize then the raw address
37
 * will be printed instead.
38
 */
39
class BackwardsTrace : Logger::Loggable<Logger::Id::backtrace> {
40
public:
41
3
  BackwardsTrace() = default;
42

            
43
  /**
44
   * Attempts to get the memory offsets of the current process, so the
45
   * stack trace addresses can be mapped to line numbers even after the
46
   * process is not running.
47
   *
48
   * This acts as a global singleton since it will be the same values for
49
   * the duration of an execution - as such, it should be called once
50
   * during startup, to avoid performing the lookup during the
51
   * crash process.
52
   *
53
   * @return a string representing the memory offset from `ASLR` of the
54
   * current process, or an empty string if the information is not
55
   * available.
56
   * The format of this line is
57
   *   `[start_addr]-[end_addr] [path_to_binary]`
58
   * e.g.
59
   *   `7d34c0e28000-7d34c1e0d000 /build/foo/bar/source/exe/envoy-static`
60
   */
61
  static absl::string_view addrMapping(bool setup = false);
62

            
63
  /**
64
   * Directs the output of logTrace() to directly stderr rather than the
65
   * logging infrastructure.
66
   *
67
   * This is intended for coverage tests, where we enable trace logs, but send
68
   * them to /dev/null to avoid accumulating too much data in CI.
69
   *
70
   * @param log_to_stderr Whether to log to stderr or the logging system.
71
   */
72
  static void setLogToStderr(bool log_to_stderr);
73

            
74
  /**
75
   * @return whether the system directing backtraces directly to stderr.
76
   */
77
1
  static bool logToStderr() { return log_to_stderr_; }
78

            
79
  /**
80
   * Capture a stack trace.
81
   *
82
   * The trace will begin with the call to capture().
83
   */
84
2
  void capture() {
85
    // Skip of one means we exclude the last call, which must be to capture().
86
2
    stack_depth_ = absl::GetStackTrace(stack_trace_, MaxStackDepth, /* skip_count = */ 1);
87
2
  }
88

            
89
  /**
90
   * Capture a stack trace from a particular context.
91
   *
92
   * This can be used to capture a useful stack trace from a fatal signal
93
   * handler. The context argument should be a pointer to the context passed
94
   * to a signal handler registered via a sigaction struct.
95
   *
96
   * @param context A pointer to ucontext_t obtained from a sigaction handler.
97
   */
98
  void captureFrom(const void* context) {
99
    stack_depth_ =
100
        absl::GetStackTraceWithContext(stack_trace_, MaxStackDepth, /* skip_count = */ 1, context,
101
                                       /* min_dropped_frames = */ nullptr);
102
  }
103

            
104
  /**
105
   * Log the stack trace.
106
   */
107
3
  void logTrace() {
108
3
    if (log_to_stderr_) {
109
2
      printTrace(std::cerr);
110
2
      return;
111
2
    }
112

            
113
1
    ENVOY_LOG(critical, "Backtrace (use tools/stack_decode.py to get line numbers):");
114
1
    ENVOY_LOG(critical, "Envoy version: {}", VersionInfo::version());
115
1
    if (!addrMapping().empty()) {
116
      ENVOY_LOG(critical, "Address mapping: {}", addrMapping());
117
    }
118

            
119
14
    visitTrace([](int index, const char* symbol, void* address) {
120
14
      if (symbol != nullptr) {
121
        ENVOY_LOG(critical, "#{}: {} [{}]", index, symbol, address);
122
14
      } else {
123
14
        ENVOY_LOG(critical, "#{}: [{}]", index, address);
124
14
      }
125
14
    });
126
1
  }
127

            
128
1
  void logFault(const char* signame, const void* addr) {
129
1
    ENVOY_LOG(critical, "Caught {}, suspect faulting address {}", signame, addr);
130
1
  }
131

            
132
2
  void printTrace(std::ostream& os) {
133
16
    visitTrace([&](int index, const char* symbol, void* address) {
134
15
      if (symbol != nullptr) {
135
        os << "#" << index << " " << symbol << " [" << address << "]\n";
136
15
      } else {
137
15
        os << "#" << index << " [" << address << "]\n";
138
15
      }
139
15
    });
140
2
  }
141

            
142
private:
143
  static bool log_to_stderr_;
144

            
145
  /**
146
   * Visit the previously captured stack trace.
147
   *
148
   * The visitor function is called once per frame, with 3 parameters:
149
   * 1. (int) The index of the current frame.
150
   * 2. (const char*) The symbol name for the address of the current frame. nullptr means
151
   * symbolization failed.
152
   * 3. (void*) The address of the current frame.
153
   */
154
3
  void visitTrace(const std::function<void(int, const char*, void*)>& visitor) {
155
32
    for (int i = 0; i < stack_depth_; ++i) {
156
29
      char out[1024];
157
29
      const bool success = absl::Symbolize(stack_trace_[i], out, sizeof(out));
158
29
      if (success) {
159
        visitor(i, out, stack_trace_[i]);
160
29
      } else {
161
29
        visitor(i, nullptr, stack_trace_[i]);
162
29
      }
163
29
    }
164
3
  }
165

            
166
  static constexpr int MaxStackDepth = 64;
167
  void* stack_trace_[MaxStackDepth];
168
  int stack_depth_{0};
169
};
170
} // namespace Envoy