2c906b6e39bdbec036a19c00705e5f8a1530ea22
[folly.git] / folly / experimental / symbolizer / Symbolizer.h
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/Range.h>
26 #include <folly/String.h>
27 #include <folly/experimental/symbolizer/Dwarf.h>
28 #include <folly/experimental/symbolizer/Elf.h>
29 #include <folly/experimental/symbolizer/ElfCache.h>
30 #include <folly/experimental/symbolizer/StackTrace.h>
31 #include <folly/io/IOBuf.h>
32
33 namespace folly {
34 namespace symbolizer {
35
36 class Symbolizer;
37
38 /**
39  * Frame information: symbol name and location.
40  */
41 struct SymbolizedFrame {
42   SymbolizedFrame() {}
43
44   void set(
45       const std::shared_ptr<ElfFile>& file,
46       uintptr_t address,
47       Dwarf::LocationInfoMode mode);
48
49   void clear() {
50     *this = SymbolizedFrame();
51   }
52
53   bool found = false;
54   const char* name = nullptr;
55   Dwarf::LocationInfo location;
56
57   /**
58    * Demangle the name and return it. Not async-signal-safe; allocates memory.
59    */
60   fbstring demangledName() const {
61     return name ? demangle(name) : fbstring();
62   }
63
64  private:
65   std::shared_ptr<ElfFile> file_;
66 };
67
68 template <size_t N>
69 struct FrameArray {
70   FrameArray() {}
71
72   size_t frameCount = 0;
73   uintptr_t addresses[N];
74   SymbolizedFrame frames[N];
75 };
76
77 /**
78  * Get stack trace into a given FrameArray, return true on success (and
79  * set frameCount to the actual frame count, which may be > N) and false
80  * on failure.
81  */
82 namespace detail {
83 template <size_t N>
84 bool fixFrameArray(FrameArray<N>& fa, ssize_t n) {
85   if (n != -1) {
86     fa.frameCount = n;
87     for (size_t i = 0; i < fa.frameCount; ++i) {
88       fa.frames[i].found = false;
89     }
90     return true;
91   } else {
92     fa.frameCount = 0;
93     return false;
94   }
95 }
96 } // namespace detail
97
98 // Always inline these functions; they don't do much, and unittests rely
99 // on them never showing up in a stack trace.
100 template <size_t N>
101 FOLLY_ALWAYS_INLINE bool getStackTrace(FrameArray<N>& fa);
102
103 template <size_t N>
104 inline bool getStackTrace(FrameArray<N>& fa) {
105   return detail::fixFrameArray(fa, getStackTrace(fa.addresses, N));
106 }
107 template <size_t N>
108 FOLLY_ALWAYS_INLINE bool getStackTraceSafe(FrameArray<N>& fa);
109
110 template <size_t N>
111 inline bool getStackTraceSafe(FrameArray<N>& fa) {
112   return detail::fixFrameArray(fa, getStackTraceSafe(fa.addresses, N));
113 }
114
115 class Symbolizer {
116  public:
117   static constexpr Dwarf::LocationInfoMode kDefaultLocationInfoMode =
118       Dwarf::LocationInfoMode::FAST;
119
120   explicit Symbolizer(Dwarf::LocationInfoMode mode = kDefaultLocationInfoMode)
121       : Symbolizer(nullptr, mode) {}
122
123   explicit Symbolizer(
124       ElfCacheBase* cache,
125       Dwarf::LocationInfoMode mode = kDefaultLocationInfoMode);
126
127   /**
128    * Symbolize given addresses.
129    */
130   void symbolize(
131       const uintptr_t* addresses,
132       SymbolizedFrame* frames,
133       size_t frameCount);
134
135   template <size_t N>
136   void symbolize(FrameArray<N>& fa) {
137     symbolize(fa.addresses, fa.frames, fa.frameCount);
138   }
139
140   /**
141    * Shortcut to symbolize one address.
142    */
143   bool symbolize(uintptr_t address, SymbolizedFrame& frame) {
144     symbolize(&address, &frame, 1);
145     return frame.found;
146   }
147
148  private:
149   ElfCacheBase* const cache_;
150   const Dwarf::LocationInfoMode mode_;
151 };
152
153 /**
154  * Format one address in the way it's usually printed by SymbolizePrinter.
155  * Async-signal-safe.
156  */
157 class AddressFormatter {
158  public:
159   AddressFormatter();
160
161   /**
162    * Format the address. Returns an internal buffer.
163    */
164   StringPiece format(uintptr_t address);
165
166  private:
167   static constexpr char bufTemplate[] = "    @ 0000000000000000";
168   char buf_[sizeof(bufTemplate)];
169 };
170
171 /**
172  * Print a list of symbolized addresses. Base class.
173  */
174 class SymbolizePrinter {
175  public:
176   /**
177    * Print one address, no ending newline.
178    */
179   void print(uintptr_t address, const SymbolizedFrame& frame);
180
181   /**
182    * Print one address with ending newline.
183    */
184   void println(uintptr_t address, const SymbolizedFrame& frame);
185
186   /**
187    * Print multiple addresses on separate lines.
188    */
189   void println(
190       const uintptr_t* addresses,
191       const SymbolizedFrame* frames,
192       size_t frameCount);
193
194   /**
195    * Print a string, no endling newline.
196    */
197   void print(StringPiece sp) {
198     doPrint(sp);
199   }
200
201   /**
202    * Print multiple addresses on separate lines, skipping the first
203    * skip addresses.
204    */
205   template <size_t N>
206   void println(const FrameArray<N>& fa, size_t skip = 0) {
207     if (skip < fa.frameCount) {
208       println(fa.addresses + skip, fa.frames + skip, fa.frameCount - skip);
209     }
210   }
211
212   virtual ~SymbolizePrinter() {}
213
214   enum Options {
215     // Skip file and line information
216     NO_FILE_AND_LINE = 1 << 0,
217
218     // As terse as it gets: function name if found, address otherwise
219     TERSE = 1 << 1,
220
221     // Always colorize output (ANSI escape code)
222     COLOR = 1 << 2,
223
224     // Colorize output only if output is printed to a TTY (ANSI escape code)
225     COLOR_IF_TTY = 1 << 3,
226
227     // Skip frame address information
228     NO_FRAME_ADDRESS = 1 << 4,
229   };
230
231   // NOTE: enum values used as indexes in kColorMap.
232   enum Color { DEFAULT, RED, GREEN, YELLOW, BLUE, CYAN, WHITE, PURPLE, NUM };
233   void color(Color c);
234
235  protected:
236   explicit SymbolizePrinter(int options, bool isTty = false)
237       : options_(options), isTty_(isTty) {}
238
239   const int options_;
240   const bool isTty_;
241
242  private:
243   void printTerse(uintptr_t address, const SymbolizedFrame& frame);
244   virtual void doPrint(StringPiece sp) = 0;
245
246   static constexpr std::array<const char*, Color::NUM> kColorMap = {{
247       "\x1B[0m",
248       "\x1B[31m",
249       "\x1B[32m",
250       "\x1B[33m",
251       "\x1B[34m",
252       "\x1B[36m",
253       "\x1B[37m",
254       "\x1B[35m",
255   }};
256 };
257
258 /**
259  * Print a list of symbolized addresses to a stream.
260  * Not reentrant. Do not use from signal handling code.
261  */
262 class OStreamSymbolizePrinter : public SymbolizePrinter {
263  public:
264   explicit OStreamSymbolizePrinter(std::ostream& out, int options = 0);
265
266  private:
267   void doPrint(StringPiece sp) override;
268   std::ostream& out_;
269 };
270
271 /**
272  * Print a list of symbolized addresses to a file descriptor.
273  * Ignores errors. Async-signal-safe.
274  */
275 class FDSymbolizePrinter : public SymbolizePrinter {
276  public:
277   explicit FDSymbolizePrinter(int fd, int options = 0, size_t bufferSize = 0);
278   ~FDSymbolizePrinter() override;
279   void flush();
280
281  private:
282   void doPrint(StringPiece sp) override;
283
284   const int fd_;
285   std::unique_ptr<IOBuf> buffer_;
286 };
287
288 /**
289  * Print a list of symbolized addresses to a FILE*.
290  * Ignores errors. Not reentrant. Do not use from signal handling code.
291  */
292 class FILESymbolizePrinter : public SymbolizePrinter {
293  public:
294   explicit FILESymbolizePrinter(FILE* file, int options = 0);
295
296  private:
297   void doPrint(StringPiece sp) override;
298   FILE* const file_ = nullptr;
299 };
300
301 /**
302  * Print a list of symbolized addresses to a std::string.
303  * Not reentrant. Do not use from signal handling code.
304  */
305 class StringSymbolizePrinter : public SymbolizePrinter {
306  public:
307   explicit StringSymbolizePrinter(int options = 0)
308       : SymbolizePrinter(options) {}
309
310   std::string str() const {
311     return buf_.toStdString();
312   }
313   const fbstring& fbstr() const {
314     return buf_;
315   }
316   fbstring moveFbString() {
317     return std::move(buf_);
318   }
319
320  private:
321   void doPrint(StringPiece sp) override;
322   fbstring buf_;
323 };
324
325 /**
326  * Use this class to print a stack trace from a signal handler, or other place
327  * where you shouldn't allocate memory on the heap, and fsync()ing your file
328  * descriptor is more important than performance.
329  *
330  * Make sure to create one of these on startup, not in the signal handler, as
331  * the constructo allocates on the heap, whereas the other methods don't.  Best
332  * practice is to just leak this object, rather than worry about destruction
333  * order.
334  *
335  * These methods aren't thread safe, so if you could have signals on multiple
336  * threads at the same time, you need to do your own locking to ensure you don't
337  * call these methods from multiple threads.  They are signal safe, however.
338  */
339 class StackTracePrinter {
340  public:
341   static constexpr size_t kDefaultMinSignalSafeElfCacheSize = 500;
342
343   explicit StackTracePrinter(
344       size_t minSignalSafeElfCacheSize = kDefaultMinSignalSafeElfCacheSize,
345       int fd = STDERR_FILENO);
346
347   /**
348    * Only allocates on the stack and is signal-safe but not thread-safe.  Don't
349    * call printStackTrace() on the same StackTracePrinter object from multiple
350    * threads at the same time.
351    */
352   FOLLY_NOINLINE void printStackTrace(bool symbolize);
353
354   void print(StringPiece sp) {
355     printer_.print(sp);
356   }
357
358   // Flush printer_, also fsync, in case we're about to crash again...
359   void flush();
360
361  private:
362   static constexpr size_t kMaxStackTraceDepth = 100;
363
364   int fd_;
365   SignalSafeElfCache elfCache_;
366   FDSymbolizePrinter printer_;
367   std::unique_ptr<FrameArray<kMaxStackTraceDepth>> addresses_;
368 };
369
370 } // namespace symbolizer
371 } // namespace folly