From: Zachary Turner Date: Thu, 13 Apr 2017 21:11:00 +0000 (+0000) Subject: [llvm-pdbdump] Recursively dump class layout. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=10683346a57eaec13c4d6885ce845e4bc392441f;p=llvm [llvm-pdbdump] Recursively dump class layout. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@300258 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/DebugInfo/PDB/ConcreteSymbolEnumerator.h b/include/llvm/DebugInfo/PDB/ConcreteSymbolEnumerator.h index 9bf07383156..9713dce362d 100644 --- a/include/llvm/DebugInfo/PDB/ConcreteSymbolEnumerator.h +++ b/include/llvm/DebugInfo/PDB/ConcreteSymbolEnumerator.h @@ -34,12 +34,11 @@ public: std::unique_ptr getChildAtIndex(uint32_t Index) const override { std::unique_ptr Child = Enumerator->getChildAtIndex(Index); - return make_concrete_child(std::move(Child)); + return unique_dyn_cast_or_null(Child); } std::unique_ptr getNext() override { - std::unique_ptr Child = Enumerator->getNext(); - return make_concrete_child(std::move(Child)); + return unique_dyn_cast_or_null(Enumerator->getNext()); } void reset() override { Enumerator->reset(); } @@ -50,11 +49,6 @@ public: } private: - std::unique_ptr - make_concrete_child(std::unique_ptr Child) const { - ChildType *ConcreteChild = dyn_cast_or_null(Child.release()); - return std::unique_ptr(ConcreteChild); - } std::unique_ptr Enumerator; }; diff --git a/include/llvm/DebugInfo/PDB/PDBSymbolFunc.h b/include/llvm/DebugInfo/PDB/PDBSymbolFunc.h index 5686f8716a0..c2f02ea6f12 100644 --- a/include/llvm/DebugInfo/PDB/PDBSymbolFunc.h +++ b/include/llvm/DebugInfo/PDB/PDBSymbolFunc.h @@ -27,6 +27,8 @@ public: void dump(PDBSymDumper &Dumper) const override; + bool isDestructor() const; + std::unique_ptr> getArguments() const; DECLARE_PDB_SYMBOL_CONCRETE_TYPE(PDB_SymType::Function) diff --git a/include/llvm/DebugInfo/PDB/UDTLayout.h b/include/llvm/DebugInfo/PDB/UDTLayout.h index fca2d43bb3e..e3dcba50bd1 100644 --- a/include/llvm/DebugInfo/PDB/UDTLayout.h +++ b/include/llvm/DebugInfo/PDB/UDTLayout.h @@ -67,6 +67,8 @@ public: virtual uint32_t deepPaddingSize() const; const PDBSymbolData &getDataMember(); + bool hasUDTLayout() const; + const ClassLayout &getUDTLayout() const; private: std::unique_ptr DataMember; @@ -77,13 +79,24 @@ class VTableLayoutItem : public StorageItemBase { public: VTableLayoutItem(const UDTLayoutBase &Parent, std::unique_ptr VTable); + ArrayRef funcs() const { return VTableFuncs; } + + uint32_t getElementSize() const { return ElementSize; } + + void setFunction(uint32_t Index, PDBSymbolFunc &Func) { + VTableFuncs[Index] = &Func; + } private: + uint32_t ElementSize = 0; + std::unique_ptr Shape; std::unique_ptr VTable; - std::vector> VTableFuncs; + std::vector VTableFuncs; }; class UDTLayoutBase { + template using UniquePtrVector = std::vector>; + public: UDTLayoutBase(const PDBSymbol &Symbol, const std::string &Name, uint32_t Size); @@ -99,26 +112,37 @@ public: return ChildStorage; } - ArrayRef base_classes() const { return BaseClasses; } + VTableLayoutItem *findVTableAtOffset(uint32_t RelativeOffset); - ArrayRef> other_items() const { - return NonStorageItems; + StringRef getUDTName() const { return Name; } + + ArrayRef bases() const { return BaseClasses; } + ArrayRef> vbases() const { + return VirtualBases; } + ArrayRef> funcs() const { return Funcs; } + + ArrayRef> other_items() const { return Other; } + const PDBSymbol &getSymbolBase() const { return SymbolBase; } protected: void initializeChildren(const PDBSymbol &Sym); void addChildToLayout(std::unique_ptr Child); + void addVirtualOverride(PDBSymbolFunc &Func); + void addVirtualIntro(PDBSymbolFunc &Func); const PDBSymbol &SymbolBase; std::string Name; uint32_t SizeOf = 0; BitVector UsedBytes; - std::vector> NonStorageItems; - std::vector> ChildStorage; + UniquePtrVector Other; + UniquePtrVector Funcs; + UniquePtrVector VirtualBases; + UniquePtrVector ChildStorage; std::vector> ChildrenPerByte; std::vector BaseClasses; VTableLayoutItem *VTable = nullptr; @@ -129,6 +153,8 @@ public: explicit ClassLayout(const PDBSymbolTypeUDT &UDT); explicit ClassLayout(std::unique_ptr UDT); + ClassLayout(ClassLayout &&Other) = default; + const PDBSymbolTypeUDT &getClass() const { return UDT; } private: @@ -142,6 +168,7 @@ public: std::unique_ptr Base); const PDBSymbolTypeBaseClass &getBase() const { return *Base; } + bool isVirtualBase() const { return IsVirtualBase; } private: std::unique_ptr Base; diff --git a/lib/DebugInfo/PDB/PDBSymbol.cpp b/lib/DebugInfo/PDB/PDBSymbol.cpp index 14eb6ba8ad8..74010c2dd7d 100644 --- a/lib/DebugInfo/PDB/PDBSymbol.cpp +++ b/lib/DebugInfo/PDB/PDBSymbol.cpp @@ -159,6 +159,8 @@ PDBSymbol::findInlineFramesByRVA(uint32_t RVA) const { std::unique_ptr PDBSymbol::getChildStats(TagStats &Stats) const { std::unique_ptr Result(findAllChildren()); + if (!Result) + return nullptr; Stats.clear(); while (auto Child = Result->getNext()) { ++Stats[Child->getSymTag()]; diff --git a/lib/DebugInfo/PDB/PDBSymbolFunc.cpp b/lib/DebugInfo/PDB/PDBSymbolFunc.cpp index 3c0bd25ed09..0734a1f8314 100644 --- a/lib/DebugInfo/PDB/PDBSymbolFunc.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolFunc.cpp @@ -95,3 +95,14 @@ PDBSymbolFunc::getArguments() const { } void PDBSymbolFunc::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); } + +bool PDBSymbolFunc::isDestructor() const { + std::string Name = getName(); + if (Name.empty()) + return false; + if (Name[0] == '~') + return true; + if (Name == "__vecDelDtor") + return true; + return false; +} diff --git a/lib/DebugInfo/PDB/UDTLayout.cpp b/lib/DebugInfo/PDB/UDTLayout.cpp index 5751e020cb7..61cef093d4c 100644 --- a/lib/DebugInfo/PDB/UDTLayout.cpp +++ b/lib/DebugInfo/PDB/UDTLayout.cpp @@ -9,6 +9,7 @@ #include "llvm/DebugInfo/PDB/UDTLayout.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/DebugInfo/PDB/IPDBSession.h" #include "llvm/DebugInfo/PDB/PDBSymbol.h" #include "llvm/DebugInfo/PDB/PDBSymbolData.h" @@ -70,6 +71,12 @@ const PDBSymbolData &DataMemberLayoutItem::getDataMember() { return *dyn_cast(&Symbol); } +bool DataMemberLayoutItem::hasUDTLayout() const { return UdtLayout != nullptr; } + +const ClassLayout &DataMemberLayoutItem::getUDTLayout() const { + return *UdtLayout; +} + uint32_t DataMemberLayoutItem::deepPaddingSize() const { uint32_t Result = StorageItemBase::deepPaddingSize(); if (UdtLayout) @@ -81,31 +88,13 @@ VTableLayoutItem::VTableLayoutItem(const UDTLayoutBase &Parent, std::unique_ptr VTable) : StorageItemBase(Parent, *VTable, "", 0, getTypeLength(*VTable)), VTable(std::move(VTable)) { - // initialize vtbl methods. auto VTableType = cast(this->VTable->getType()); - uint32_t PointerSize = VTableType->getLength(); + ElementSize = VTableType->getLength(); - if (auto Shape = unique_dyn_cast( - VTableType->getPointeeType())) { + Shape = + unique_dyn_cast(VTableType->getPointeeType()); + if (Shape) VTableFuncs.resize(Shape->getCount()); - - auto ParentFunctions = - Parent.getSymbolBase().findAllChildren(); - while (auto Func = ParentFunctions->getNext()) { - if (Func->isVirtual()) { - uint32_t Index = Func->getVirtualBaseOffset(); - assert(Index % PointerSize == 0); - Index /= PointerSize; - - // Don't allow a compiler generated function to overwrite a user - // function in the VTable. Not sure why this happens, but a function - // named __vecDelDtor sometimes shows up on top of the destructor. - if (Func->isCompilerGenerated() && VTableFuncs[Index]) - continue; - VTableFuncs[Index] = std::move(Func); - } - } - } } UDTLayoutBase::UDTLayoutBase(const PDBSymbol &Symbol, const std::string &Name, @@ -145,44 +134,191 @@ uint32_t UDTLayoutBase::deepPaddingSize() const { } void UDTLayoutBase::initializeChildren(const PDBSymbol &Sym) { + // Handled bases first, followed by VTables, followed by data members, + // followed by functions, followed by other. This ordering is necessary + // so that bases and vtables get initialized before any functions which + // may override them. + + UniquePtrVector Bases; + UniquePtrVector VTables; + UniquePtrVector Members; auto Children = Sym.findAllChildren(); while (auto Child = Children->getNext()) { - if (auto Data = unique_dyn_cast(Child)) { - if (Data->getDataKind() == PDB_DataKind::Member) { - auto DM = - llvm::make_unique(*this, std::move(Data)); - - addChildToLayout(std::move(DM)); - } else { - NonStorageItems.push_back(std::move(Data)); - } - continue; + if (auto Base = unique_dyn_cast(Child)) { + if (Base->isVirtualBaseClass()) + VirtualBases.push_back(std::move(Base)); + else + Bases.push_back(std::move(Base)); } - if (auto Base = unique_dyn_cast(Child)) { - auto BL = llvm::make_unique(*this, std::move(Base)); - BaseClasses.push_back(BL.get()); + else if (auto Data = unique_dyn_cast(Child)) { + if (Data->getDataKind() == PDB_DataKind::Member) + Members.push_back(std::move(Data)); + else + Other.push_back(std::move(Child)); + } else if (auto VT = unique_dyn_cast(Child)) + VTables.push_back(std::move(VT)); + else if (auto Func = unique_dyn_cast(Child)) + Funcs.push_back(std::move(Func)); + else + Other.push_back(std::move(Child)); + } - addChildToLayout(std::move(BL)); + for (auto &Base : Bases) { + auto BL = llvm::make_unique(*this, std::move(Base)); + BaseClasses.push_back(BL.get()); + + addChildToLayout(std::move(BL)); + } + + for (auto &VT : VTables) { + auto VTLayout = llvm::make_unique(*this, std::move(VT)); + + VTable = VTLayout.get(); + + addChildToLayout(std::move(VTLayout)); + continue; + } + + for (auto &Data : Members) { + auto DM = llvm::make_unique(*this, std::move(Data)); + + addChildToLayout(std::move(DM)); + } + + for (auto &Func : Funcs) { + if (!Func->isVirtual()) continue; - } - if (auto VT = unique_dyn_cast(Child)) { - auto VTLayout = llvm::make_unique(*this, std::move(VT)); + if (Func->isIntroVirtualFunction()) + addVirtualIntro(*Func); + else + addVirtualOverride(*Func); + } +} + +void UDTLayoutBase::addVirtualIntro(PDBSymbolFunc &Func) { + // Kind of a hack, but we prefer the more common destructor name that people + // are familiar with, e.g. ~ClassName. It seems there are always both and + // the vector deleting destructor overwrites the nice destructor, so just + // ignore the vector deleting destructor. + if (Func.getName() == "__vecDelDtor") + return; + + if (!VTable) { + // FIXME: Handle this. What's most likely happening is we have an intro + // virtual in a derived class where the base also has an intro virtual. + // In this case the vtable lives in the base. What we really need is + // for each UDTLayoutBase to contain a list of all its vtables, and + // then propagate this list up the hierarchy so that derived classes have + // direct access to their bases' vtables. + return; + } - VTable = VTLayout.get(); + uint32_t Stride = VTable->getElementSize(); + + uint32_t Index = Func.getVirtualBaseOffset(); + assert(Index % Stride == 0); + Index /= Stride; + + VTable->setFunction(Index, Func); +} - addChildToLayout(std::move(VTLayout)); +VTableLayoutItem *UDTLayoutBase::findVTableAtOffset(uint32_t RelativeOffset) { + if (VTable && VTable->getOffsetInParent() == RelativeOffset) + return VTable; + for (auto Base : BaseClasses) { + uint32_t Begin = Base->getOffsetInParent(); + uint32_t End = Begin + Base->getSize(); + if (RelativeOffset < Begin || RelativeOffset >= End) + continue; + + return Base->findVTableAtOffset(RelativeOffset - Begin); + } + + return nullptr; +} + +void UDTLayoutBase::addVirtualOverride(PDBSymbolFunc &Func) { + auto Signature = Func.getSignature(); + auto ThisAdjust = Signature->getThisAdjust(); + // ThisAdjust tells us which VTable we're looking for. Specifically, it's + // the offset into the current class of the VTable we're looking for. So + // look through the base hierarchy until we find one such that + // AbsoluteOffset(VT) == ThisAdjust + VTableLayoutItem *VT = findVTableAtOffset(ThisAdjust); + if (!VT) { + // FIXME: There really should be a vtable here. If there's not it probably + // means that the vtable is in a virtual base, which we don't yet support. + assert(!VirtualBases.empty()); + return; + } + int32_t OverrideIndex = -1; + // Now we've found the VTable. Func will not have a virtual base offset set, + // so instead we need to compare names and signatures. We iterate each item + // in the VTable. All items should already have non null entries because they + // were initialized by the intro virtual, which was guaranteed to come before. + for (auto ItemAndIndex : enumerate(VT->funcs())) { + auto Item = ItemAndIndex.value(); + assert(Item); + // If the name doesn't match, this isn't an override. Note that it's ok + // for the return type to not match (e.g. co-variant return). + if (Item->getName() != Func.getName()) { + if (Item->isDestructor() && Func.isDestructor()) { + OverrideIndex = ItemAndIndex.index(); + break; + } continue; } + // Now make sure it's the right overload. Get the signature of the existing + // vtable method and make sure it has the same arglist and the same cv-ness. + auto ExistingSig = Item->getSignature(); + if (ExistingSig->isConstType() != Signature->isConstType()) + continue; + if (ExistingSig->isVolatileType() != Signature->isVolatileType()) + continue; - NonStorageItems.push_back(std::move(Child)); + // Now compare arguments. Using the raw bytes of the PDB this would be + // trivial + // because there is an ArgListId and they should be identical. But DIA + // doesn't + // expose this, so the best we can do is iterate each argument and confirm + // that + // each one is identical. + if (ExistingSig->getCount() != Signature->getCount()) + continue; + bool IsMatch = true; + auto ExistingEnumerator = ExistingSig->getArguments(); + auto NewEnumerator = Signature->getArguments(); + for (uint32_t I = 0; I < ExistingEnumerator->getChildCount(); ++I) { + auto ExistingArg = ExistingEnumerator->getNext(); + auto NewArg = NewEnumerator->getNext(); + if (ExistingArg->getSymIndexId() != NewArg->getSymIndexId()) { + IsMatch = false; + break; + } + } + if (!IsMatch) + continue; + + // It's a match! Stick the new function into the VTable. + OverrideIndex = ItemAndIndex.index(); + break; } + if (OverrideIndex == -1) { + // FIXME: This is probably due to one of the other FIXMEs in this file. + return; + } + VT->setFunction(OverrideIndex, Func); } void UDTLayoutBase::addChildToLayout(std::unique_ptr Child) { uint32_t Begin = Child->getOffsetInParent(); uint32_t End = Begin + Child->getSize(); + // Due to the empty base optimization, End might point outside the bounds of + // the parent class. If that happens, just clamp the value. + End = std::min(End, getClassSize()); + UsedBytes.set(Begin, End); while (Begin != End) { ChildrenPerByte[Begin].push_back(Child.get()); diff --git a/test/tools/llvm-pdbdump/Inputs/SimplePaddingTest.cpp b/test/tools/llvm-pdbdump/Inputs/SimplePaddingTest.cpp index b10839beea2..b52af149533 100644 --- a/test/tools/llvm-pdbdump/Inputs/SimplePaddingTest.cpp +++ b/test/tools/llvm-pdbdump/Inputs/SimplePaddingTest.cpp @@ -116,6 +116,51 @@ struct SimplePadAggregate { // the presence of X will cause 3 bytes of padding to be injected. } N; +struct SimplePadVtable1 { + static void operator delete(void *ptr, size_t sz) {} + virtual ~SimplePadVtable1() {} + virtual void A1() {} + virtual void B1() {} +} O; + +struct SimplePadVtable2 { + static void operator delete(void *ptr, size_t sz) {} + virtual ~SimplePadVtable2() {} + virtual void X2() {} + virtual void Y2() {} + virtual void Z2() {} +} P; + +struct SimplePadVtable3 { + static void operator delete(void *ptr, size_t sz) {} + virtual ~SimplePadVtable3() {} + virtual void Foo3() {} + virtual void Bar3() {} + virtual void Baz3() {} + virtual void Buzz3() {} +} Q; + +struct SimplePadMultiVTables + : public SimplePadVtable1, + public SimplePadVtable2, + public SimplePadVtable3 { + + ~SimplePadMultiVTables() override {} + static void operator delete(void *ptr, size_t sz) {} + + // SimplePadVtable1 overrides + void A1() override {} + + // SimplePadVtable2 overrides + void Y2() override {} + void Z2() override {} + + // SimplePadVtable3 overrides + void Bar3() override {} + void Baz3() override {} + void Buzz3() override {} +} R; + int main(int argc, char **argv) { return 0; diff --git a/test/tools/llvm-pdbdump/Inputs/SimplePaddingTest.pdb b/test/tools/llvm-pdbdump/Inputs/SimplePaddingTest.pdb index 44207d60193..f0bd496c1c8 100644 Binary files a/test/tools/llvm-pdbdump/Inputs/SimplePaddingTest.pdb and b/test/tools/llvm-pdbdump/Inputs/SimplePaddingTest.pdb differ diff --git a/test/tools/llvm-pdbdump/simple-padding-graphical.test b/test/tools/llvm-pdbdump/simple-padding-graphical.test new file mode 100644 index 00000000000..aacb0a33045 --- /dev/null +++ b/test/tools/llvm-pdbdump/simple-padding-graphical.test @@ -0,0 +1,121 @@ +; RUN: llvm-pdbdump pretty -classes -class-definitions=graphical \ +; RUN: -include-types=SimplePad %p/Inputs/SimplePaddingTest.pdb > %t + +; RUN: FileCheck -input-file=%t %s -check-prefix=NO_PADDING +; RUN: FileCheck -input-file=%t %s -check-prefix=UNION +; RUN: FileCheck -input-file=%t %s -check-prefix=NESTED_UNION +; RUN: FileCheck -input-file=%t %s -check-prefix=PAD_FROM_FIELDS1 +; RUN: FileCheck -input-file=%t %s -check-prefix=PAD_FROM_FIELDS2 +; RUN: FileCheck -input-file=%t %s -check-prefix=NO_PAD_IN_BASE +; RUN: FileCheck -input-file=%t %s -check-prefix=PAD_IN_DERIVED +; RUN: FileCheck -input-file=%t %s -check-prefix=EMPTY_BASE +; RUN: FileCheck -input-file=%t %s -check-prefix=VFPTR +; RUN: FileCheck -input-file=%t %s -check-prefix=MULTIPLE_INHERIT +; RUN: FileCheck -input-file=%t %s -check-prefix=MULTIPLE_INHERIT2 +; RUN: FileCheck -input-file=%t %s -check-prefix=DEEP_INHERIT +; RUN: FileCheck -input-file=%t %s -check-prefix=AGGREGATE + +; NO_PADDING: struct SimplePadNoPadding [sizeof = 8] { +; NO_PADDING-NEXT: data +0x00 [sizeof=4] int X +; NO_PADDING-NEXT: data +0x04 [sizeof=4] int Y +; NO_PADDING-NEXT: } + +; UNION: struct SimplePadUnion [sizeof = 16] { +; UNION-NEXT: data +0x00 [sizeof=4] int X +; UNION-NEXT: data +0x00 [sizeof=8] __int64 Y +; UNION-NEXT: data +0x00 [sizeof=16] SimplePadUnion:: +; UNION-NEXT: data +0x00 [sizeof=4] int X +; UNION-NEXT: (4 bytes) +; UNION-NEXT: data +0x08 [sizeof=8] __int64 Y +; UNION-NEXT: } + +; NESTED_UNION: struct {{SimplePadUnion::.*}} [sizeof = 16] { +; NESTED_UNION-NEXT: data +0x00 [sizeof=4] int X +; NESTED_UNION-NEXT: (4 bytes) +; NESTED_UNION-NEXT: data +0x08 [sizeof=8] __int64 Y +; NESTED_UNION-NEXT: } + +; PAD_FROM_FIELDS1: struct SimplePadFields1 [sizeof = 4] { +; PAD_FROM_FIELDS1-NEXT: data +0x00 [sizeof=1] char A +; PAD_FROM_FIELDS1-NEXT: data +0x01 [sizeof=1] char B +; PAD_FROM_FIELDS1-NEXT: data +0x02 [sizeof=1] char C +; PAD_FROM_FIELDS1-NEXT: (1 bytes) +; PAD_FROM_FIELDS1-NEXT: } + +; PAD_FROM_FIELDS2: struct SimplePadFields2 [sizeof = 8] { +; PAD_FROM_FIELDS2-NEXT: data +0x00 [sizeof=4] int Y +; PAD_FROM_FIELDS2-NEXT: data +0x04 [sizeof=1] char X +; PAD_FROM_FIELDS2-NEXT: (3 bytes) +; PAD_FROM_FIELDS2-NEXT: } + +; NO_PAD_IN_BASE: struct SimplePadBase [sizeof = 4] { +; NO_PAD_IN_BASE-NEXT: data +0x00 [sizeof=4] int X +; NO_PAD_IN_BASE-NEXT: } + +; PAD_IN_DERIVED: struct SimplePadDerived [sizeof = 16] +; PAD_IN_DERIVED-NEXT: : public SimplePadBase { +; PAD_IN_DERIVED-NEXT: base +0x00 [sizeof=4] SimplePadBase +; PAD_IN_DERIVED-NEXT: data +0x00 [sizeof=4] int X +; PAD_IN_DERIVED-NEXT: (4 bytes) +; PAD_IN_DERIVED-NEXT: data +0x08 [sizeof=8] __int64 Y +; PAD_IN_DERIVED-NEXT: } + +; EMPTY_BASE: struct SimplePadEmpty [sizeof = 8] +; EMPTY_BASE-NEXT: : public SimplePadEmptyBase1 +; EMPTY_BASE-NEXT: , public SimplePadEmptyBase2 { +; EMPTY_BASE-NEXT: base +0x00 [sizeof=1] SimplePadEmptyBase1 +; EMPTY_BASE-NEXT: base +0x01 [sizeof=1] SimplePadEmptyBase2 +; EMPTY_BASE-NEXT: (2 bytes) +; EMPTY_BASE-NEXT: data +0x04 [sizeof=4] int X +; EMPTY_BASE-NEXT: } + +; VFPTR: struct SimplePadVfptr [sizeof = 8] { +; VFPTR-NEXT: vfptr +0x00 [sizeof=4] +; VFPTR-NEXT: [0] &SimplePadVfptr::~SimplePadVfptr +; VFPTR-NEXT: data +0x04 [sizeof=4] int X +; VFPTR-NEXT: } + +; MULTIPLE_INHERIT: struct SimplePadMultiInherit [sizeof = 8] +; MULTIPLE_INHERIT-NEXT: : public NonEmptyBase1 +; MULTIPLE_INHERIT-NEXT: , public NonEmptyBase2 { +; MULTIPLE_INHERIT-NEXT: base +0x00 [sizeof=1] NonEmptyBase1 +; MULTIPLE_INHERIT-NEXT: data +0x00 [sizeof=1] bool X +; MULTIPLE_INHERIT-NEXT: base +0x01 [sizeof=1] NonEmptyBase2 +; MULTIPLE_INHERIT-NEXT: data +0x01 [sizeof=1] bool Y +; MULTIPLE_INHERIT-NEXT: (2 bytes) +; MULTIPLE_INHERIT-NEXT: data +0x04 [sizeof=4] int X +; MULTIPLE_INHERIT-NEXT: } + +; MULTIPLE_INHERIT2: SimplePadMultiInherit2 [sizeof = 16] +; MULTIPLE_INHERIT2-NEXT: : public SimplePadFields1 +; MULTIPLE_INHERIT2-NEXT: , public SimplePadFields2 { +; MULTIPLE_INHERIT2-NEXT: base +0x00 [sizeof=4] SimplePadFields1 +; MULTIPLE_INHERIT2-NEXT: data +0x00 [sizeof=1] char A +; MULTIPLE_INHERIT2-NEXT: data +0x01 [sizeof=1] char B +; MULTIPLE_INHERIT2-NEXT: data +0x02 [sizeof=1] char C +; MULTIPLE_INHERIT2-NEXT: (1 bytes) +; MULTIPLE_INHERIT2-NEXT: base +0x04 [sizeof=8] SimplePadFields2 +; MULTIPLE_INHERIT2-NEXT: data +0x04 [sizeof=4] int Y +; MULTIPLE_INHERIT2-NEXT: data +0x08 [sizeof=1] char X +; MULTIPLE_INHERIT2-NEXT: (3 bytes) +; MULTIPLE_INHERIT2-NEXT: data +0x0c [sizeof=4] int X +; MULTIPLE_INHERIT2-NEXT: } + +; DEEP_INHERIT: struct SimplePadTwoLevelInherit [sizeof = 16] +; DEEP_INHERIT-NEXT: : public OneLevelInherit { +; DEEP_INHERIT-NEXT: base +0x00 [sizeof=4] OneLevelInherit +; DEEP_INHERIT-NEXT: base +0x00 [sizeof=1] NonEmptyBase1 +; DEEP_INHERIT-NEXT: data +0x00 [sizeof=1] bool X +; DEEP_INHERIT-NEXT: (1 bytes) +; DEEP_INHERIT-NEXT: data +0x02 [sizeof=2] short Y +; DEEP_INHERIT-NEXT: (4 bytes) +; DEEP_INHERIT-NEXT: data +0x08 [sizeof=8] __int64 Z +; DEEP_INHERIT-NEXT: } + + +; AGGREGATE: struct SimplePadAggregate [sizeof = 8] { +; AGGREGATE-NEXT: data +0x00 [sizeof=1] NonEmptyBase1 X +; AGGREGATE-NEXT: data +0x00 [sizeof=1] bool X +; AGGREGATE-NEXT: (3 bytes) +; AGGREGATE-NEXT: data +0x04 [sizeof=4] int Y +; AGGREGATE-NEXT: } diff --git a/test/tools/llvm-pdbdump/simple-padding.test b/test/tools/llvm-pdbdump/simple-padding-text.test similarity index 98% rename from test/tools/llvm-pdbdump/simple-padding.test rename to test/tools/llvm-pdbdump/simple-padding-text.test index 8f835f154fa..b33af565f65 100644 --- a/test/tools/llvm-pdbdump/simple-padding.test +++ b/test/tools/llvm-pdbdump/simple-padding-text.test @@ -63,7 +63,7 @@ ; EMPTY_BASE-NEXT: } ; VFPTR: struct SimplePadVfptr [sizeof = 8] { -; VFPTR-NEXT: data +0x00 [sizeof=4] __vfptr +; VFPTR-NEXT: vfptr +0x00 [sizeof=4] ; VFPTR-NEXT: data +0x04 [sizeof=4] int X ; VFPTR-NEXT: } diff --git a/tools/llvm-pdbdump/LinePrinter.cpp b/tools/llvm-pdbdump/LinePrinter.cpp index e5dd66fd9aa..d4a5a8d859e 100644 --- a/tools/llvm-pdbdump/LinePrinter.cpp +++ b/tools/llvm-pdbdump/LinePrinter.cpp @@ -12,6 +12,7 @@ #include "llvm-pdbdump.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/PDB/UDTLayout.h" #include "llvm/Support/Regex.h" #include @@ -70,8 +71,20 @@ void LinePrinter::NewLine() { OS.indent(CurrentIndent); } -bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName) { - return IsItemExcluded(TypeName, IncludeTypeFilters, ExcludeTypeFilters); +bool LinePrinter::IsClassExcluded(const ClassLayout &Class) { + if (IsTypeExcluded(Class.getUDTName(), Class.getClassSize())) + return true; + if (Class.deepPaddingSize() < opts::pretty::PaddingThreshold) + return true; + return false; +} + +bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName, uint32_t Size) { + if (IsItemExcluded(TypeName, IncludeTypeFilters, ExcludeTypeFilters)) + return true; + if (Size < opts::pretty::SizeThreshold) + return true; + return false; } bool LinePrinter::IsSymbolExcluded(llvm::StringRef SymbolName) { diff --git a/tools/llvm-pdbdump/LinePrinter.h b/tools/llvm-pdbdump/LinePrinter.h index 8b3d8755ad8..1a922feb1e6 100644 --- a/tools/llvm-pdbdump/LinePrinter.h +++ b/tools/llvm-pdbdump/LinePrinter.h @@ -20,6 +20,8 @@ namespace llvm { namespace pdb { +class ClassLayout; + class LinePrinter { friend class WithColor; @@ -34,7 +36,8 @@ public: raw_ostream &getStream() { return OS; } int getIndentLevel() const { return CurrentIndent; } - bool IsTypeExcluded(llvm::StringRef TypeName); + bool IsClassExcluded(const ClassLayout &Class); + bool IsTypeExcluded(llvm::StringRef TypeName, uint32_t Size); bool IsSymbolExcluded(llvm::StringRef SymbolName); bool IsCompilandExcluded(llvm::StringRef CompilandName); diff --git a/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp b/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp index ba829e794ea..9f213a4b4d9 100644 --- a/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp +++ b/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp @@ -33,15 +33,15 @@ void ClassDefinitionDumper::start(const PDBSymbolTypeUDT &Class) { opts::pretty::ClassDefinitionFormat::None); ClassLayout Layout(Class); + start(Layout); +} - if (opts::pretty::OnlyPaddingClasses && (Layout.shallowPaddingSize() == 0)) - return; - +void ClassDefinitionDumper::start(const ClassLayout &Layout) { prettyPrintClassIntro(Layout); switch (opts::pretty::ClassFormat) { case opts::pretty::ClassDefinitionFormat::Graphical: { - PrettyClassLayoutGraphicalDumper Dumper(Printer); + PrettyClassLayoutGraphicalDumper Dumper(Printer, 0); DumpedAnything = Dumper.start(Layout); break; } @@ -58,6 +58,20 @@ void ClassDefinitionDumper::start(const PDBSymbolTypeUDT &Class) { prettyPrintClassOutro(Layout); } +static void printBase(LinePrinter &Printer, const PDBSymbolTypeBaseClass &Base, + uint32_t &CurIndex, uint32_t TotalBaseCount, + bool IsVirtual) { + Printer << " "; + WithColor(Printer, PDB_ColorItem::Keyword).get() << Base.getAccess(); + if (IsVirtual) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " virtual"; + WithColor(Printer, PDB_ColorItem::Type).get() << " " << Base.getName(); + if (++CurIndex < TotalBaseCount) { + Printer.NewLine(); + Printer << ","; + } +} + void ClassDefinitionDumper::prettyPrintClassIntro(const ClassLayout &Layout) { DumpedAnything = false; Printer.NewLine(); @@ -69,24 +83,22 @@ void ClassDefinitionDumper::prettyPrintClassIntro(const ClassLayout &Layout) { WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName(); WithColor(Printer, PDB_ColorItem::Comment).get() << " [sizeof = " << Size << "]"; - uint32_t BaseCount = Layout.base_classes().size(); - if (BaseCount > 0) { + uint32_t BaseCount = Layout.bases().size(); + uint32_t VBaseCount = Layout.vbases().size(); + uint32_t TotalBaseCount = BaseCount + VBaseCount; + if (TotalBaseCount > 0) { Printer.Indent(); Printer.NewLine(); Printer << ":"; uint32_t BaseIndex = 0; - for (auto BC : Layout.base_classes()) { + for (auto BC : Layout.bases()) { const auto &Base = BC->getBase(); - Printer << " "; - WithColor(Printer, PDB_ColorItem::Keyword).get() << Base.getAccess(); - if (Base.isVirtualBaseClass()) - WithColor(Printer, PDB_ColorItem::Keyword).get() << " virtual"; - WithColor(Printer, PDB_ColorItem::Type).get() << " " << Base.getName(); - if (++BaseIndex < BaseCount) { - Printer.NewLine(); - Printer << ","; - } + printBase(Printer, Base, BaseIndex, TotalBaseCount, false); } + for (auto &BC : Layout.vbases()) { + printBase(Printer, *BC, BaseIndex, TotalBaseCount, true); + } + Printer.Unindent(); } diff --git a/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h b/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h index 6b24c7c8398..0e27733b3cc 100644 --- a/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h +++ b/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h @@ -32,7 +32,8 @@ class ClassDefinitionDumper : public PDBSymDumper { public: ClassDefinitionDumper(LinePrinter &P); - void start(const PDBSymbolTypeUDT &Exe); + void start(const PDBSymbolTypeUDT &Class); + void start(const ClassLayout &Class); private: void prettyPrintClassIntro(const ClassLayout &Class); diff --git a/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.cpp b/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.cpp index 13368934ed8..d146ca9d471 100644 --- a/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.cpp +++ b/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.cpp @@ -9,30 +9,143 @@ #include "PrettyClassLayoutGraphicalDumper.h" +#include "LinePrinter.h" +#include "PrettyClassDefinitionDumper.h" +#include "PrettyVariableDumper.h" + +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/DebugInfo/PDB/UDTLayout.h" +#include "llvm/Support/Format.h" + using namespace llvm; using namespace llvm::pdb; PrettyClassLayoutGraphicalDumper::PrettyClassLayoutGraphicalDumper( - LinePrinter &P) - : PDBSymDumper(true) {} + LinePrinter &P, uint32_t InitialOffset) + : PDBSymDumper(true), Printer(P), ClassOffsetZero(InitialOffset), + CurrentAbsoluteOffset(InitialOffset) {} + +bool PrettyClassLayoutGraphicalDumper::start(const UDTLayoutBase &Layout) { + const BitVector &UseMap = Layout.usedBytes(); + int NextPaddingByte = UseMap.find_first_unset(); + + for (auto &Item : Layout.layout_items()) { + // Calculate the absolute offset of the first byte of the next field. + uint32_t RelativeOffset = Item->getOffsetInParent(); + CurrentAbsoluteOffset = ClassOffsetZero + RelativeOffset; + + // Since there is storage there, it should be set! However, this might + // be an empty base, in which case it could extend outside the bounds of + // the parent class. + if (RelativeOffset < UseMap.size() && (Item->getSize() > 0)) { + assert(UseMap.test(RelativeOffset)); + + // If there is any remaining padding in this class, and the offset of the + // new item is after the padding, then we must have just jumped over some + // padding. Print a padding row and then look for where the next block + // of padding begins. + if ((NextPaddingByte >= 0) && + (RelativeOffset > uint32_t(NextPaddingByte))) { + printPaddingRow(RelativeOffset - NextPaddingByte); + NextPaddingByte = UseMap.find_next_unset(RelativeOffset); + } + } -bool PrettyClassLayoutGraphicalDumper::start(const ClassLayout &Layout) { - return false; + CurrentItem = Item.get(); + Item->getSymbol().dump(*this); + } + + if (NextPaddingByte >= 0 && Layout.getClassSize() > 1) { + uint32_t Amount = Layout.getClassSize() - NextPaddingByte; + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Padding).get() << " (" << Amount + << " bytes)"; + DumpedAnything = true; + } + + return DumpedAnything; +} + +void PrettyClassLayoutGraphicalDumper::printPaddingRow(uint32_t Amount) { + if (Amount == 0) + return; + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Padding).get() << " (" << Amount + << " bytes)"; + DumpedAnything = true; } void PrettyClassLayoutGraphicalDumper::dump( - const PDBSymbolTypeBaseClass &Symbol) {} + const PDBSymbolTypeBaseClass &Symbol) { + assert(CurrentItem != nullptr); -void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolData &Symbol) {} + Printer.NewLine(); + BaseClassLayout &Layout = static_cast(*CurrentItem); -void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeEnum &Symbol) {} + std::string Label = Layout.isVirtualBase() ? "vbase" : "base"; + Printer << Label << " "; -void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolFunc &Symbol) {} + WithColor(Printer, PDB_ColorItem::Offset).get() + << "+" << format_hex(CurrentAbsoluteOffset, 4) + << " [sizeof=" << Layout.getSize() << "] "; -void PrettyClassLayoutGraphicalDumper::dump( - const PDBSymbolTypeTypedef &Symbol) {} + WithColor(Printer, PDB_ColorItem::Identifier).get() << Layout.getName(); -void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeUDT &Symbol) {} + Printer.Indent(); + uint32_t ChildOffsetZero = ClassOffsetZero + Layout.getOffsetInParent(); + PrettyClassLayoutGraphicalDumper BaseDumper(Printer, ChildOffsetZero); + BaseDumper.start(Layout); + Printer.Unindent(); + + DumpedAnything = true; +} + +void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolData &Symbol) { + assert(CurrentItem != nullptr); + + DataMemberLayoutItem &Layout = + static_cast(*CurrentItem); + + VariableDumper VarDumper(Printer); + VarDumper.start(Symbol, ClassOffsetZero); + + if (Layout.hasUDTLayout()) { + Printer.Indent(); + PrettyClassLayoutGraphicalDumper TypeDumper(Printer, ClassOffsetZero); + TypeDumper.start(Layout.getUDTLayout()); + Printer.Unindent(); + } + + DumpedAnything = true; +} void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeVTable &Symbol) { + assert(CurrentItem != nullptr); + + VTableLayoutItem &Layout = static_cast(*CurrentItem); + + VariableDumper VarDumper(Printer); + VarDumper.start(Symbol, ClassOffsetZero); + + Printer.Indent(); + uint32_t Index = 0; + for (auto &Func : Layout.funcs()) { + Printer.NewLine(); + std::string Name = Func->getName(); + auto ParentClass = + unique_dyn_cast(Func->getClassParent()); + assert(ParentClass); + WithColor(Printer, PDB_ColorItem::Address).get() << " [" << Index << "] "; + WithColor(Printer, PDB_ColorItem::Identifier).get() + << "&" << ParentClass->getName(); + Printer << "::"; + WithColor(Printer, PDB_ColorItem::Identifier).get() << Name; + ++Index; + } + Printer.Unindent(); + + DumpedAnything = true; } diff --git a/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.h b/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.h index 6b35b23a855..7dfb74c4e14 100644 --- a/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.h +++ b/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.h @@ -18,22 +18,29 @@ namespace llvm { namespace pdb { -class ClassLayout; +class UDTLayoutBase; +class StorageItemBase; class LinePrinter; class PrettyClassLayoutGraphicalDumper : public PDBSymDumper { public: - explicit PrettyClassLayoutGraphicalDumper(LinePrinter &P); + PrettyClassLayoutGraphicalDumper(LinePrinter &P, uint32_t InitialOffset); - bool start(const ClassLayout &Layout); + bool start(const UDTLayoutBase &Layout); void dump(const PDBSymbolTypeBaseClass &Symbol) override; void dump(const PDBSymbolData &Symbol) override; - void dump(const PDBSymbolTypeEnum &Symbol) override; - void dump(const PDBSymbolFunc &Symbol) override; - void dump(const PDBSymbolTypeTypedef &Symbol) override; - void dump(const PDBSymbolTypeUDT &Symbol) override; void dump(const PDBSymbolTypeVTable &Symbol) override; + +private: + void printPaddingRow(uint32_t Amount); + + LinePrinter &Printer; + + StorageItemBase *CurrentItem = nullptr; + uint32_t ClassOffsetZero = 0; + uint32_t CurrentAbsoluteOffset = 0; + bool DumpedAnything = false; }; } } diff --git a/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.cpp b/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.cpp index de654d3f81d..02f31108b0d 100644 --- a/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.cpp +++ b/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.cpp @@ -38,6 +38,8 @@ bool PrettyClassLayoutTextDumper::start(const ClassLayout &Layout) { opts::pretty::ClassDefinitionFormat::Standard) { for (auto &Other : Layout.other_items()) Other->dump(*this); + for (auto &Func : Layout.funcs()) + Func->dump(*this); } const BitVector &UseMap = Layout.usedBytes(); @@ -101,9 +103,6 @@ void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeVTable &Symbol) { } void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeEnum &Symbol) { - if (Printer.IsTypeExcluded(Symbol.getName())) - return; - DumpedAnything = true; Printer.NewLine(); EnumDumper Dumper(Printer); @@ -111,9 +110,6 @@ void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeEnum &Symbol) { } void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeTypedef &Symbol) { - if (Printer.IsTypeExcluded(Symbol.getName())) - return; - DumpedAnything = true; Printer.NewLine(); TypedefDumper Dumper(Printer); diff --git a/tools/llvm-pdbdump/PrettyTypeDumper.cpp b/tools/llvm-pdbdump/PrettyTypeDumper.cpp index 2857b07078c..ffeef72150d 100644 --- a/tools/llvm-pdbdump/PrettyTypeDumper.cpp +++ b/tools/llvm-pdbdump/PrettyTypeDumper.cpp @@ -22,24 +22,82 @@ #include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/DebugInfo/PDB/UDTLayout.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/FormatVariadic.h" using namespace llvm; using namespace llvm::pdb; +using LayoutPtr = std::unique_ptr; + +typedef bool (*CompareFunc)(const LayoutPtr &S1, const LayoutPtr &S2); + +static bool CompareNames(const LayoutPtr &S1, const LayoutPtr &S2) { + return S1->getUDTName() < S2->getUDTName(); +} + +static bool CompareSizes(const LayoutPtr &S1, const LayoutPtr &S2) { + return S1->getClassSize() < S2->getClassSize(); +} + +static bool ComparePadding(const LayoutPtr &S1, const LayoutPtr &S2) { + return S1->deepPaddingSize() < S2->deepPaddingSize(); +} + +static CompareFunc getComparisonFunc(opts::pretty::ClassSortMode Mode) { + switch (Mode) { + case opts::pretty::ClassSortMode::Name: + return CompareNames; + case opts::pretty::ClassSortMode::Size: + return CompareSizes; + case opts::pretty::ClassSortMode::Padding: + return ComparePadding; + default: + return nullptr; + } +} + template -static std::vector> -filterClassDefs(LinePrinter &Printer, Enumerator &E) { - std::vector> Filtered; +static std::vector> +filterAndSortClassDefs(LinePrinter &Printer, Enumerator &E, + uint32_t UnfilteredCount) { + std::vector> Filtered; + + Filtered.reserve(UnfilteredCount); + CompareFunc Comp = getComparisonFunc(opts::pretty::ClassOrder); + + uint32_t Examined = 0; + uint32_t Discarded = 0; while (auto Class = E.getNext()) { - if (Class->getUnmodifiedTypeId() != 0) + ++Examined; + if (Examined % 10000 == 0) { + outs() << formatv("Examined {0}/{1} items. {2} items discarded\n", + Examined, UnfilteredCount, Discarded); + outs().flush(); + } + + if (Class->getUnmodifiedTypeId() != 0) { + ++Discarded; continue; + } - if (Printer.IsTypeExcluded(Class->getName())) + if (Printer.IsTypeExcluded(Class->getName(), Class->getLength())) { + ++Discarded; continue; + } - Filtered.push_back(std::move(Class)); + auto Layout = llvm::make_unique(std::move(Class)); + if (Layout->deepPaddingSize() < opts::pretty::PaddingThreshold) { + ++Discarded; + continue; + } + + Filtered.push_back(std::move(Layout)); } + if (Comp) + std::sort(Filtered.begin(), Filtered.end(), Comp); return Filtered; } @@ -70,20 +128,52 @@ void TypeDumper::start(const PDBSymbolExe &Exe) { if (opts::pretty::Classes) { auto Classes = Exe.findAllChildren(); - auto Filtered = filterClassDefs(Printer, *Classes); - - Printer.NewLine(); - uint32_t Shown = Filtered.size(); uint32_t All = Classes->getChildCount(); + Printer.NewLine(); WithColor(Printer, PDB_ColorItem::Identifier).get() << "Classes"; + + bool Precompute = false; + Precompute = + (opts::pretty::ClassOrder != opts::pretty::ClassSortMode::None); + + // If we're using no sort mode, then we can start getting immediate output + // from the tool by just filtering as we go, rather than processing + // everything up front so that we can sort it. This makes the tool more + // responsive. So only precompute the filtered/sorted set of classes if + // necessary due to the specified options. + std::vector Filtered; + uint32_t Shown = All; + if (Precompute) { + Filtered = filterAndSortClassDefs(Printer, *Classes, All); + + Shown = Filtered.size(); + } + Printer << ": (Showing " << Shown << " items"; if (Shown < All) Printer << ", " << (All - Shown) << " filtered"; Printer << ")"; Printer.Indent(); - for (auto &Class : Filtered) - Class->dump(*this); + + // If we pre-computed, iterate the filtered/sorted list, otherwise iterate + // the DIA enumerator and filter on the fly. + if (Precompute) { + for (auto &Class : Filtered) + dumpClassLayout(*Class); + } else { + while (auto Class = Classes->getNext()) { + if (Printer.IsTypeExcluded(Class->getName(), Class->getLength())) + continue; + + auto Layout = llvm::make_unique(std::move(Class)); + if (Layout->deepPaddingSize() < opts::pretty::PaddingThreshold) + continue; + + dumpClassLayout(*Layout); + } + } + Printer.Unindent(); } } @@ -91,7 +181,7 @@ void TypeDumper::start(const PDBSymbolExe &Exe) { void TypeDumper::dump(const PDBSymbolTypeEnum &Symbol) { assert(opts::pretty::Enums); - if (Printer.IsTypeExcluded(Symbol.getName())) + if (Printer.IsTypeExcluded(Symbol.getName(), Symbol.getLength())) return; // Dump member enums when dumping their class definition. if (nullptr != Symbol.getClassParent()) @@ -105,7 +195,7 @@ void TypeDumper::dump(const PDBSymbolTypeEnum &Symbol) { void TypeDumper::dump(const PDBSymbolTypeTypedef &Symbol) { assert(opts::pretty::Typedefs); - if (Printer.IsTypeExcluded(Symbol.getName())) + if (Printer.IsTypeExcluded(Symbol.getName(), Symbol.getLength())) return; Printer.NewLine(); @@ -113,15 +203,15 @@ void TypeDumper::dump(const PDBSymbolTypeTypedef &Symbol) { Dumper.start(Symbol); } -void TypeDumper::dump(const PDBSymbolTypeUDT &Symbol) { +void TypeDumper::dumpClassLayout(const ClassLayout &Class) { assert(opts::pretty::Classes); if (opts::pretty::ClassFormat == opts::pretty::ClassDefinitionFormat::None) { Printer.NewLine(); WithColor(Printer, PDB_ColorItem::Keyword).get() << "class "; - WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << Class.getUDTName(); } else { ClassDefinitionDumper Dumper(Printer); - Dumper.start(Symbol); + Dumper.start(Class); } } diff --git a/tools/llvm-pdbdump/PrettyTypeDumper.h b/tools/llvm-pdbdump/PrettyTypeDumper.h index f9d8304c320..68a2f0246eb 100644 --- a/tools/llvm-pdbdump/PrettyTypeDumper.h +++ b/tools/llvm-pdbdump/PrettyTypeDumper.h @@ -15,6 +15,7 @@ namespace llvm { namespace pdb { class LinePrinter; +class ClassLayout; class TypeDumper : public PDBSymDumper { public: @@ -24,7 +25,8 @@ public: void dump(const PDBSymbolTypeEnum &Symbol) override; void dump(const PDBSymbolTypeTypedef &Symbol) override; - void dump(const PDBSymbolTypeUDT &Symbol) override; + + void dumpClassLayout(const ClassLayout &Class); private: LinePrinter &Printer; diff --git a/tools/llvm-pdbdump/PrettyVariableDumper.cpp b/tools/llvm-pdbdump/PrettyVariableDumper.cpp index ef9a9b51bd0..76a0d23bf87 100644 --- a/tools/llvm-pdbdump/PrettyVariableDumper.cpp +++ b/tools/llvm-pdbdump/PrettyVariableDumper.cpp @@ -35,7 +35,7 @@ using namespace llvm::pdb; VariableDumper::VariableDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {} -void VariableDumper::start(const PDBSymbolData &Var) { +void VariableDumper::start(const PDBSymbolData &Var, uint32_t Offset) { if (Var.isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated) return; if (Printer.IsSymbolExcluded(Var.getName())) @@ -68,16 +68,16 @@ void VariableDumper::start(const PDBSymbolData &Var) { Printer.NewLine(); Printer << "data "; WithColor(Printer, PDB_ColorItem::Offset).get() - << "+" << format_hex(Var.getOffset(), 4) << " [sizeof=" << Length - << "] "; + << "+" << format_hex(Offset + Var.getOffset(), 4) + << " [sizeof=" << Length << "] "; dumpSymbolTypeAndName(*VarType, Var.getName()); break; case PDB_LocType::BitField: Printer.NewLine(); Printer << "data "; WithColor(Printer, PDB_ColorItem::Offset).get() - << "+" << format_hex(Var.getOffset(), 4) << " [sizeof=" << Length - << "] "; + << "+" << format_hex(Offset + Var.getOffset(), 4) + << " [sizeof=" << Length << "] "; dumpSymbolTypeAndName(*VarType, Var.getName()); Printer << " : "; WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Var.getLength(); @@ -91,17 +91,15 @@ void VariableDumper::start(const PDBSymbolData &Var) { } } -void VariableDumper::start(const PDBSymbolTypeVTable &Var) { +void VariableDumper::start(const PDBSymbolTypeVTable &Var, uint32_t Offset) { Printer.NewLine(); - Printer << "data "; + Printer << "vfptr "; auto VTableType = cast(Var.getType()); uint32_t PointerSize = VTableType->getLength(); WithColor(Printer, PDB_ColorItem::Offset).get() - << "+" << format_hex(Var.getOffset(), 4) << " [sizeof=" << PointerSize - << "] "; - - WithColor(Printer, PDB_ColorItem::Identifier).get() << " __vfptr"; + << "+" << format_hex(Offset + Var.getOffset(), 4) + << " [sizeof=" << PointerSize << "] "; } void VariableDumper::dump(const PDBSymbolTypeArray &Symbol) { diff --git a/tools/llvm-pdbdump/PrettyVariableDumper.h b/tools/llvm-pdbdump/PrettyVariableDumper.h index ba9fdb17655..4ba3bc97d85 100644 --- a/tools/llvm-pdbdump/PrettyVariableDumper.h +++ b/tools/llvm-pdbdump/PrettyVariableDumper.h @@ -24,8 +24,8 @@ class VariableDumper : public PDBSymDumper { public: VariableDumper(LinePrinter &P); - void start(const PDBSymbolData &Var); - void start(const PDBSymbolTypeVTable &Var); + void start(const PDBSymbolData &Var, uint32_t Offset = 0); + void start(const PDBSymbolTypeVTable &Var, uint32_t Offset = 0); void dump(const PDBSymbolTypeArray &Symbol) override; void dump(const PDBSymbolTypeBuiltin &Symbol) override; diff --git a/tools/llvm-pdbdump/llvm-pdbdump.cpp b/tools/llvm-pdbdump/llvm-pdbdump.cpp index 6d39871699b..06c2afc0bc7 100644 --- a/tools/llvm-pdbdump/llvm-pdbdump.cpp +++ b/tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -122,15 +122,27 @@ cl::opt Enums("enums", cl::desc("Display enum types"), cl::cat(TypeCategory), cl::sub(PrettySubcommand)); cl::opt Typedefs("typedefs", cl::desc("Display typedef types"), cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt ClassOrder( + "class-order", cl::desc("Class sort order"), cl::init(ClassSortMode::None), + cl::values(clEnumValN(ClassSortMode::None, "none", + "Undefined / no particular sort order"), + clEnumValN(ClassSortMode::Name, "name", "Sort classes by name"), + clEnumValN(ClassSortMode::Size, "size", "Sort classes by size"), + clEnumValN(ClassSortMode::Padding, "padding", + "Sort classes by amount of padding")), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); + cl::opt ClassFormat( "class-definitions", cl::desc("Class definition format"), cl::init(ClassDefinitionFormat::Standard), cl::values( clEnumValN(ClassDefinitionFormat::Standard, "all-members", "Display all class members including data, constants, " - "typedefs, etc"), + "typedefs, functions, etc"), clEnumValN(ClassDefinitionFormat::Layout, "layout-members", "Only display members that contribute to class size."), + clEnumValN(ClassDefinitionFormat::Graphical, "graphical", + "Display graphical representation of each class's layout."), clEnumValN(ClassDefinitionFormat::None, "none", "Don't display class definitions")), cl::cat(TypeCategory), cl::sub(PrettySubcommand)); @@ -173,10 +185,14 @@ cl::list IncludeCompilands( "include-compilands", cl::desc("Include only compilands those which match a regular expression"), cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand)); -cl::opt OnlyPaddingClasses( - "only-padding-classes", cl::desc("When dumping classes, only display those " - "with non-zero amounts of padding bytes"), - cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +cl::opt SizeThreshold( + "min-type-size", cl::desc("Displays only those types which are greater " + "than or equal to the specified size."), + cl::init(0), cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +cl::opt PaddingThreshold( + "min-class-padding", cl::desc("Displays only those classes which have at " + "least the specified amount of padding."), + cl::init(0), cl::cat(FilterCategory), cl::sub(PrettySubcommand)); cl::opt ExcludeCompilerGenerated( "no-compiler-generated", diff --git a/tools/llvm-pdbdump/llvm-pdbdump.h b/tools/llvm-pdbdump/llvm-pdbdump.h index 36a296087ab..a5429a253df 100644 --- a/tools/llvm-pdbdump/llvm-pdbdump.h +++ b/tools/llvm-pdbdump/llvm-pdbdump.h @@ -19,6 +19,7 @@ namespace opts { namespace pretty { enum class ClassDefinitionFormat { None, Layout, Graphical, Standard }; +enum class ClassSortMode { None, Name, Size, Padding }; extern llvm::cl::opt Compilands; extern llvm::cl::opt Symbols; @@ -36,7 +37,9 @@ extern llvm::cl::list ExcludeCompilands; extern llvm::cl::list IncludeTypes; extern llvm::cl::list IncludeSymbols; extern llvm::cl::list IncludeCompilands; -extern llvm::cl::opt OnlyPaddingClasses; +extern llvm::cl::opt ClassOrder; +extern llvm::cl::opt SizeThreshold; +extern llvm::cl::opt PaddingThreshold; extern llvm::cl::opt ClassFormat; }