Index: include/llvm/DebugInfo/GSYM/InlineInfo.h =================================================================== --- include/llvm/DebugInfo/GSYM/InlineInfo.h +++ include/llvm/DebugInfo/GSYM/InlineInfo.h @@ -30,6 +30,30 @@ /// Any clients that encode information will need to ensure the ranges are /// all contined correctly or lookups could fail. Add ranges in these objects /// must be contained in the top level FunctionInfo address ranges as well. +/// +/// ENCODING +/// +/// When saved to disk, the inline info encodes all ranges to be relative to +/// a parent address range. This will be the FunctionInfo's start address if +/// the InlineInfo is directly contained in a FunctionInfo, or a the start +/// address of the containing parent InlineInfo's first "Ranges" member. This +/// allows address ranges to be efficiently encoded using ULEB128 encodings as +/// we encode the offset and size of each range instead of full addresses. This +/// also makes any encoded addresses easy to relocate as we just need to +/// relocate the FunctionInfo's start address. +/// +/// - The AddressRanges member "Ranges" is encoded using an approriate base +/// address as described above. +/// - UINT8 boolean value that specifies if the InlineInfo object has children. +/// - UINT32 string table offset that points to the name of the inline +/// function. +/// - ULEB128 integer that specifies the file of the call site that called +/// this function. +/// - ULEB128 integer that specifies the source line of the call site that +/// called this function. +/// - if this object has children, enocode each child InlineInfo using the +/// the first address range's start address as the base address. +/// struct InlineInfo { uint32_t Name; ///< String table offset in the string table. @@ -61,6 +85,31 @@ /// \returns optional vector of InlineInfo objects that describe the /// inline call stack for a given address, false otherwise. llvm::Optional getInlineStack(uint64_t Addr) const; + + /// Decode an InlineInfo object from a binary data stream. + /// + /// \param Data The binary stream to read the data from. This object must + /// have the data for the InlineInfo object starting at offset zero. The data + /// can contain more data than needed. + /// + /// \param BaseAddr The base address to use when decoding all address ranges. + /// This will be the FunctionInfo's start address if this object is directly + /// contained in a FunctionInfo object, or the start address of the first + /// address range in an InlineInfo object of this object is a child of + /// another InlineInfo object. + void decode(DataExtractor &Data, uint64_t BaseAddr); + + /// Encode this InlineInfo object into FileWriter stream. + /// + /// \param O The binary stream to write the data to at the current file + /// position. + /// + /// \param BaseAddr The base address to use when encoding all address ranges. + /// This will be the FunctionInfo's start address if this object is directly + /// contained in a FunctionInfo object, or the start address of the first + /// address range in an InlineInfo object of this object is a child of + /// another InlineInfo object. + void encode(FileWriter &O, uint64_t BaseAddr) const; }; inline bool operator==(const InlineInfo &LHS, const InlineInfo &RHS) { Index: lib/DebugInfo/GSYM/InlineInfo.cpp =================================================================== --- lib/DebugInfo/GSYM/InlineInfo.cpp +++ lib/DebugInfo/GSYM/InlineInfo.cpp @@ -8,7 +8,9 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/GSYM/FileEntry.h" +#include "llvm/DebugInfo/GSYM/FileWriter.h" #include "llvm/DebugInfo/GSYM/InlineInfo.h" +#include "llvm/Support/DataExtractor.h" #include #include @@ -57,3 +59,48 @@ return Result; return llvm::None; } + +static bool decodeAll(InlineInfo &Inline, DataExtractor &Data, + uint64_t &Offset, uint64_t BaseAddr) { + Inline.Ranges.decode(Data, BaseAddr, Offset); + if (Inline.Ranges.empty()) + return false; + bool HasChildren = Data.getU8(&Offset) != 0; + Inline.Name = Data.getU32(&Offset); + Inline.CallFile = (uint32_t)Data.getULEB128(&Offset); + Inline.CallLine = (uint32_t)Data.getULEB128(&Offset); + if (HasChildren) { + InlineInfo Child; + while (decodeAll(Child, Data, Offset, Inline.Ranges[0].Start)) { + Inline.Children.emplace_back(std::move(Child)); + Child.clear(); + } + } + return true; +} + +void InlineInfo::decode(DataExtractor &Data, uint64_t BaseAddr) { + uint64_t Offset = 0; + decodeAll(*this, Data, Offset, BaseAddr); +} + +void InlineInfo::encode(FileWriter &O, uint64_t BaseAddr) const { + if (Ranges.empty()) + return; + Ranges.encode(O, BaseAddr); + bool HasChildren = !Children.empty(); + O.writeU8(HasChildren); + O.writeU32(Name); + O.writeULEB(CallFile); + O.writeULEB(CallLine); + if (HasChildren) { + // Child address ranges are encoded as relative to the first + // address in the Ranges for this object. This keeps the offsets + // small and allows for efficient encoding using ULEB offsets. + const uint64_t ChildBaseAddr = Ranges[0].Start; + for (const auto &Child : Children) + Child.encode(O, ChildBaseAddr); + // Terminate child sibling chain by emitting a zero for + O.writeULEB(0); + } +} Index: unittests/DebugInfo/GSYM/GSYMTest.cpp =================================================================== --- unittests/DebugInfo/GSYM/GSYMTest.cpp +++ unittests/DebugInfo/GSYM/GSYMTest.cpp @@ -226,8 +226,22 @@ ASSERT_EQ(InlineInfos->size(), 2u); ASSERT_EQ(*InlineInfos->at(0), Inline1Sub2); ASSERT_EQ(*InlineInfos->at(1), Inline1); -} + // Test encoding and decoding InlineInfo objects + SmallString<512> Str; + raw_svector_ostream OutStrm(Str); + const auto ByteOrder = llvm::support::endian::system_endianness(); + FileWriter FW(OutStrm, ByteOrder); + const uint64_t BaseAddr = Root.Ranges[0].Start; + Root.encode(FW, BaseAddr); + std::string Bytes(OutStrm.str()); + uint8_t AddressSize = 4; + DataExtractor Data(Bytes, ByteOrder == llvm::support::little, AddressSize); + InlineInfo DecodedRoot; + DecodedRoot.decode(Data, BaseAddr); + EXPECT_EQ(Root, DecodedRoot); +} + TEST(GSYMTest, TestLineEntry) { // test llvm::gsym::LineEntry structs. const uint64_t ValidAddr = 0x1000;