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; @@ -60,6 +60,16 @@ return dwarf::Form(0); } + size_t getNumAttributes() const { + return AttributeSpecs.size(); + } + + Optional getAttrByIndex(uint32_t idx) const { + if (idx < AttributeSpecs.size()) + return AttributeSpecs[idx].Attr; + return None; + } + /// Get the index of the specified attribute. /// /// Searches the this abbreviation declaration for the index of the specified 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,53 @@ getInlinedChainForAddress(const uint64_t Address, SmallVectorImpl &InlinedChain) const; + /// Get an iterator range to all attributes in the current DIE only. + /// + /// If the Error pointer is non-NULL, then any errors will be reported to the + /// error in case extracting any attributes fails. If the error pointer is + /// NULL, then no errors will be reported nor need to be handled. + /// + /// \param Err a pointer to an error if error handling is desired, NULL if no + /// error handling is desired. + /// \returns an iterator range for the attributes of the current DIE. + class attribute_iterator; + iterator_range attributes(Error *Err) 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; + /// Error that will get set during iteration if anythong goes wrong. + Error *Err; + /// The attribute index within the abbreviation declaration in Die. + uint32_t Index; + + /// Update the attribute index and attempt to read the attribute value. If the + /// attribute is able to be read, update AttrValue and the Index member + /// variable. If the attribute value is not able to be read, an appropriate + /// error will be set if the Err member variable is non-NULL and the iterator + /// will be set to the end value so iteration stops. + void updateForIndex(const DWARFAbbreviationDeclaration &AbbrDecl, uint32_t I); + + void setErrorString(const char *ErrorStr); +public: + attribute_iterator() = delete; + explicit attribute_iterator(DWARFDie D, bool End, Error *E); + 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,83 @@ return U->getSibling(Die); return DWARFDie(); } + +iterator_range +DWARFDie::attributes(Error *Err) const { + return make_range(attribute_iterator(*this, false, Err), + attribute_iterator(*this, true, nullptr)); +} + +DWARFDie::attribute_iterator::attribute_iterator(DWARFDie D, bool End, + Error *E) : + Die(D), AttrValue(0), Err(E), 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(); + updateForIndex(*AbbrDecl, 0); + } + } +} + +void DWARFDie::attribute_iterator::setErrorString(const char *ErrorStr) { + if (Err) { + ErrorAsOutParameter ErrAsOutParam(Err); + *Err = make_error(ErrorStr, inconvertibleErrorCode()); + Err = nullptr; // Don't allow the error to be set twice. + } + AttrValue.clear(); + Index = Die.getAbbreviationDeclarationPtr()->getNumAttributes(); +} + +void DWARFDie::attribute_iterator::updateForIndex( + const DWARFAbbreviationDeclaration &AbbrDecl, uint32_t I) { + Index = I; + // AbbrDecl must be valid befor calling this function. + auto NumAttrs = AbbrDecl.getNumAttributes(); + if (Index < NumAttrs) { + auto Attr = AbbrDecl.getAttrByIndex(Index); + if (!Attr) { + setErrorString("Missing attribute for index"); + return; + } + auto Form = AbbrDecl.getFormByIndex(Index); + if (!Form) { + setErrorString("Missing form for index"); + return; + } + AttrValue.Attr = *Attr; + if (!AttrValue.Attr) { + setErrorString("Invalid attribute"); + return; + } + // 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)) { + setErrorString("Failed to extract attribute value"); + return; + } + AttrValue.ByteSize = ParseOffset - AttrValue.Offset; + } else if (Index == NumAttrs) + // This is the normal end of iteration. + AttrValue.clear(); + else { + // Indexes should be [0:NumAttrs) only, anything else if an error. + setErrorString("Invalid attribute index"); + Index = NumAttrs; + } +} + +DWARFDie::attribute_iterator &DWARFDie::attribute_iterator::operator++() { + auto AbbrDecl = Die.getAbbreviationDeclarationPtr(); + if (AbbrDecl) + updateForIndex(*AbbrDecl, Index + 1); + return *this; +} Index: unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp =================================================================== --- unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp +++ unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp @@ -1214,7 +1214,7 @@ // Get the compile unit DIE is valid. auto CUDie = U->getUnitDIE(false); EXPECT_TRUE(CUDie.isValid()); - CUDie.dump(llvm::outs(), UINT32_MAX); + // CUDie.dump(llvm::outs(), UINT32_MAX); // Verify that the CU Die that says it has children, but doesn't, actually // has begin and end iterators that are equal. We want to make sure we don't @@ -1222,4 +1222,88 @@ 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; + Error Err = Error::success(); + for (auto &AttrValue : CUDie.attributes(&Err)) { + 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); + EXPECT_FALSE((bool)Err); + if (Err) { + // Handle all error so we don't crash in case there is an error. + handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) {}); + } +} + } // end anonymous namespace