1/*
2 * Copyright 2012-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include <array>
20#include <cstdint>
21#include <memory>
22#include <string>
23
24#include <folly/FBString.h>
25#include <folly/Optional.h>
26#include <folly/Range.h>
27#include <folly/String.h>
28#include <folly/Synchronized.h>
29#include <folly/container/EvictingCacheMap.h>
30#include <folly/experimental/symbolizer/Dwarf.h>
31#include <folly/experimental/symbolizer/Elf.h>
32#include <folly/experimental/symbolizer/ElfCache.h>
33#include <folly/experimental/symbolizer/StackTrace.h>
34#include <folly/io/IOBuf.h>
35
36namespace folly {
37namespace symbolizer {
38
39class Symbolizer;
40
41/**
42 * Frame information: symbol name and location.
43 */
44struct SymbolizedFrame {
45 SymbolizedFrame() {}
46
47 void set(
48 const std::shared_ptr<ElfFile>& file,
49 uintptr_t address,
50 Dwarf::LocationInfoMode mode);
51
52 void clear() {
53 *this = SymbolizedFrame();
54 }
55
56 bool found = false;
57 const char* name = nullptr;
58 Dwarf::LocationInfo location;
59
60 /**
61 * Demangle the name and return it. Not async-signal-safe; allocates memory.
62 */
63 fbstring demangledName() const {
64 return name ? demangle(name) : fbstring();
65 }
66
67 private:
68 std::shared_ptr<ElfFile> file_;
69};
70
71template <size_t N>
72struct FrameArray {
73 FrameArray() {}
74
75 size_t frameCount = 0;
76 uintptr_t addresses[N];
77 SymbolizedFrame frames[N];
78};
79
80/**
81 * Get stack trace into a given FrameArray, return true on success (and
82 * set frameCount to the actual frame count, which may be > N) and false
83 * on failure.
84 */
85namespace detail {
86template <size_t N>
87bool fixFrameArray(FrameArray<N>& fa, ssize_t n) {
88 if (n != -1) {
89 fa.frameCount = n;
90 for (size_t i = 0; i < fa.frameCount; ++i) {
91 fa.frames[i].found = false;
92 }
93 return true;
94 } else {
95 fa.frameCount = 0;
96 return false;
97 }
98}
99} // namespace detail
100
101// Always inline these functions; they don't do much, and unittests rely
102// on them never showing up in a stack trace.
103template <size_t N>
104FOLLY_ALWAYS_INLINE bool getStackTrace(FrameArray<N>& fa);
105
106template <size_t N>
107inline bool getStackTrace(FrameArray<N>& fa) {
108 return detail::fixFrameArray(fa, getStackTrace(fa.addresses, N));
109}
110template <size_t N>
111FOLLY_ALWAYS_INLINE bool getStackTraceSafe(FrameArray<N>& fa);
112
113template <size_t N>
114inline bool getStackTraceSafe(FrameArray<N>& fa) {
115 return detail::fixFrameArray(fa, getStackTraceSafe(fa.addresses, N));
116}
117
118class Symbolizer {
119 public:
120 static constexpr Dwarf::LocationInfoMode kDefaultLocationInfoMode =
121 Dwarf::LocationInfoMode::FAST;
122
123 explicit Symbolizer(Dwarf::LocationInfoMode mode = kDefaultLocationInfoMode)
124 : Symbolizer(nullptr, mode) {}
125
126 explicit Symbolizer(
127 ElfCacheBase* cache,
128 Dwarf::LocationInfoMode mode = kDefaultLocationInfoMode,
129 size_t symbolCacheSize = 0);
130 /**
131 * Symbolize given addresses.
132 */
133 void symbolize(
134 const uintptr_t* addresses,
135 SymbolizedFrame* frames,
136 size_t frameCount);
137
138 template <size_t N>
139 void symbolize(FrameArray<N>& fa) {
140 symbolize(fa.addresses, fa.frames, fa.frameCount);
141 }
142
143 /**
144 * Shortcut to symbolize one address.
145 */
146 bool symbolize(uintptr_t address, SymbolizedFrame& frame) {
147 symbolize(&address, &frame, 1);
148 return frame.found;
149 }
150
151 private:
152 ElfCacheBase* const cache_;
153 const Dwarf::LocationInfoMode mode_;
154
155 using SymbolCache = EvictingCacheMap<uintptr_t, SymbolizedFrame>;
156 folly::Optional<Synchronized<SymbolCache>> symbolCache_;
157};
158
159/**
160 * Format one address in the way it's usually printed by SymbolizePrinter.
161 * Async-signal-safe.
162 */
163class AddressFormatter {
164 public:
165 AddressFormatter();
166
167 /**
168 * Format the address. Returns an internal buffer.
169 */
170 StringPiece format(uintptr_t address);
171
172 private:
173 static constexpr char bufTemplate[] = " @ 0000000000000000";
174 char buf_[sizeof(bufTemplate)];
175};
176
177/**
178 * Print a list of symbolized addresses. Base class.
179 */
180class SymbolizePrinter {
181 public:
182 /**
183 * Print one address, no ending newline.
184 */
185 void print(uintptr_t address, const SymbolizedFrame& frame);
186
187 /**
188 * Print one address with ending newline.
189 */
190 void println(uintptr_t address, const SymbolizedFrame& frame);
191
192 /**
193 * Print multiple addresses on separate lines.
194 */
195 void println(
196 const uintptr_t* addresses,
197 const SymbolizedFrame* frames,
198 size_t frameCount);
199
200 /**
201 * Print a string, no endling newline.
202 */
203 void print(StringPiece sp) {
204 doPrint(sp);
205 }
206
207 /**
208 * Print multiple addresses on separate lines, skipping the first
209 * skip addresses.
210 */
211 template <size_t N>
212 void println(const FrameArray<N>& fa, size_t skip = 0) {
213 if (skip < fa.frameCount) {
214 println(fa.addresses + skip, fa.frames + skip, fa.frameCount - skip);
215 }
216 }
217
218 /**
219 * If output buffered inside this class, send it to the output stream, so that
220 * any output done in other ways appears after this.
221 */
222 virtual void flush() {}
223
224 virtual ~SymbolizePrinter() {}
225
226 enum Options {
227 // Skip file and line information
228 NO_FILE_AND_LINE = 1 << 0,
229
230 // As terse as it gets: function name if found, address otherwise
231 TERSE = 1 << 1,
232
233 // Always colorize output (ANSI escape code)
234 COLOR = 1 << 2,
235
236 // Colorize output only if output is printed to a TTY (ANSI escape code)
237 COLOR_IF_TTY = 1 << 3,
238
239 // Skip frame address information
240 NO_FRAME_ADDRESS = 1 << 4,
241 };
242
243 // NOTE: enum values used as indexes in kColorMap.
244 enum Color { DEFAULT, RED, GREEN, YELLOW, BLUE, CYAN, WHITE, PURPLE, NUM };
245 void color(Color c);
246
247 protected:
248 explicit SymbolizePrinter(int options, bool isTty = false)
249 : options_(options), isTty_(isTty) {}
250
251 const int options_;
252 const bool isTty_;
253
254 private:
255 void printTerse(uintptr_t address, const SymbolizedFrame& frame);
256 virtual void doPrint(StringPiece sp) = 0;
257
258 static constexpr std::array<const char*, Color::NUM> kColorMap = {{
259 "\x1B[0m",
260 "\x1B[31m",
261 "\x1B[32m",
262 "\x1B[33m",
263 "\x1B[34m",
264 "\x1B[36m",
265 "\x1B[37m",
266 "\x1B[35m",
267 }};
268};
269
270/**
271 * Print a list of symbolized addresses to a stream.
272 * Not reentrant. Do not use from signal handling code.
273 */
274class OStreamSymbolizePrinter : public SymbolizePrinter {
275 public:
276 explicit OStreamSymbolizePrinter(std::ostream& out, int options = 0);
277
278 private:
279 void doPrint(StringPiece sp) override;
280 std::ostream& out_;
281};
282
283/**
284 * Print a list of symbolized addresses to a file descriptor.
285 * Ignores errors. Async-signal-safe.
286 */
287class FDSymbolizePrinter : public SymbolizePrinter {
288 public:
289 explicit FDSymbolizePrinter(int fd, int options = 0, size_t bufferSize = 0);
290 ~FDSymbolizePrinter() override;
291 virtual void flush() override;
292
293 private:
294 void doPrint(StringPiece sp) override;
295
296 const int fd_;
297 std::unique_ptr<IOBuf> buffer_;
298};
299
300/**
301 * Print a list of symbolized addresses to a FILE*.
302 * Ignores errors. Not reentrant. Do not use from signal handling code.
303 */
304class FILESymbolizePrinter : public SymbolizePrinter {
305 public:
306 explicit FILESymbolizePrinter(FILE* file, int options = 0);
307
308 private:
309 void doPrint(StringPiece sp) override;
310 FILE* const file_ = nullptr;
311};
312
313/**
314 * Print a list of symbolized addresses to a std::string.
315 * Not reentrant. Do not use from signal handling code.
316 */
317class StringSymbolizePrinter : public SymbolizePrinter {
318 public:
319 explicit StringSymbolizePrinter(int options = 0)
320 : SymbolizePrinter(options) {}
321
322 std::string str() const {
323 return buf_.toStdString();
324 }
325 const fbstring& fbstr() const {
326 return buf_;
327 }
328 fbstring moveFbString() {
329 return std::move(buf_);
330 }
331
332 private:
333 void doPrint(StringPiece sp) override;
334 fbstring buf_;
335};
336
337/**
338 * Use this class to print a stack trace from a signal handler, or other place
339 * where you shouldn't allocate memory on the heap, and fsync()ing your file
340 * descriptor is more important than performance.
341 *
342 * Make sure to create one of these on startup, not in the signal handler, as
343 * the constructor allocates on the heap, whereas the other methods don't. Best
344 * practice is to just leak this object, rather than worry about destruction
345 * order.
346 *
347 * These methods aren't thread safe, so if you could have signals on multiple
348 * threads at the same time, you need to do your own locking to ensure you don't
349 * call these methods from multiple threads. They are signal safe, however.
350 */
351class SafeStackTracePrinter {
352 public:
353 static constexpr size_t kDefaultMinSignalSafeElfCacheSize = 500;
354
355 explicit SafeStackTracePrinter(
356 size_t minSignalSafeElfCacheSize = kDefaultMinSignalSafeElfCacheSize,
357 int fd = STDERR_FILENO);
358
359 virtual ~SafeStackTracePrinter() {}
360
361 /**
362 * Only allocates on the stack and is signal-safe but not thread-safe. Don't
363 * call printStackTrace() on the same StackTracePrinter object from multiple
364 * threads at the same time.
365 *
366 * This is NOINLINE to make sure it shows up in the stack we grab, which makes
367 * it easy to skip printing it.
368 */
369 FOLLY_NOINLINE void printStackTrace(bool symbolize);
370
371 void print(StringPiece sp) {
372 printer_.print(sp);
373 }
374
375 // Flush printer_, also fsync, in case we're about to crash again...
376 void flush();
377
378 protected:
379 virtual void printSymbolizedStackTrace();
380
381 private:
382 static constexpr size_t kMaxStackTraceDepth = 100;
383
384 int fd_;
385 SignalSafeElfCache elfCache_;
386 FDSymbolizePrinter printer_;
387 std::unique_ptr<FrameArray<kMaxStackTraceDepth>> addresses_;
388};
389
390/**
391 * Use this class to print a stack trace from normal code. It will malloc and
392 * won't flush or sync.
393 *
394 * These methods are thread safe, through locking. However, they are not signal
395 * safe.
396 */
397class FastStackTracePrinter {
398 public:
399 static constexpr size_t kDefaultSymbolCacheSize = 10000;
400
401 explicit FastStackTracePrinter(
402 std::unique_ptr<SymbolizePrinter> printer,
403 size_t elfCacheSize = 0, // 0 means "use the default elf cache instance."
404 size_t symbolCacheSize = kDefaultSymbolCacheSize);
405
406 ~FastStackTracePrinter();
407
408 /**
409 * This is NOINLINE to make sure it shows up in the stack we grab, which makes
410 * it easy to skip printing it.
411 */
412 FOLLY_NOINLINE void printStackTrace(bool symbolize);
413
414 void flush();
415
416 private:
417 static constexpr size_t kMaxStackTraceDepth = 100;
418
419 const std::unique_ptr<ElfCache> elfCache_;
420 const std::unique_ptr<SymbolizePrinter> printer_;
421 Symbolizer symbolizer_;
422};
423
424/**
425 * Use this class in rare situations where signal handlers are running in a
426 * tiny stack specified by sigaltstack.
427 *
428 * This is neither thread-safe nor signal-safe. However, it can usually print
429 * something useful while SafeStackTracePrinter would stack overflow.
430 *
431 * Signal handlers would need to block other signals to make this safer.
432 * Note it's still unsafe even with that.
433 */
434class UnsafeSelfAllocateStackTracePrinter : public SafeStackTracePrinter {
435 protected:
436 void printSymbolizedStackTrace() override;
437 const long pageSizeUnchecked_ = sysconf(_SC_PAGESIZE);
438};
439
440} // namespace symbolizer
441} // namespace folly
442