mirror of
https://github.com/RPCS3/llvm.git
synced 2025-03-03 16:18:37 +00:00
[llvm-pdbdump] Recursively dump class layout.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@300258 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
a402bd1ba7
commit
10683346a5
@ -34,12 +34,11 @@ public:
|
||||
|
||||
std::unique_ptr<ChildType> getChildAtIndex(uint32_t Index) const override {
|
||||
std::unique_ptr<PDBSymbol> Child = Enumerator->getChildAtIndex(Index);
|
||||
return make_concrete_child(std::move(Child));
|
||||
return unique_dyn_cast_or_null<ChildType>(Child);
|
||||
}
|
||||
|
||||
std::unique_ptr<ChildType> getNext() override {
|
||||
std::unique_ptr<PDBSymbol> Child = Enumerator->getNext();
|
||||
return make_concrete_child(std::move(Child));
|
||||
return unique_dyn_cast_or_null<ChildType>(Enumerator->getNext());
|
||||
}
|
||||
|
||||
void reset() override { Enumerator->reset(); }
|
||||
@ -50,11 +49,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ChildType>
|
||||
make_concrete_child(std::unique_ptr<PDBSymbol> Child) const {
|
||||
ChildType *ConcreteChild = dyn_cast_or_null<ChildType>(Child.release());
|
||||
return std::unique_ptr<ChildType>(ConcreteChild);
|
||||
}
|
||||
|
||||
std::unique_ptr<IPDBEnumSymbols> Enumerator;
|
||||
};
|
||||
|
@ -27,6 +27,8 @@ public:
|
||||
|
||||
void dump(PDBSymDumper &Dumper) const override;
|
||||
|
||||
bool isDestructor() const;
|
||||
|
||||
std::unique_ptr<IPDBEnumChildren<PDBSymbolData>> getArguments() const;
|
||||
|
||||
DECLARE_PDB_SYMBOL_CONCRETE_TYPE(PDB_SymType::Function)
|
||||
|
@ -67,6 +67,8 @@ public:
|
||||
virtual uint32_t deepPaddingSize() const;
|
||||
|
||||
const PDBSymbolData &getDataMember();
|
||||
bool hasUDTLayout() const;
|
||||
const ClassLayout &getUDTLayout() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<PDBSymbolData> DataMember;
|
||||
@ -77,13 +79,24 @@ class VTableLayoutItem : public StorageItemBase {
|
||||
public:
|
||||
VTableLayoutItem(const UDTLayoutBase &Parent,
|
||||
std::unique_ptr<PDBSymbolTypeVTable> VTable);
|
||||
ArrayRef<PDBSymbolFunc *> 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<PDBSymbolTypeVTableShape> Shape;
|
||||
std::unique_ptr<PDBSymbolTypeVTable> VTable;
|
||||
std::vector<std::unique_ptr<PDBSymbolFunc>> VTableFuncs;
|
||||
std::vector<PDBSymbolFunc *> VTableFuncs;
|
||||
};
|
||||
|
||||
class UDTLayoutBase {
|
||||
template <typename T> using UniquePtrVector = std::vector<std::unique_ptr<T>>;
|
||||
|
||||
public:
|
||||
UDTLayoutBase(const PDBSymbol &Symbol, const std::string &Name,
|
||||
uint32_t Size);
|
||||
@ -99,26 +112,37 @@ public:
|
||||
return ChildStorage;
|
||||
}
|
||||
|
||||
ArrayRef<BaseClassLayout *> base_classes() const { return BaseClasses; }
|
||||
VTableLayoutItem *findVTableAtOffset(uint32_t RelativeOffset);
|
||||
|
||||
ArrayRef<std::unique_ptr<PDBSymbol>> other_items() const {
|
||||
return NonStorageItems;
|
||||
StringRef getUDTName() const { return Name; }
|
||||
|
||||
ArrayRef<BaseClassLayout *> bases() const { return BaseClasses; }
|
||||
ArrayRef<std::unique_ptr<PDBSymbolTypeBaseClass>> vbases() const {
|
||||
return VirtualBases;
|
||||
}
|
||||
|
||||
ArrayRef<std::unique_ptr<PDBSymbolFunc>> funcs() const { return Funcs; }
|
||||
|
||||
ArrayRef<std::unique_ptr<PDBSymbol>> other_items() const { return Other; }
|
||||
|
||||
const PDBSymbol &getSymbolBase() const { return SymbolBase; }
|
||||
|
||||
protected:
|
||||
void initializeChildren(const PDBSymbol &Sym);
|
||||
|
||||
void addChildToLayout(std::unique_ptr<StorageItemBase> Child);
|
||||
void addVirtualOverride(PDBSymbolFunc &Func);
|
||||
void addVirtualIntro(PDBSymbolFunc &Func);
|
||||
|
||||
const PDBSymbol &SymbolBase;
|
||||
std::string Name;
|
||||
uint32_t SizeOf = 0;
|
||||
|
||||
BitVector UsedBytes;
|
||||
std::vector<std::unique_ptr<PDBSymbol>> NonStorageItems;
|
||||
std::vector<std::unique_ptr<StorageItemBase>> ChildStorage;
|
||||
UniquePtrVector<PDBSymbol> Other;
|
||||
UniquePtrVector<PDBSymbolFunc> Funcs;
|
||||
UniquePtrVector<PDBSymbolTypeBaseClass> VirtualBases;
|
||||
UniquePtrVector<StorageItemBase> ChildStorage;
|
||||
std::vector<std::list<StorageItemBase *>> ChildrenPerByte;
|
||||
std::vector<BaseClassLayout *> BaseClasses;
|
||||
VTableLayoutItem *VTable = nullptr;
|
||||
@ -129,6 +153,8 @@ public:
|
||||
explicit ClassLayout(const PDBSymbolTypeUDT &UDT);
|
||||
explicit ClassLayout(std::unique_ptr<PDBSymbolTypeUDT> UDT);
|
||||
|
||||
ClassLayout(ClassLayout &&Other) = default;
|
||||
|
||||
const PDBSymbolTypeUDT &getClass() const { return UDT; }
|
||||
|
||||
private:
|
||||
@ -142,6 +168,7 @@ public:
|
||||
std::unique_ptr<PDBSymbolTypeBaseClass> Base);
|
||||
|
||||
const PDBSymbolTypeBaseClass &getBase() const { return *Base; }
|
||||
bool isVirtualBase() const { return IsVirtualBase; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<PDBSymbolTypeBaseClass> Base;
|
||||
|
@ -159,6 +159,8 @@ PDBSymbol::findInlineFramesByRVA(uint32_t RVA) const {
|
||||
std::unique_ptr<IPDBEnumSymbols>
|
||||
PDBSymbol::getChildStats(TagStats &Stats) const {
|
||||
std::unique_ptr<IPDBEnumSymbols> Result(findAllChildren());
|
||||
if (!Result)
|
||||
return nullptr;
|
||||
Stats.clear();
|
||||
while (auto Child = Result->getNext()) {
|
||||
++Stats[Child->getSymTag()];
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<PDBSymbolData>(&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<PDBSymbolTypeVTable> VTable)
|
||||
: StorageItemBase(Parent, *VTable, "<vtbl>", 0, getTypeLength(*VTable)),
|
||||
VTable(std::move(VTable)) {
|
||||
// initialize vtbl methods.
|
||||
auto VTableType = cast<PDBSymbolTypePointer>(this->VTable->getType());
|
||||
uint32_t PointerSize = VTableType->getLength();
|
||||
ElementSize = VTableType->getLength();
|
||||
|
||||
if (auto Shape = unique_dyn_cast<PDBSymbolTypeVTableShape>(
|
||||
VTableType->getPointeeType())) {
|
||||
Shape =
|
||||
unique_dyn_cast<PDBSymbolTypeVTableShape>(VTableType->getPointeeType());
|
||||
if (Shape)
|
||||
VTableFuncs.resize(Shape->getCount());
|
||||
|
||||
auto ParentFunctions =
|
||||
Parent.getSymbolBase().findAllChildren<PDBSymbolFunc>();
|
||||
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<PDBSymbolTypeBaseClass> Bases;
|
||||
UniquePtrVector<PDBSymbolTypeVTable> VTables;
|
||||
UniquePtrVector<PDBSymbolData> Members;
|
||||
auto Children = Sym.findAllChildren();
|
||||
while (auto Child = Children->getNext()) {
|
||||
if (auto Data = unique_dyn_cast<PDBSymbolData>(Child)) {
|
||||
if (Data->getDataKind() == PDB_DataKind::Member) {
|
||||
auto DM =
|
||||
llvm::make_unique<DataMemberLayoutItem>(*this, std::move(Data));
|
||||
if (auto Base = unique_dyn_cast<PDBSymbolTypeBaseClass>(Child)) {
|
||||
if (Base->isVirtualBaseClass())
|
||||
VirtualBases.push_back(std::move(Base));
|
||||
else
|
||||
Bases.push_back(std::move(Base));
|
||||
}
|
||||
|
||||
addChildToLayout(std::move(DM));
|
||||
} else {
|
||||
NonStorageItems.push_back(std::move(Data));
|
||||
else if (auto Data = unique_dyn_cast<PDBSymbolData>(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<PDBSymbolTypeVTable>(Child))
|
||||
VTables.push_back(std::move(VT));
|
||||
else if (auto Func = unique_dyn_cast<PDBSymbolFunc>(Child))
|
||||
Funcs.push_back(std::move(Func));
|
||||
else
|
||||
Other.push_back(std::move(Child));
|
||||
}
|
||||
|
||||
for (auto &Base : Bases) {
|
||||
auto BL = llvm::make_unique<BaseClassLayout>(*this, std::move(Base));
|
||||
BaseClasses.push_back(BL.get());
|
||||
|
||||
addChildToLayout(std::move(BL));
|
||||
}
|
||||
|
||||
for (auto &VT : VTables) {
|
||||
auto VTLayout = llvm::make_unique<VTableLayoutItem>(*this, std::move(VT));
|
||||
|
||||
VTable = VTLayout.get();
|
||||
|
||||
addChildToLayout(std::move(VTLayout));
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto &Data : Members) {
|
||||
auto DM = llvm::make_unique<DataMemberLayoutItem>(*this, std::move(Data));
|
||||
|
||||
addChildToLayout(std::move(DM));
|
||||
}
|
||||
|
||||
for (auto &Func : Funcs) {
|
||||
if (!Func->isVirtual())
|
||||
continue;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
uint32_t Stride = VTable->getElementSize();
|
||||
|
||||
uint32_t Index = Func.getVirtualBaseOffset();
|
||||
assert(Index % Stride == 0);
|
||||
Index /= Stride;
|
||||
|
||||
VTable->setFunction(Index, Func);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (auto Base = unique_dyn_cast<PDBSymbolTypeBaseClass>(Child)) {
|
||||
auto BL = llvm::make_unique<BaseClassLayout>(*this, std::move(Base));
|
||||
BaseClasses.push_back(BL.get());
|
||||
|
||||
addChildToLayout(std::move(BL));
|
||||
// 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 (auto VT = unique_dyn_cast<PDBSymbolTypeVTable>(Child)) {
|
||||
auto VTLayout = llvm::make_unique<VTableLayoutItem>(*this, std::move(VT));
|
||||
|
||||
VTable = VTLayout.get();
|
||||
|
||||
addChildToLayout(std::move(VTLayout));
|
||||
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<StorageItemBase> 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());
|
||||
|
@ -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;
|
||||
|
Binary file not shown.
121
test/tools/llvm-pdbdump/simple-padding-graphical.test
Normal file
121
test/tools/llvm-pdbdump/simple-padding-graphical.test
Normal file
@ -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: <padding> (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: <padding> (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: <padding> (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: <padding> (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: <padding> (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: <padding> (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: <padding> (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: <padding> (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: <padding> (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: <padding> (1 bytes)
|
||||
; DEEP_INHERIT-NEXT: data +0x02 [sizeof=2] short Y
|
||||
; DEEP_INHERIT-NEXT: <padding> (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: <padding> (3 bytes)
|
||||
; AGGREGATE-NEXT: data +0x04 [sizeof=4] int Y
|
||||
; AGGREGATE-NEXT: }
|
@ -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: }
|
||||
|
@ -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 <algorithm>
|
||||
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 ClassLayout &Layout) {
|
||||
return false;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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() << "<padding> (" << Amount
|
||||
<< " bytes)";
|
||||
DumpedAnything = true;
|
||||
}
|
||||
|
||||
return DumpedAnything;
|
||||
}
|
||||
|
||||
void PrettyClassLayoutGraphicalDumper::printPaddingRow(uint32_t Amount) {
|
||||
if (Amount == 0)
|
||||
return;
|
||||
|
||||
Printer.NewLine();
|
||||
WithColor(Printer, PDB_ColorItem::Padding).get() << "<padding> (" << 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<BaseClassLayout &>(*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<DataMemberLayoutItem &>(*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<VTableLayoutItem &>(*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<PDBSymbolTypeUDT>(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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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<ClassLayout>;
|
||||
|
||||
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 <typename Enumerator>
|
||||
static std::vector<std::unique_ptr<PDBSymbolTypeUDT>>
|
||||
filterClassDefs(LinePrinter &Printer, Enumerator &E) {
|
||||
std::vector<std::unique_ptr<PDBSymbolTypeUDT>> Filtered;
|
||||
static std::vector<std::unique_ptr<ClassLayout>>
|
||||
filterAndSortClassDefs(LinePrinter &Printer, Enumerator &E,
|
||||
uint32_t UnfilteredCount) {
|
||||
std::vector<std::unique_ptr<ClassLayout>> 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)
|
||||
continue;
|
||||
++Examined;
|
||||
if (Examined % 10000 == 0) {
|
||||
outs() << formatv("Examined {0}/{1} items. {2} items discarded\n",
|
||||
Examined, UnfilteredCount, Discarded);
|
||||
outs().flush();
|
||||
}
|
||||
|
||||
if (Printer.IsTypeExcluded(Class->getName()))
|
||||
if (Class->getUnmodifiedTypeId() != 0) {
|
||||
++Discarded;
|
||||
continue;
|
||||
}
|
||||
|
||||
Filtered.push_back(std::move(Class));
|
||||
if (Printer.IsTypeExcluded(Class->getName(), Class->getLength())) {
|
||||
++Discarded;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto Layout = llvm::make_unique<ClassLayout>(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<PDBSymbolTypeUDT>();
|
||||
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<LayoutPtr> 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<ClassLayout>(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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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<PDBSymbolTypePointer>(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) {
|
||||
|
@ -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;
|
||||
|
@ -122,15 +122,27 @@ cl::opt<bool> Enums("enums", cl::desc("Display enum types"),
|
||||
cl::cat(TypeCategory), cl::sub(PrettySubcommand));
|
||||
cl::opt<bool> Typedefs("typedefs", cl::desc("Display typedef types"),
|
||||
cl::cat(TypeCategory), cl::sub(PrettySubcommand));
|
||||
cl::opt<ClassSortMode> 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<ClassDefinitionFormat> 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<std::string> IncludeCompilands(
|
||||
"include-compilands",
|
||||
cl::desc("Include only compilands those which match a regular expression"),
|
||||
cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
|
||||
cl::opt<bool> 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<uint32_t> 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<uint32_t> 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<bool> ExcludeCompilerGenerated(
|
||||
"no-compiler-generated",
|
||||
|
@ -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<bool> Compilands;
|
||||
extern llvm::cl::opt<bool> Symbols;
|
||||
@ -36,7 +37,9 @@ extern llvm::cl::list<std::string> ExcludeCompilands;
|
||||
extern llvm::cl::list<std::string> IncludeTypes;
|
||||
extern llvm::cl::list<std::string> IncludeSymbols;
|
||||
extern llvm::cl::list<std::string> IncludeCompilands;
|
||||
extern llvm::cl::opt<bool> OnlyPaddingClasses;
|
||||
extern llvm::cl::opt<ClassSortMode> ClassOrder;
|
||||
extern llvm::cl::opt<uint32_t> SizeThreshold;
|
||||
extern llvm::cl::opt<uint32_t> PaddingThreshold;
|
||||
extern llvm::cl::opt<ClassDefinitionFormat> ClassFormat;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user