folly: Symbolizer: don't allocate from function which is supposed to be signal safe
[folly.git] / folly / experimental / symbolizer / Symbolizer.h
1 /*
2  * Copyright 2015 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 #ifndef FOLLY_EXPERIMENTAL_SYMBOLIZER_SYMBOLIZER_H_
18 #define FOLLY_EXPERIMENTAL_SYMBOLIZER_SYMBOLIZER_H_
19
20 #include <array>
21 #include <cstdint>
22 #include <memory>
23 #include <string>
24
25 #include <folly/FBString.h>
26 #include <folly/Range.h>
27 #include <folly/String.h>
28 #include <folly/io/IOBuf.h>
29 #include <folly/experimental/symbolizer/Elf.h>
30 #include <folly/experimental/symbolizer/ElfCache.h>
31 #include <folly/experimental/symbolizer/Dwarf.h>
32 #include <folly/experimental/symbolizer/StackTrace.h>
33
34 namespace folly {
35 namespace symbolizer {
36
37 class Symbolizer;
38
39 /**
40  * Frame information: symbol name and location.
41  */
42 struct SymbolizedFrame {
43   SymbolizedFrame() { }
44
45   void set(const std::shared_ptr<ElfFile>& file, uintptr_t address);
46   void clear() { *this = SymbolizedFrame(); }
47
48   bool found = false;
49   const char* name = nullptr;
50   Dwarf::LocationInfo location;
51
52   /**
53    * Demangle the name and return it. Not async-signal-safe; allocates memory.
54    */
55   fbstring demangledName() const {
56     return name ? demangle(name) : fbstring();
57   }
58  private:
59   std::shared_ptr<ElfFile> file_;
60 };
61
62 template <size_t N>
63 struct FrameArray {
64   FrameArray() { }
65
66   size_t frameCount = 0;
67   uintptr_t addresses[N];
68   SymbolizedFrame frames[N];
69 };
70
71 /**
72  * Get stack trace into a given FrameArray, return true on success (and
73  * set frameCount to the actual frame count, which may be > N) and false
74  * on failure.
75  */
76 namespace detail {
77 template <size_t N>
78 bool fixFrameArray(FrameArray<N>& fa, ssize_t n) {
79   if (n != -1) {
80     fa.frameCount = n;
81     for (size_t i = 0; i < fa.frameCount; ++i) {
82       fa.frames[i].found = false;
83     }
84     return true;
85   } else {
86     fa.frameCount = 0;
87     return false;
88   }
89 }
90 }  // namespace detail
91
92 // Always inline these functions; they don't do much, and unittests rely
93 // on them never showing up in a stack trace.
94 template <size_t N>
95 FOLLY_ALWAYS_INLINE bool getStackTrace(FrameArray<N>& fa);
96
97 template <size_t N>
98 inline bool getStackTrace(FrameArray<N>& fa) {
99   return detail::fixFrameArray(fa, getStackTrace(fa.addresses, N));
100 }
101 template <size_t N>
102 FOLLY_ALWAYS_INLINE bool getStackTraceSafe(FrameArray<N>& fa);
103
104 template <size_t N>
105 inline bool getStackTraceSafe(FrameArray<N>& fa) {
106   return detail::fixFrameArray(fa, getStackTraceSafe(fa.addresses, N));
107 }
108
109 class Symbolizer {
110  public:
111   explicit Symbolizer(ElfCacheBase* cache = nullptr);
112
113   /**
114    * Symbolize given addresses.
115    */
116   void symbolize(const uintptr_t* addresses,
117                  SymbolizedFrame* frames,
118                  size_t frameCount);
119
120   template <size_t N>
121   void symbolize(FrameArray<N>& fa) {
122     symbolize(fa.addresses, fa.frames, fa.frameCount);
123   }
124
125   /**
126    * Shortcut to symbolize one address.
127    */
128   bool symbolize(uintptr_t address, SymbolizedFrame& frame) {
129     symbolize(&address, &frame, 1);
130     return frame.found;
131   }
132
133  private:
134   ElfCacheBase* const cache_ = nullptr;
135 };
136
137 /**
138  * Format one address in the way it's usually printer by SymbolizePrinter.
139  * Async-signal-safe.
140  */
141 class AddressFormatter {
142  public:
143   AddressFormatter();
144
145   /**
146    * Format the address. Returns an internal buffer.
147    */
148   StringPiece format(uintptr_t address);
149
150  private:
151   static constexpr char bufTemplate[] = "    @ 0000000000000000";
152   char buf_[sizeof(bufTemplate)];
153 };
154
155 /**
156  * Print a list of symbolized addresses. Base class.
157  */
158 class SymbolizePrinter {
159  public:
160   /**
161    * Print one address, no ending newline.
162    */
163   void print(uintptr_t address, const SymbolizedFrame& frame);
164
165   /**
166    * Print one address with ending newline.
167    */
168   void println(uintptr_t address, const SymbolizedFrame& frame);
169
170   /**
171    * Print multiple addresses on separate lines.
172    */
173   void println(const uintptr_t* addresses,
174                const SymbolizedFrame* frames,
175                size_t frameCount);
176
177   /**
178    * Print a string, no endling newline.
179    */
180   void print(StringPiece sp) { doPrint(sp); }
181
182   /**
183    * Print multiple addresses on separate lines, skipping the first
184    * skip addresses.
185    */
186   template <size_t N>
187   void println(const FrameArray<N>& fa, size_t skip=0) {
188     if (skip < fa.frameCount) {
189       println(fa.addresses + skip, fa.frames + skip, fa.frameCount - skip);
190     }
191   }
192
193   virtual ~SymbolizePrinter() { }
194
195   enum Options {
196     // Skip file and line information
197     NO_FILE_AND_LINE = 1 << 0,
198
199     // As terse as it gets: function name if found, address otherwise
200     TERSE = 1 << 1,
201
202     // Always colorize output (ANSI escape code)
203     COLOR = 1 << 2,
204
205     // Colorize output only if output is printed to a TTY (ANSI escape code)
206     COLOR_IF_TTY = 1 << 3,
207   };
208
209   // NOTE: enum values used as indexes in kColorMap.
210   enum Color { DEFAULT, RED, GREEN, YELLOW, BLUE, CYAN, WHITE, PURPLE, NUM };
211   void color(Color c);
212
213  protected:
214   explicit SymbolizePrinter(int options, bool isTty = false)
215     : options_(options),
216       isTty_(isTty) {
217   }
218
219   const int options_;
220   const bool isTty_;
221
222  private:
223   void printTerse(uintptr_t address, const SymbolizedFrame& frame);
224   virtual void doPrint(StringPiece sp) = 0;
225
226   static constexpr std::array<const char*, Color::NUM> kColorMap = {{
227       "\x1B[0m",
228       "\x1B[31m",
229       "\x1B[32m",
230       "\x1B[33m",
231       "\x1B[34m",
232       "\x1B[36m",
233       "\x1B[37m",
234       "\x1B[35m",
235   }};
236 };
237
238 /**
239  * Print a list of symbolized addresses to a stream.
240  * Not reentrant. Do not use from signal handling code.
241  */
242 class OStreamSymbolizePrinter : public SymbolizePrinter {
243  public:
244   explicit OStreamSymbolizePrinter(std::ostream& out, int options=0);
245  private:
246   void doPrint(StringPiece sp) override;
247   std::ostream& out_;
248 };
249
250 /**
251  * Print a list of symbolized addresses to a file descriptor.
252  * Ignores errors. Async-signal-safe.
253  */
254 class FDSymbolizePrinter : public SymbolizePrinter {
255  public:
256   explicit FDSymbolizePrinter(int fd, int options=0,
257                               size_t bufferSize=0);
258   ~FDSymbolizePrinter();
259   void flush();
260  private:
261   void doPrint(StringPiece sp) override;
262
263   const int fd_;
264   std::unique_ptr<IOBuf> buffer_;
265 };
266
267 /**
268  * Print a list of symbolized addresses to a FILE*.
269  * Ignores errors. Not reentrant. Do not use from signal handling code.
270  */
271 class FILESymbolizePrinter : public SymbolizePrinter {
272  public:
273   explicit FILESymbolizePrinter(FILE* file, int options=0);
274  private:
275   void doPrint(StringPiece sp) override;
276   FILE* const file_ = nullptr;
277 };
278
279 /**
280  * Print a list of symbolized addresses to a std::string.
281  * Not reentrant. Do not use from signal handling code.
282  */
283 class StringSymbolizePrinter : public SymbolizePrinter {
284  public:
285   explicit StringSymbolizePrinter(int options=0) : SymbolizePrinter(options) { }
286
287   std::string str() const { return buf_.toStdString(); }
288   const fbstring& fbstr() const { return buf_; }
289   fbstring moveFbString() { return std::move(buf_); }
290
291  private:
292   void doPrint(StringPiece sp) override;
293   fbstring buf_;
294 };
295
296 }  // namespace symbolizer
297 }  // namespace folly
298
299 #endif /* FOLLY_EXPERIMENTAL_SYMBOLIZER_SYMBOLIZER_H_ */