stack_trace_test was broken in debug mode
[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 // Always inline these functions; they don't do much, and unittests rely
78 // on them never showing up in a stack trace.
79 template <size_t N>
80 inline bool getStackTrace(FrameArray<N>& fa) __attribute__((always_inline));
81
82 template <size_t N>
83 inline bool getStackTrace(FrameArray<N>& fa) {
84   return detail::fixFrameArray(fa, getStackTrace(fa.addresses, N));
85 }
86 template <size_t N>
87 inline bool getStackTraceSafe(FrameArray<N>& fa) __attribute__((always_inline));
88
89 template <size_t N>
90 inline bool getStackTraceSafe(FrameArray<N>& fa) {
91   return detail::fixFrameArray(fa, getStackTraceSafe(fa.addresses, N));
92 }
93
94 class Symbolizer {
95  public:
96   Symbolizer() : fileCount_(0) { }
97
98   /**
99    * Symbolize given addresses.
100    */
101   void symbolize(const uintptr_t* addresses,
102                  SymbolizedFrame* frames,
103                  size_t frameCount);
104
105   template <size_t N>
106   void symbolize(FrameArray<N>& fa) {
107     symbolize(fa.addresses, fa.frames, fa.frameCount);
108   }
109
110   /**
111    * Shortcut to symbolize one address.
112    */
113   bool symbolize(uintptr_t address, SymbolizedFrame& frame) {
114     symbolize(&address, &frame, 1);
115     return frame.found;
116   }
117
118  private:
119   // We can't allocate memory, so we'll preallocate room.
120   // "1023 shared libraries should be enough for everyone"
121   static constexpr size_t kMaxFiles = 1024;
122   size_t fileCount_;
123   ElfFile files_[kMaxFiles];
124 };
125
126 /**
127  * Print a list of symbolized addresses. Base class.
128  */
129 class SymbolizePrinter {
130  public:
131   /**
132    * Print one address, no ending newline.
133    */
134   void print(uintptr_t address, const SymbolizedFrame& frame);
135
136   /**
137    * Print one address with ending newline.
138    */
139   void println(uintptr_t address, const SymbolizedFrame& frame);
140
141   /**
142    * Print multiple addresses on separate lines.
143    */
144   void println(const uintptr_t* addresses,
145                const SymbolizedFrame* frames,
146                size_t frameCount);
147
148   /**
149    * Print multiple addresses on separate lines, skipping the first
150    * skip addresses.
151    */
152   template <size_t N>
153   void println(const FrameArray<N>& fa, size_t skip=0) {
154     if (skip < fa.frameCount) {
155       println(fa.addresses + skip, fa.frames + skip, fa.frameCount - skip);
156     }
157   }
158
159   virtual ~SymbolizePrinter() { }
160
161   enum Options {
162     // Skip file and line information
163     NO_FILE_AND_LINE = 1 << 0,
164
165     // As terse as it gets: function name if found, address otherwise
166     TERSE = 1 << 1,
167
168     // Always colorize output (ANSI escape code)
169     COLOR = 1 << 2,
170
171     // Colorize output only if output is printed to a TTY (ANSI escape code)
172     COLOR_IF_TTY = 1 << 3,
173   };
174
175   enum Color { DEFAULT, RED, GREEN, YELLOW, BLUE, CYAN, WHITE, PURPLE };
176   void color(Color c);
177
178  protected:
179   explicit SymbolizePrinter(int options, bool isTty = false)
180     : options_(options),
181       isTty_(isTty) {
182   }
183
184   const int options_;
185   const bool isTty_;
186
187  private:
188   void printTerse(uintptr_t address, const SymbolizedFrame& frame);
189   virtual void doPrint(StringPiece sp) = 0;
190 };
191
192 /**
193  * Print a list of symbolized addresses to a stream.
194  * Not reentrant. Do not use from signal handling code.
195  */
196 class OStreamSymbolizePrinter : public SymbolizePrinter {
197  public:
198   explicit OStreamSymbolizePrinter(std::ostream& out, int options=0);
199  private:
200   void doPrint(StringPiece sp) override;
201   std::ostream& out_;
202 };
203
204 /**
205  * Print a list of symbolized addresses to a file descriptor.
206  * Ignores errors. Async-signal-safe.
207  */
208 class FDSymbolizePrinter : public SymbolizePrinter {
209  public:
210   explicit FDSymbolizePrinter(int fd, int options=0);
211  private:
212   void doPrint(StringPiece sp) override;
213   int fd_;
214 };
215
216 /**
217  * Print a list of symbolized addresses to a FILE*.
218  * Ignores errors. Not reentrant. Do not use from signal handling code.
219  */
220 class FILESymbolizePrinter : public SymbolizePrinter {
221  public:
222   explicit FILESymbolizePrinter(FILE* file, int options=0);
223  private:
224   void doPrint(StringPiece sp) override;
225   FILE* file_;
226 };
227
228 /**
229  * Print a list of symbolized addresses to a std::string.
230  * Not reentrant. Do not use from signal handling code.
231  */
232 class StringSymbolizePrinter : public SymbolizePrinter {
233  public:
234   explicit StringSymbolizePrinter(int options=0) : SymbolizePrinter(options) { }
235
236   std::string str() const { return buf_.toStdString(); }
237   const fbstring& fbstr() const { return buf_; }
238   fbstring moveFbString() { return std::move(buf_); }
239
240  private:
241   void doPrint(StringPiece sp) override;
242   fbstring buf_;
243 };
244
245 }  // namespace symbolizer
246 }  // namespace folly
247
248 #endif /* FOLLY_EXPERIMENTAL_SYMBOLIZER_SYMBOLIZER_H_ */