folly: symbolizer: small terse write fix + colorize signal handler output if printing...
[folly.git] / folly / experimental / symbolizer / Symbolizer.h
1 /*
2  * Copyright 2014 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 <cstdint>
21 #include <string>
22 #include <unordered_map>
23
24 #include "folly/FBString.h"
25 #include "folly/Range.h"
26 #include "folly/experimental/symbolizer/Elf.h"
27 #include "folly/experimental/symbolizer/Dwarf.h"
28 #include "folly/experimental/symbolizer/StackTrace.h"
29
30 namespace folly {
31 namespace symbolizer {
32
33 /**
34  * Frame information: symbol name and location.
35  *
36  * Note that both name and location are references in the Symbolizer object,
37  * which must outlive this SymbolizedFrame object.
38  */
39 struct SymbolizedFrame {
40   SymbolizedFrame() : found(false) { }
41   bool isSignalFrame;
42   bool found;
43   StringPiece name;
44   Dwarf::LocationInfo location;
45 };
46
47 template <size_t N>
48 struct FrameArray {
49   FrameArray() : frameCount(0) { }
50
51   size_t frameCount;
52   uintptr_t addresses[N];
53   SymbolizedFrame frames[N];
54 };
55
56 /**
57  * Get stack trace into a given FrameArray, return true on success (and
58  * set frameCount to the actual frame count, which may be > N) and false
59  * on failure.
60  */
61 namespace detail {
62 template <size_t N>
63 bool fixFrameArray(FrameArray<N>& fa, ssize_t n) {
64   if (n != -1) {
65     fa.frameCount = n;
66     for (size_t i = 0; i < fa.frameCount; ++i) {
67       fa.frames[i].found = false;
68     }
69     return true;
70   } else {
71     fa.frameCount = 0;
72     return false;
73   }
74 }
75 }  // namespace detail
76
77 template <size_t N>
78 bool getStackTrace(FrameArray<N>& fa) {
79   return detail::fixFrameArray(fa, getStackTrace(fa.addresses, N));
80 }
81
82 template <size_t N>
83 bool getStackTraceSafe(FrameArray<N>& fa) {
84   return detail::fixFrameArray(fa, getStackTraceSafe(fa.addresses, N));
85 }
86
87 class Symbolizer {
88  public:
89   Symbolizer() : fileCount_(0) { }
90
91   /**
92    * Symbolize given addresses.
93    */
94   void symbolize(const uintptr_t* addresses,
95                  SymbolizedFrame* frames,
96                  size_t frameCount);
97
98   template <size_t N>
99   void symbolize(FrameArray<N>& fa) {
100     symbolize(fa.addresses, fa.frames, fa.frameCount);
101   }
102
103   /**
104    * Shortcut to symbolize one address.
105    */
106   bool symbolize(uintptr_t address, SymbolizedFrame& frame) {
107     symbolize(&address, &frame, 1);
108     return frame.found;
109   }
110
111  private:
112   // We can't allocate memory, so we'll preallocate room.
113   // "1023 shared libraries should be enough for everyone"
114   static constexpr size_t kMaxFiles = 1024;
115   size_t fileCount_;
116   ElfFile files_[kMaxFiles];
117 };
118
119 /**
120  * Print a list of symbolized addresses. Base class.
121  */
122 class SymbolizePrinter {
123  public:
124   /**
125    * Print one address, no ending newline.
126    */
127   void print(uintptr_t address, const SymbolizedFrame& frame);
128
129   /**
130    * Print one address with ending newline.
131    */
132   void println(uintptr_t address, const SymbolizedFrame& frame);
133
134   /**
135    * Print multiple addresses on separate lines.
136    */
137   void println(const uintptr_t* addresses,
138                const SymbolizedFrame* frames,
139                size_t frameCount);
140
141   /**
142    * Print multiple addresses on separate lines, skipping the first
143    * skip addresses.
144    */
145   template <size_t N>
146   void println(const FrameArray<N>& fa, size_t skip=0) {
147     if (skip < fa.frameCount) {
148       println(fa.addresses + skip, fa.frames + skip, fa.frameCount - skip);
149     }
150   }
151
152   virtual ~SymbolizePrinter() { }
153
154   enum Options {
155     // Skip file and line information
156     NO_FILE_AND_LINE = 1 << 0,
157
158     // As terse as it gets: function name if found, address otherwise
159     TERSE = 1 << 1,
160
161     // Always colorize output (ANSI escape code)
162     COLOR = 1 << 2,
163
164     // Colorize output only if output is printed to a TTY (ANSI escape code)
165     COLOR_IF_TTY = 1 << 3,
166   };
167
168   enum Color { DEFAULT, RED, GREEN, YELLOW, BLUE, CYAN, WHITE, PURPLE };
169   void color(Color c);
170
171  protected:
172   explicit SymbolizePrinter(int options, bool isTty = false)
173     : options_(options),
174       isTty_(isTty) {
175   }
176
177   const int options_;
178   const bool isTty_;
179
180  private:
181   void printTerse(uintptr_t address, const SymbolizedFrame& frame);
182   virtual void doPrint(StringPiece sp) = 0;
183 };
184
185 /**
186  * Print a list of symbolized addresses to a stream.
187  * Not reentrant. Do not use from signal handling code.
188  */
189 class OStreamSymbolizePrinter : public SymbolizePrinter {
190  public:
191   explicit OStreamSymbolizePrinter(std::ostream& out, int options=0);
192  private:
193   void doPrint(StringPiece sp) override;
194   std::ostream& out_;
195 };
196
197 /**
198  * Print a list of symbolized addresses to a file descriptor.
199  * Ignores errors. Async-signal-safe.
200  */
201 class FDSymbolizePrinter : public SymbolizePrinter {
202  public:
203   explicit FDSymbolizePrinter(int fd, int options=0);
204  private:
205   void doPrint(StringPiece sp) override;
206   int fd_;
207 };
208
209 /**
210  * Print a list of symbolized addresses to a FILE*.
211  * Ignores errors. Not reentrant. Do not use from signal handling code.
212  */
213 class FILESymbolizePrinter : public SymbolizePrinter {
214  public:
215   explicit FILESymbolizePrinter(FILE* file, int options=0);
216  private:
217   void doPrint(StringPiece sp) override;
218   FILE* file_;
219 };
220
221 /**
222  * Print a list of symbolized addresses to a std::string.
223  * Not reentrant. Do not use from signal handling code.
224  */
225 class StringSymbolizePrinter : public SymbolizePrinter {
226  public:
227   explicit StringSymbolizePrinter(int options=0) : SymbolizePrinter(options) { }
228
229   std::string str() const { return buf_.toStdString(); }
230   const fbstring& fbstr() const { return buf_; }
231   fbstring moveFbString() { return std::move(buf_); }
232
233  private:
234   void doPrint(StringPiece sp) override;
235   fbstring buf_;
236 };
237
238 }  // namespace symbolizer
239 }  // namespace folly
240
241 #endif /* FOLLY_EXPERIMENTAL_SYMBOLIZER_SYMBOLIZER_H_ */