Index: include/llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h =================================================================== --- include/llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h +++ include/llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h @@ -44,9 +44,9 @@ DWARFAbbreviationDeclaration(); uint32_t getCode() const { return Code; } + uint8_t getCodeByteSize() const { return CodeByteSize; } dwarf::Tag getTag() const { return Tag; } bool hasChildren() const { return HasChildren; } - typedef iterator_range attr_iterator_range; @@ -54,6 +54,16 @@ return attr_iterator_range(AttributeSpecs.begin(), AttributeSpecs.end()); } + size_t getNumAttributes() const { + return AttributeSpecs.size(); + } + + dwarf::Attribute getAttrByIndex(uint32_t idx) const { + if (idx < AttributeSpecs.size()) + return AttributeSpecs[idx].Attr; + return dwarf::Attribute(0); + } + dwarf::Form getFormByIndex(uint32_t idx) const { if (idx < AttributeSpecs.size()) return AttributeSpecs[idx].Form; Index: include/llvm/DebugInfo/DWARF/DWARFAttribute.h =================================================================== --- include/llvm/DebugInfo/DWARF/DWARFAttribute.h +++ include/llvm/DebugInfo/DWARF/DWARFAttribute.h @@ -0,0 +1,56 @@ +//===-- DWARFAttribute.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_DEBUGINFO_DWARFATTRIBUTE_H +#define LLVM_LIB_DEBUGINFO_DWARFATTRIBUTE_H + +#include "llvm/Support/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" + +namespace llvm { + +//===----------------------------------------------------------------------===// +/// Encapsulates a DWARF attribute value and all of the data required to +/// describe the attribute value. +/// +/// This class is designed to be used by clients that want to iterate across all +/// attributes in a DWARFDie. +struct DWARFAttribute { + /// The debug info/types offset for this attribute. + uint32_t Offset; + /// The debug info/types section byte size of the data for this attribute. + uint32_t ByteSize; + /// The attribute enumeration of this attribute. + dwarf::Attribute Attr; + /// The form and value for this attribute. + DWARFFormValue Value; + + DWARFAttribute(uint32_t O, dwarf::Attribute A = dwarf::Attribute(0), + dwarf::Form F = dwarf::Form(0)) : + Offset(0), ByteSize(0), Attr(A), Value(F) {} + + bool isValid() const { + return Offset != 0 && Attr != dwarf::Attribute(0); + } + + explicit operator bool() const { + return isValid(); + } + + void clear() { + Offset = 0; + ByteSize = 0; + Attr = dwarf::Attribute(0); + Value = DWARFFormValue(); + } +}; + +} + +#endif Index: include/llvm/DebugInfo/DWARF/DWARFDie.h =================================================================== --- include/llvm/DebugInfo/DWARF/DWARFDie.h +++ include/llvm/DebugInfo/DWARF/DWARFDie.h @@ -13,6 +13,7 @@ #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" #include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/DWARF/DWARFAttribute.h" #include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" namespace llvm { @@ -360,12 +361,39 @@ getInlinedChainForAddress(const uint64_t Address, SmallVectorImpl &InlinedChain) const; + class attribute_iterator; + iterator_range attributes() const; + class iterator; iterator begin() const; iterator end() const; iterator_range children() const; }; + +class DWARFDie::attribute_iterator : + public iterator_facade_base { + /// The DWARF DIE we are extracting attributes from. + DWARFDie Die; + /// The value vended to clients via the operator*() or operator->(). + DWARFAttribute AttrValue; + /// The attribute index within the abbreviation declaration in Die. + uint32_t Index; + + /// After Index has been initialized or incremented, this function will + /// attempt to read the attribute value and update AttrValue. If the attribute + /// is not able to be read, it will set the iterator to the end value so that + /// iteration will stop. + bool extractValueForIndex(); +public: + attribute_iterator() = delete; + explicit attribute_iterator(DWARFDie D, bool End); + attribute_iterator &operator++(); + explicit operator bool() const { return AttrValue.isValid(); } + const DWARFAttribute &operator*() const { return AttrValue; } + bool operator==(const attribute_iterator &X) const { return Index == X.Index; } +}; inline bool operator==(const DWARFDie &LHS, const DWARFDie &RHS) { Index: lib/DebugInfo/DWARF/DWARFDie.cpp =================================================================== --- lib/DebugInfo/DWARF/DWARFDie.cpp +++ lib/DebugInfo/DWARF/DWARFDie.cpp @@ -18,6 +18,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/Dwarf.h" #include "llvm/Support/Format.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -436,3 +437,58 @@ return U->getSibling(Die); return DWARFDie(); } + +iterator_range DWARFDie::attributes() const { + return make_range(attribute_iterator(*this, false), + attribute_iterator(*this, true)); +} + +DWARFDie::attribute_iterator::attribute_iterator(DWARFDie D, bool End) : + Die(D), AttrValue(0), Index(0) { + if (auto AbbrDecl = Die.getAbbreviationDeclarationPtr()) { + if (End) { + // This is the end iterator so we set the index to the attribute count. + Index = AbbrDecl->getNumAttributes(); + } else { + // This is the begin iterator so we extract the value for this->Index. + AttrValue.Offset = D.getOffset() + AbbrDecl->getCodeByteSize(); + extractValueForIndex(); + } + } +} + +bool DWARFDie::attribute_iterator::extractValueForIndex() { + // AbbrDecl must be valid befor calling this function. + auto AbbrDecl = Die.getAbbreviationDeclarationPtr(); + auto NumAttrs = AbbrDecl->getNumAttributes(); + if (Index < NumAttrs) { + AttrValue.Attr = AbbrDecl->getAttrByIndex(Index); + auto Form = AbbrDecl->getFormByIndex(Index); + if (AttrValue.Attr && Form) { + // Add the previous byte size of any previous attribute value. + AttrValue.Offset += AttrValue.ByteSize; + AttrValue.Value.setForm(Form); + uint32_t ParseOffset = AttrValue.Offset; + auto U = Die.getDwarfUnit(); + if (AttrValue.Value.extractValue(U->getDebugInfoExtractor(), + &ParseOffset, U)) { + AttrValue.ByteSize = ParseOffset - AttrValue.Offset; + return true; + } + } + } + AttrValue.clear(); + Index = NumAttrs; + return false; +} + +DWARFDie::attribute_iterator &DWARFDie::attribute_iterator::operator++() { + auto AbbrDecl = Die.getAbbreviationDeclarationPtr(); + if (AbbrDecl) { + // Only increment our index if we have a valid abbreviation and fetch the + // attribute value at the updated index. + ++Index; + extractValueForIndex(); + } + return *this; +} Index: unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp =================================================================== --- unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp +++ unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp @@ -1222,4 +1222,82 @@ EXPECT_EQ(CUDie.begin(), CUDie.end()); } +TEST(DWARFDebugInfo, TestAttributeIterators) { + // Test the DWARF APIs related to iterating across all attribute values in a + // a DWARFDie. + uint16_t Version = 4; + + const uint8_t AddrSize = sizeof(void *); + initLLVMIfNeeded(); + Triple Triple = getHostTripleForAddrSize(AddrSize); + auto ExpectedDG = dwarfgen::Generator::create(Triple, Version); + if (HandleExpectedError(ExpectedDG)) + return; + dwarfgen::Generator *DG = ExpectedDG.get().get(); + dwarfgen::CompileUnit &CU = DG->addCompileUnit(); + const uint64_t CULowPC = 0x1000; + const char *CUPath = "/tmp/main.c"; + + // Scope to allow us to re-use the same DIE names + { + auto CUDie = CU.getUnitDIE(); + // Encode an attribute value before an attribute with no data. + CUDie.addAttribute(DW_AT_name, DW_FORM_strp, CUPath); + // Encode an attribute value with no data in .debug_info/types to ensure + // the iteration works correctly. + CUDie.addAttribute(DW_AT_declaration, DW_FORM_flag_present); + // Encode an attribute value after an attribute with no data. + CUDie.addAttribute(DW_AT_low_pc, DW_FORM_addr, CULowPC); + } + + MemoryBufferRef FileBuffer(DG->generate(), "dwarf"); + auto Obj = object::ObjectFile::createObjectFile(FileBuffer); + EXPECT_TRUE((bool)Obj); + DWARFContextInMemory DwarfContext(*Obj.get()); + + // Verify the number of compile units is correct. + uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + EXPECT_EQ(NumCUs, 1u); + DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + + // Get the compile unit DIE is valid. + auto CUDie = U->getUnitDIE(false); + EXPECT_TRUE(CUDie.isValid()); + // CUDie.dump(llvm::outs(), UINT32_MAX); + bool GotName = false; + bool GotDecl = false; + bool GotLowPC = false; + for (auto &AttrValue : CUDie.attributes()) { + switch (AttrValue.Attr) { + case DW_AT_name: { + auto NameOpt = AttrValue.Value.getAsCString(); + GotName = NameOpt.hasValue(); + if (GotName) + EXPECT_TRUE(strcmp(NameOpt.getValue(), CUPath) == 0); + break; + } + case DW_AT_declaration: { + auto DeclOpt = AttrValue.Value.getAsUnsignedConstant(); + GotDecl = DeclOpt.hasValue(); + if (GotDecl) + EXPECT_EQ(DeclOpt.getValue(), 1ull); + break; + } + case DW_AT_low_pc: { + auto LowPCOpt = AttrValue.Value.getAsAddress(); + GotLowPC = LowPCOpt.hasValue(); + if (GotLowPC) + EXPECT_EQ(LowPCOpt.getValue(), CULowPC); + break; + } + default: + EXPECT_TRUE(false); + break; + } + } + EXPECT_TRUE(GotName); + EXPECT_TRUE(GotDecl); + EXPECT_TRUE(GotLowPC); +} + } // end anonymous namespace