723154a0b7233d650f90472313694b1ce864d54a
[folly.git] / folly / experimental / symbolizer / Dwarf.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 // DWARF record parser
18
19 #ifndef FOLLY_EXPERIMENTAL_SYMBOLIZER_DWARF_H_
20 #define FOLLY_EXPERIMENTAL_SYMBOLIZER_DWARF_H_
21
22 #include <boost/variant.hpp>
23
24 #include <folly/experimental/symbolizer/Elf.h>
25 #include <folly/Range.h>
26
27 namespace folly {
28 namespace symbolizer {
29
30 /**
31  * DWARF record parser.
32  *
33  * We only implement enough DWARF functionality to convert from PC address
34  * to file and line number information.
35  *
36  * This means (although they're not part of the public API of this class), we
37  * can parse Debug Information Entries (DIEs), abbreviations, attributes (of
38  * all forms), and we can interpret bytecode for the line number VM.
39  *
40  * We can interpret DWARF records of version 2, 3, or 4, although we don't
41  * actually support many of the version 4 features (such as VLIW, multiple
42  * operations per instruction)
43  *
44  * Note that the DWARF record parser does not allocate heap memory at all.
45  * This is on purpose: you can use the parser from
46  * memory-constrained situations (such as an exception handler for
47  * std::out_of_memory)  If it weren't for this requirement, some things would
48  * be much simpler: the Path class would be unnecessary and would be replaced
49  * with a std::string; the list of file names in the line number VM would be
50  * kept as a vector of strings instead of re-executing the program to look for
51  * DW_LNE_define_file instructions, etc.
52  */
53 class Dwarf {
54   // Note that Dwarf uses (and returns) StringPiece a lot.
55   // The StringPieces point within sections in the ELF file, and so will
56   // be live for as long as the passed-in ElfFile is live.
57  public:
58   /** Create a DWARF parser around an ELF file. */
59   explicit Dwarf(const ElfFile* elf);
60
61   /**
62    * Represent a file path a s collection of three parts (base directory,
63    * subdirectory, and file).
64    */
65   class Path {
66    public:
67     Path() { }
68
69     Path(folly::StringPiece baseDir, folly::StringPiece subDir,
70          folly::StringPiece file);
71
72     folly::StringPiece baseDir() const { return baseDir_; };
73     folly::StringPiece subDir() const { return subDir_; }
74     folly::StringPiece file() const { return file_; }
75
76     size_t size() const;
77
78     /**
79      * Copy the Path to a buffer of size bufSize.
80      *
81      * toBuffer behaves like snprintf: It will always null-terminate the
82      * buffer (so it will copy at most bufSize-1 bytes), and it will return
83      * the number of bytes that would have been written if there had been
84      * enough room, so, if toBuffer returns a value >= bufSize, the output
85      * was truncated.
86      */
87     size_t toBuffer(char* buf, size_t bufSize) const;
88
89     void toString(std::string& dest) const;
90     std::string toString() const {
91       std::string s;
92       toString(s);
93       return s;
94     }
95
96     // TODO(tudorb): Implement operator==, operator!=; not as easy as it
97     // seems as the same path can be represented in multiple ways
98    private:
99     folly::StringPiece baseDir_;
100     folly::StringPiece subDir_;
101     folly::StringPiece file_;
102   };
103
104   struct LocationInfo {
105     LocationInfo() : hasMainFile(false), hasFileAndLine(false), line(0) { }
106
107     bool hasMainFile;
108     Path mainFile;
109
110     bool hasFileAndLine;
111     Path file;
112     uint64_t line;
113   };
114
115   /** Find the file and line number information corresponding to address */
116   bool findAddress(uintptr_t address, LocationInfo& info) const;
117
118  private:
119   void init();
120
121   const ElfFile* elf_;
122
123   // DWARF section made up of chunks, each prefixed with a length header.
124   // The length indicates whether the chunk is DWARF-32 or DWARF-64, which
125   // guides interpretation of "section offset" records.
126   // (yes, DWARF-32 and DWARF-64 sections may coexist in the same file)
127   class Section {
128    public:
129     Section() : is64Bit_(false) { }
130
131     explicit Section(folly::StringPiece d);
132
133     // Return next chunk, if any; the 4- or 12-byte length was already
134     // parsed and isn't part of the chunk.
135     bool next(folly::StringPiece& chunk);
136
137     // Is the current chunk 64 bit?
138     bool is64Bit() const { return is64Bit_; }
139
140    private:
141     // Yes, 32- and 64- bit sections may coexist.  Yikes!
142     bool is64Bit_;
143     folly::StringPiece data_;
144   };
145
146   // Abbreviation for a Debugging Information Entry.
147   struct DIEAbbreviation {
148     uint64_t code;
149     uint64_t tag;
150     bool hasChildren;
151
152     struct Attribute {
153       uint64_t name;
154       uint64_t form;
155     };
156
157     folly::StringPiece attributes;
158   };
159
160   // Interpreter for the line number bytecode VM
161   class LineNumberVM {
162    public:
163     LineNumberVM(folly::StringPiece data,
164                  folly::StringPiece compilationDirectory);
165
166     bool findAddress(uintptr_t address, Path& file, uint64_t& line);
167
168    private:
169     void init();
170     void reset();
171
172     // Execute until we commit one new row to the line number matrix
173     bool next(folly::StringPiece& program);
174     enum StepResult {
175       CONTINUE,  // Continue feeding opcodes
176       COMMIT,    // Commit new <address, file, line> tuple
177       END,       // End of sequence
178     };
179     // Execute one opcode
180     StepResult step(folly::StringPiece& program);
181
182     struct FileName {
183       folly::StringPiece relativeName;
184       // 0 = current compilation directory
185       // otherwise, 1-based index in the list of include directories
186       uint64_t directoryIndex;
187     };
188     // Read one FileName object, advance sp
189     static bool readFileName(folly::StringPiece& sp, FileName& fn);
190
191     // Get file name at given index; may be in the initial table
192     // (fileNames_) or defined using DW_LNE_define_file (and we reexecute
193     // enough of the program to find it, if so)
194     FileName getFileName(uint64_t index) const;
195
196     // Get include directory at given index
197     folly::StringPiece getIncludeDirectory(uint64_t index) const;
198
199     // Execute opcodes until finding a DW_LNE_define_file and return true;
200     // return file at the end.
201     bool nextDefineFile(folly::StringPiece& program, FileName& fn) const;
202
203     // Initialization
204     bool is64Bit_;
205     folly::StringPiece data_;
206     folly::StringPiece compilationDirectory_;
207
208     // Header
209     uint16_t version_;
210     uint8_t minLength_;
211     bool defaultIsStmt_;
212     int8_t lineBase_;
213     uint8_t lineRange_;
214     uint8_t opcodeBase_;
215     const uint8_t* standardOpcodeLengths_;
216
217     folly::StringPiece includeDirectories_;
218     size_t includeDirectoryCount_;
219
220     folly::StringPiece fileNames_;
221     size_t fileNameCount_;
222
223     // State machine registers
224     uint64_t address_;
225     uint64_t file_;
226     uint64_t line_;
227     uint64_t column_;
228     bool isStmt_;
229     bool basicBlock_;
230     bool endSequence_;
231     bool prologueEnd_;
232     bool epilogueBegin_;
233     uint64_t isa_;
234     uint64_t discriminator_;
235   };
236
237   // Read an abbreviation from a StringPiece, return true if at end; advance sp
238   static bool readAbbreviation(folly::StringPiece& sp, DIEAbbreviation& abbr);
239
240   // Get abbreviation corresponding to a code, in the chunk starting at
241   // offset in the .debug_abbrev section
242   DIEAbbreviation getAbbreviation(uint64_t code, uint64_t offset) const;
243
244   // Read one attribute <name, form> pair, advance sp; returns <0, 0> at end.
245   static DIEAbbreviation::Attribute readAttribute(folly::StringPiece& sp);
246
247   // Read one attribute value, advance sp
248   typedef boost::variant<uint64_t, folly::StringPiece> AttributeValue;
249   AttributeValue readAttributeValue(
250       folly::StringPiece& sp,
251       uint64_t form,
252       bool is64Bit) const;
253
254   // Get an ELF section by name, return true if found
255   bool getSection(const char* name, folly::StringPiece* section) const;
256
257   // Get a string from the .debug_str section
258   folly::StringPiece getStringFromStringSection(uint64_t offset) const;
259
260   folly::StringPiece info_;       // .debug_info
261   folly::StringPiece abbrev_;     // .debug_abbrev
262   folly::StringPiece aranges_;    // .debug_aranges
263   folly::StringPiece line_;       // .debug_line
264   folly::StringPiece strings_;    // .debug_str
265 };
266
267 inline std::ostream& operator<<(std::ostream& out, const Dwarf::Path& path) {
268   return out << path.toString();
269 }
270
271 }  // namespace symbolizer
272 }  // namespace folly
273
274 #endif /* FOLLY_EXPERIMENTAL_SYMBOLIZER_DWARF_H_ */