From d1f6d693d6c1fe9ae876d670669d7a75a8c3866b Mon Sep 17 00:00:00 2001 From: Greg Clayton Date: Thu, 5 Jan 2017 23:47:37 +0000 Subject: [PATCH] Add iterator support to DWARFDie to allow child DIE iteration. Differential Revision: https://reviews.llvm.org/D28303 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@291194 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/CodeGen/DIE.h | 9 +- include/llvm/DebugInfo/DWARF/DWARFDie.h | 58 ++++++++- lib/DebugInfo/DWARF/DWARFDie.cpp | 5 +- tools/dsymutil/DwarfLinker.cpp | 12 +- .../DebugInfo/DWARF/DWARFDebugInfoTest.cpp | 121 ++++++++++++++++++ unittests/DebugInfo/DWARF/DwarfGenerator.cpp | 4 + unittests/DebugInfo/DWARF/DwarfGenerator.h | 3 + 7 files changed, 195 insertions(+), 17 deletions(-) diff --git a/include/llvm/CodeGen/DIE.h b/include/llvm/CodeGen/DIE.h index 1e3476cd839..09c3bf6a1b5 100644 --- a/include/llvm/CodeGen/DIE.h +++ b/include/llvm/CodeGen/DIE.h @@ -651,6 +651,9 @@ class DIE : IntrusiveBackListNode, public DIEValueList { unsigned AbbrevNumber = ~0u; /// Dwarf tag code. dwarf::Tag Tag = (dwarf::Tag)0; + /// Set to true to force a DIE to emit an abbreviation that says it has + /// children even when it doesn't. This is used for unit testing purposes. + bool ForceChildren; /// Children DIEs. IntrusiveBackList Children; @@ -659,7 +662,8 @@ class DIE : IntrusiveBackListNode, public DIEValueList { PointerUnion Owner; DIE() = delete; - explicit DIE(dwarf::Tag Tag) : Offset(0), Size(0), Tag(Tag) {} + explicit DIE(dwarf::Tag Tag) : Offset(0), Size(0), Tag(Tag), + ForceChildren(false) {} public: static DIE *get(BumpPtrAllocator &Alloc, dwarf::Tag Tag) { @@ -677,7 +681,8 @@ public: /// Get the compile/type unit relative offset of this DIE. unsigned getOffset() const { return Offset; } unsigned getSize() const { return Size; } - bool hasChildren() const { return !Children.empty(); } + bool hasChildren() const { return ForceChildren || !Children.empty(); } + void setForceChildren(bool B) { ForceChildren = B; } typedef IntrusiveBackList::iterator child_iterator; typedef IntrusiveBackList::const_iterator const_child_iterator; diff --git a/include/llvm/DebugInfo/DWARF/DWARFDie.h b/include/llvm/DebugInfo/DWARF/DWARFDie.h index f33758de6a5..5a24b7c8729 100644 --- a/include/llvm/DebugInfo/DWARF/DWARFDie.h +++ b/include/llvm/DebugInfo/DWARF/DWARFDie.h @@ -10,6 +10,8 @@ #ifndef LLVM_LIB_DEBUGINFO_DWARFDIE_H #define LLVM_LIB_DEBUGINFO_DWARFDIE_H +#include "llvm/ADT/iterator.h" +#include "llvm/ADT/iterator_range.h" #include "llvm/ADT/Optional.h" #include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" @@ -40,9 +42,6 @@ public: bool isValid() const { return U && Die; } explicit operator bool() const { return isValid(); } - bool operator ==(const DWARFDie &RHS) const { - return Die == RHS.Die && U == RHS.U; - } const DWARFDebugInfoEntry *getDebugInfoEntry() const { return Die; } DWARFUnit *getDwarfUnit() const { return U; } @@ -361,8 +360,61 @@ public: getInlinedChainForAddress(const uint64_t Address, SmallVectorImpl &InlinedChain) const; + class iterator; + + iterator begin() const; + iterator end() const; + iterator_range children() const; }; + +inline bool operator==(const DWARFDie &LHS, const DWARFDie &RHS) { + return LHS.getDebugInfoEntry() == RHS.getDebugInfoEntry() && + LHS.getDwarfUnit() == RHS.getDwarfUnit(); +} + +inline bool operator!=(const DWARFDie &LHS, const DWARFDie &RHS) { + return !(LHS == RHS); +} + +class DWARFDie::iterator : public iterator_facade_base { + DWARFDie Die; + void skipNull() { + if (Die && Die.isNULL()) + Die = DWARFDie(); + } +public: + iterator() = default; + explicit iterator(DWARFDie D) : Die(D) { + // If we start out with only a Null DIE then invalidate. + skipNull(); + } + iterator &operator++() { + Die = Die.getSibling(); + // Don't include the NULL die when iterating. + skipNull(); + return *this; + } + explicit operator bool() const { return Die.isValid(); } + const DWARFDie &operator*() const { return Die; } + bool operator==(const iterator &X) const { return Die == X.Die; } +}; + +// These inline functions must follow the DWARFDie::iterator definition above +// as they use functions from that class. +inline DWARFDie::iterator DWARFDie::begin() const { + return iterator(getFirstChild()); +} + +inline DWARFDie::iterator DWARFDie::end() const { + return iterator(); +} + +inline iterator_range DWARFDie::children() const { + return make_range(begin(), end()); +} } // end namespace llvm diff --git a/lib/DebugInfo/DWARF/DWARFDie.cpp b/lib/DebugInfo/DWARF/DWARFDie.cpp index deec1633022..2aac3474654 100644 --- a/lib/DebugInfo/DWARF/DWARFDie.cpp +++ b/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -299,11 +299,8 @@ DWARFDie::collectChildrenAddressRanges(DWARFAddressRangesVector& Ranges) const { Ranges.insert(Ranges.end(), DIERanges.begin(), DIERanges.end()); } - DWARFDie Child = getFirstChild(); - while (Child) { + for (auto Child: children()) Child.collectChildrenAddressRanges(Ranges); - Child = Child.getSibling(); - } } bool DWARFDie::addressRangeContainsAddress(const uint64_t Address) const { diff --git a/tools/dsymutil/DwarfLinker.cpp b/tools/dsymutil/DwarfLinker.cpp index 018d91d6c31..ecd631c1039 100644 --- a/tools/dsymutil/DwarfLinker.cpp +++ b/tools/dsymutil/DwarfLinker.cpp @@ -1796,8 +1796,7 @@ static bool analyzeContextInfo(const DWARFDie &DIE, Info.Prune = InImportedModule; if (DIE.hasChildren()) - for (auto Child = DIE.getFirstChild(); Child && !Child.isNULL(); - Child = Child.getSibling()) + for (auto Child: DIE.children()) Info.Prune &= analyzeContextInfo(Child, MyIdx, CU, CurrentDeclContext, StringPool, Contexts, InImportedModule); @@ -2294,8 +2293,7 @@ void DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr, if (!Die.hasChildren() || (Flags & TF_ParentWalk)) return; - for (auto Child = Die.getFirstChild(); Child && !Child.isNULL(); - Child = Child.getSibling()) + for (auto Child: Die.children()) lookForDIEsToKeep(RelocMgr, Child, DMO, CU, Flags); } @@ -2814,8 +2812,7 @@ DIE *DwarfLinker::DIECloner::cloneDIE( // Determine whether there are any children that we want to keep. bool HasChildren = false; - for (auto Child = InputDIE.getFirstChild(); Child && !Child.isNULL(); - Child = Child.getSibling()) { + for (auto Child: InputDIE.children()) { unsigned Idx = U.getDIEIndex(Child); if (Unit.getInfo(Idx).Keep) { HasChildren = true; @@ -2840,8 +2837,7 @@ DIE *DwarfLinker::DIECloner::cloneDIE( } // Recursively clone children. - for (auto Child = InputDIE.getFirstChild(); Child && !Child.isNULL(); - Child = Child.getSibling()) { + for (auto Child: InputDIE.children()) { if (DIE *Clone = cloneDIE(Child, Unit, PCOffset, OutOffset, Flags)) { Die->addChild(Clone); OutOffset = Clone->getOffset() + Clone->getSize(); diff --git a/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp b/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp index ec8297f277f..4435b794268 100644 --- a/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp +++ b/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp @@ -1100,5 +1100,126 @@ TEST(DWARFDebugInfo, TestDWARFDie) { EXPECT_FALSE(DefaultDie.getSibling().isValid()); } +TEST(DWARFDebugInfo, TestChildIterators) { + // Test the DWARF APIs related to iterating across the children of a DIE using + // the DWARFDie::iterator class. + 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(); + + enum class Tag: uint16_t { + A = dwarf::DW_TAG_lo_user, + B, + }; + + // Scope to allow us to re-use the same DIE names + { + // Create DWARF tree that looks like: + // + // CU + // A + // B + auto CUDie = CU.getUnitDIE(); + CUDie.addChild((dwarf::Tag)Tag::A); + CUDie.addChild((dwarf::Tag)Tag::B); + } + + 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); + uint32_t Index; + DWARFDie A; + DWARFDie B; + + // Verify the compile unit DIE's children. + Index = 0; + for (auto Die : CUDie.children()) { + switch (Index++) { + case 0: A = Die; break; + case 1: B = Die; break; + } + } + + EXPECT_EQ(A.getTag(), (dwarf::Tag)Tag::A); + EXPECT_EQ(B.getTag(), (dwarf::Tag)Tag::B); + + // Verify that A has no children by verifying that the begin and end contain + // invalid DIEs and also that the iterators are equal. + EXPECT_EQ(A.begin(), A.end()); +} + +TEST(DWARFDebugInfo, TestChildIteratorsOnInvalidDie) { + // Verify that an invalid DIE has no children. + DWARFDie Invalid; + auto begin = Invalid.begin(); + auto end = Invalid.end(); + EXPECT_FALSE(begin->isValid()); + EXPECT_FALSE(end->isValid()); + EXPECT_EQ(begin, end); +} + + +TEST(DWARFDebugInfo, TestEmptyChildren) { + // Test a DIE that says it has children in the abbreviation, but actually + // doesn't have any attributes, will not return anything during iteration. + // We do this by making sure the begin and end iterators are equal. + 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(); + + // Scope to allow us to re-use the same DIE names + { + // Create a compile unit DIE that has an abbreviation that says it has + // children, but doesn't have any actual attributes. This helps us test + // a DIE that has only one child: a NULL DIE. + auto CUDie = CU.getUnitDIE(); + CUDie.setForceChildren(); + } + + 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); + + // 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 + // see the Null DIEs during iteration. + EXPECT_EQ(CUDie.begin(), CUDie.end()); +} } // end anonymous namespace diff --git a/unittests/DebugInfo/DWARF/DwarfGenerator.cpp b/unittests/DebugInfo/DWARF/DwarfGenerator.cpp index 9a72f70a0f2..9ec43cab4dc 100644 --- a/unittests/DebugInfo/DWARF/DwarfGenerator.cpp +++ b/unittests/DebugInfo/DWARF/DwarfGenerator.cpp @@ -108,6 +108,10 @@ dwarfgen::DIE dwarfgen::CompileUnit::getUnitDIE() { return dwarfgen::DIE(this, &DU.getUnitDie()); } +void dwarfgen::DIE::setForceChildren() { + Die->setForceChildren(true); +} + //===----------------------------------------------------------------------===// /// dwarfgen::Generator implementation. //===----------------------------------------------------------------------===// diff --git a/unittests/DebugInfo/DWARF/DwarfGenerator.h b/unittests/DebugInfo/DWARF/DwarfGenerator.h index 966725b4fa4..2978d1ca002 100644 --- a/unittests/DebugInfo/DWARF/DwarfGenerator.h +++ b/unittests/DebugInfo/DWARF/DwarfGenerator.h @@ -129,6 +129,9 @@ public: /// \returns the newly created DIE object that is now a child owned by this /// object. dwarfgen::DIE addChild(dwarf::Tag Tag); + + /// Force a DIE to say it has children even when it doesn't. + void setForceChildren(); }; /// A DWARF compile unit used to generate DWARF compile/type units.