Move exception tracer library to folly/experimental
[folly.git] / folly / experimental / symbolizer / Dwarf.h
diff --git a/folly/experimental/symbolizer/Dwarf.h b/folly/experimental/symbolizer/Dwarf.h
new file mode 100644 (file)
index 0000000..32382c3
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2012 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// DWARF record parser
+
+#ifndef FOLLY_EXPERIMENTAL_SYMBOLIZER_DWARF_H_
+#define FOLLY_EXPERIMENTAL_SYMBOLIZER_DWARF_H_
+
+#include <boost/variant.hpp>
+
+#include "folly/experimental/symbolizer/Elf.h"
+#include "folly/Range.h"
+
+namespace facebook {
+namespace symbolizer {
+
+/**
+ * DWARF record parser.
+ *
+ * We only implement enough DWARF functionality to convert from PC address
+ * to file and line number information.
+ *
+ * This means (although they're not part of the public API of this class), we
+ * can parse Debug Information Entries (DIEs), abbreviations, attributes (of
+ * all forms), and we can interpret bytecode for the line number VM.
+ *
+ * We can interpret DWARF records of version 2, 3, or 4, although we don't
+ * actually support many of the version 4 features (such as VLIW, multiple
+ * operations per instruction)
+ *
+ * Note that the DWARF record parser does not allocate heap memory at all
+ * during normal operation (it might in the error case, as throwing exceptions
+ * uses the heap).  This is on purpose: you can use the parser from
+ * memory-constrained situations (such as an exception handler for
+ * std::out_of_memory)  If it weren't for this requirement, some things would
+ * be much simpler: the Path class would be unnecessary and would be replaced
+ * with a std::string; the list of file names in the line number VM would be
+ * kept as a vector of strings instead of re-executing the program to look for
+ * DW_LNE_define_file instructions, etc.
+ */
+class Dwarf {
+  // Note that Dwarf uses (and returns) StringPiece a lot.
+  // The StringPieces point within sections in the ELF file, and so will
+  // be live for as long as the passed-in ElfFile is live.
+ public:
+  /** Create a DWARF parser around an ELF file. */
+  explicit Dwarf(const ElfFile* elf);
+
+  /**
+   * Represent a file path a s collection of three parts (base directory,
+   * subdirectory, and file).
+   */
+  class Path {
+   public:
+    Path() { }
+
+    Path(folly::StringPiece baseDir, folly::StringPiece subDir,
+         folly::StringPiece file);
+
+    folly::StringPiece baseDir() const { return baseDir_; };
+    folly::StringPiece subDir() const { return subDir_; }
+    folly::StringPiece file() const { return file_; }
+
+    size_t size() const;
+
+    /**
+     * Copy the Path to a buffer of size bufSize.
+     *
+     * toBuffer behaves like snprintf: It will always null-terminate the
+     * buffer (so it will copy at most bufSize-1 bytes), and it will return
+     * the number of bytes that would have been written if there had been
+     * enough room, so, if toBuffer returns a value >= bufSize, the output
+     * was truncated.
+     */
+    size_t toBuffer(char* buf, size_t bufSize) const;
+
+    void toString(std::string& dest) const;
+    std::string toString() const {
+      std::string s;
+      toString(s);
+      return s;
+    }
+
+    // TODO(tudorb): Implement operator==, operator!=; not as easy as it
+    // seems as the same path can be represented in multiple ways
+   private:
+    folly::StringPiece baseDir_;
+    folly::StringPiece subDir_;
+    folly::StringPiece file_;
+  };
+
+  struct LocationInfo {
+    LocationInfo() : hasMainFile(false), hasFileAndLine(false), line(0) { }
+
+    bool hasMainFile;
+    Path mainFile;
+
+    bool hasFileAndLine;
+    Path file;
+    uint64_t line;
+  };
+
+  /** Find the file and line number information corresponding to address */
+  bool findAddress(uintptr_t address, LocationInfo& info) const;
+
+ private:
+  void init();
+
+  const ElfFile* elf_;
+
+  // DWARF section made up of chunks, each prefixed with a length header.
+  // The length indicates whether the chunk is DWARF-32 or DWARF-64, which
+  // guides interpretation of "section offset" records.
+  // (yes, DWARF-32 and DWARF-64 sections may coexist in the same file)
+  class Section {
+   public:
+    Section() : is64Bit_(false) { }
+
+    explicit Section(folly::StringPiece d);
+
+    // Return next chunk, if any; the 4- or 12-byte length was already
+    // parsed and isn't part of the chunk.
+    bool next(folly::StringPiece& chunk);
+
+    // Is the current chunk 64 bit?
+    bool is64Bit() const { return is64Bit_; }
+
+   private:
+    // Yes, 32- and 64- bit sections may coexist.  Yikes!
+    bool is64Bit_;
+    folly::StringPiece data_;
+  };
+
+  // Abbreviation for a Debugging Information Entry.
+  struct DIEAbbreviation {
+    uint64_t code;
+    uint64_t tag;
+    bool hasChildren;
+
+    struct Attribute {
+      uint64_t name;
+      uint64_t form;
+    };
+
+    folly::StringPiece attributes;
+  };
+
+  // Interpreter for the line number bytecode VM
+  class LineNumberVM {
+   public:
+    LineNumberVM(folly::StringPiece data,
+                 folly::StringPiece compilationDirectory);
+
+    bool findAddress(uintptr_t address, Path& file, uint64_t& line);
+
+   private:
+    void init();
+    void reset();
+
+    // Execute until we commit one new row to the line number matrix
+    bool next(folly::StringPiece& program);
+    enum StepResult {
+      CONTINUE,  // Continue feeding opcodes
+      COMMIT,    // Commit new <address, file, line> tuple
+      END,       // End of sequence
+    };
+    // Execute one opcode
+    StepResult step(folly::StringPiece& program);
+
+    struct FileName {
+      folly::StringPiece relativeName;
+      // 0 = current compilation directory
+      // otherwise, 1-based index in the list of include directories
+      uint64_t directoryIndex;
+    };
+    // Read one FileName object, advance sp
+    static bool readFileName(folly::StringPiece& sp, FileName& fn);
+
+    // Get file name at given index; may be in the initial table
+    // (fileNames_) or defined using DW_LNE_define_file (and we reexecute
+    // enough of the program to find it, if so)
+    FileName getFileName(uint64_t index) const;
+
+    // Get include directory at given index
+    folly::StringPiece getIncludeDirectory(uint64_t index) const;
+
+    // Execute opcodes until finding a DW_LNE_define_file and return true;
+    // return file at the end.
+    bool nextDefineFile(folly::StringPiece& program, FileName& fn) const;
+
+    // Initialization
+    bool is64Bit_;
+    folly::StringPiece data_;
+    folly::StringPiece compilationDirectory_;
+
+    // Header
+    uint16_t version_;
+    uint8_t minLength_;
+    bool defaultIsStmt_;
+    int8_t lineBase_;
+    uint8_t lineRange_;
+    uint8_t opcodeBase_;
+    const uint8_t* standardOpcodeLengths_;
+
+    folly::StringPiece includeDirectories_;
+    size_t includeDirectoryCount_;
+
+    folly::StringPiece fileNames_;
+    size_t fileNameCount_;
+
+    // State machine registers
+    uint64_t address_;
+    uint64_t file_;
+    uint64_t line_;
+    uint64_t column_;
+    bool isStmt_;
+    bool basicBlock_;
+    bool endSequence_;
+    bool prologueEnd_;
+    bool epilogueBegin_;
+    uint64_t isa_;
+    uint64_t discriminator_;
+  };
+
+  // Read an abbreviation from a StringPiece, return true if at end; advance sp
+  static bool readAbbreviation(folly::StringPiece& sp, DIEAbbreviation& abbr);
+
+  // Get abbreviation corresponding to a code, in the chunk starting at
+  // offset in the .debug_abbrev section
+  DIEAbbreviation getAbbreviation(uint64_t code, uint64_t offset) const;
+
+  // Read one attribute <name, form> pair, advance sp; returns <0, 0> at end.
+  static DIEAbbreviation::Attribute readAttribute(folly::StringPiece& sp);
+
+  // Read one attribute value, advance sp
+  typedef boost::variant<uint64_t, folly::StringPiece> AttributeValue;
+  AttributeValue readAttributeValue(
+      folly::StringPiece& sp,
+      uint64_t form,
+      bool is64Bit) const;
+
+  // Get an ELF section by name, return true if found
+  bool getSection(const char* name, folly::StringPiece* section) const;
+
+  // Get a string from the .debug_str section
+  folly::StringPiece getStringFromStringSection(uint64_t offset) const;
+
+  folly::StringPiece info_;       // .debug_info
+  folly::StringPiece abbrev_;     // .debug_abbrev
+  folly::StringPiece aranges_;    // .debug_aranges
+  folly::StringPiece line_;       // .debug_line
+  folly::StringPiece strings_;    // .debug_str
+};
+
+inline std::ostream& operator<<(std::ostream& out, const Dwarf::Path& path) {
+  return out << path.toString();
+}
+
+}  // namespace symbolizer
+}  // namespace facebook
+
+#endif /* FOLLY_EXPERIMENTAL_SYMBOLIZER_DWARF_H_ */
+