Allow specification of what kinds of class members to dump.

Previously when dumping class definitions, there were only
two modes - on or off.  But it's useful to sometimes get a
little more fine-grained.  For example, you might only want
to see the record layout (for example to look for extraneous
padding).  This patch adds a third mode, layout mode, which
does exactly that.  Only this-relative data members are
displayed in this mode.

Differential Revision: https://reviews.llvm.org/D31794

llvm-svn: 299733
This commit is contained in:
Zachary Turner 2017-04-06 23:43:39 +00:00
parent 63230a4e71
commit 10169b6d0d
8 changed files with 63 additions and 119 deletions

View File

@ -30,7 +30,6 @@
; TYPES_1: Classes
; TYPES_1: struct A {
; TYPES_1: public:
; TYPES_1: virtual void PureFunc() = 0
; TYPES_1: virtual void VirtualFunc()
; TYPES_1: void RegularFunc()

View File

@ -17,9 +17,7 @@
; MEMBERS_TEST: class MembersTest::A {
; MEMBERS_TEST-DAG: typedef int NestedTypedef
; MEMBERS_TEST-DAG: enum NestedEnum
; MEMBERS_TEST: public:
; MEMBERS_TEST-NEXT: void MemberFunc()
; MEMBERS_TEST-NEXT: private:
; MEMBERS_TEST: void MemberFunc()
; MEMBERS_TEST-DAG: int IntMemberVar
; MEMBERS_TEST-DAG: double DoubleMemberVar
; MEMBERS_TEST: }
@ -48,7 +46,6 @@
; BITFIELD_TEST: ---TYPES---
; BITFIELD_TEST: struct BitFieldTest::A {
; BITFIELD_TEST-NEXT: public:
; BITFIELD_TEST-NEXT: +0x00 int Bits1 : 1
; BITFIELD_TEST-NEXT: +0x00 int Bits2 : 2
; BITFIELD_TEST-NEXT: +0x00 int Bits3 : 3

View File

@ -1,9 +1,16 @@
; RUN: llvm-pdbdump pretty -symbols -globals -types %p/Inputs/FilterTest.pdb \
; RUN: | FileCheck --check-prefix=NO_FILTER %s
; RUN: llvm-pdbdump pretty -types -exclude-types="GlobalTypedef|NestedTypedef" \
; RUN: %p/Inputs/FilterTest.pdb | FileCheck --check-prefix=EXCLUDE_TYPEDEFS %s
; RUN: llvm-pdbdump pretty -classes -enums %p/Inputs/FilterTest.pdb \
; RUN: | FileCheck --check-prefix=EXCLUDE_TYPEDEFS %s
; RUN: llvm-pdbdump pretty -types -exclude-types="GlobalEnum|NestedEnum" \
; RUN: %p/Inputs/FilterTest.pdb | FileCheck --check-prefix=EXCLUDE_ENUMS %s
; RUN: llvm-pdbdump pretty -classes -typedefs %p/Inputs/FilterTest.pdb \
; RUN: | FileCheck --check-prefix=EXCLUDE_ENUMS %s
; RUN: llvm-pdbdump pretty -types -symbols -globals -exclude-symbols="MemberVar|GlobalVar" \
; RUN: %p/Inputs/FilterTest.pdb | FileCheck --check-prefix=EXCLUDE_VARS %s
; RUN: llvm-pdbdump pretty -types -exclude-types="FilterTestClass" \
@ -36,31 +43,25 @@
; NO_FILTER-DAG: GlobalEnum GlobalEnumVar
; EXCLUDE_TYPEDEFS: ---TYPES---
; EXCLUDE_TYPEDEFS: Enums:
; EXCLUDE_TYPEDEFS: GlobalEnum
; EXCLUDE_TYPEDEFS: Typedefs
; EXCLUDE_TYPEDEFS-NOT: GlobalTypedef
; EXCLUDE_TYPEDEFS: Classes
; EXCLUDE_TYPEDEFS: class FilterTestClass
; EXCLUDE_TYPEDEFS-NOT: NestedTypedef
; EXCLUDE_TYPEDEFS: private:
; EXCLUDE_TYPEDEFS-DAG: GlobalEnum
; EXCLUDE_TYPEDEFS-DAG: NestedEnum
; EXCLUDE_TYPEDEFS: class FilterTestClass
; EXCLUDE_ENUMS: ---TYPES---
; EXCLUDE_ENUMS: Enums:
; EXCLUDE_ENUMS-NOT: GlobalEnum
; EXCLUDE_ENUMS: Typedefs
; EXCLUDE_ENUMS: GlobalTypedef
; EXCLUDE_ENUMS: Classes
; EXCLUDE_ENUMS: class FilterTestClass
; EXCLUDE_ENUMS-NOT: NestedEnum
; EXCLUDE_ENUMS: private:
; EXCLUDE_ENUMS: GlobalTypedef
; EXCLUDE_ENUMS: class FilterTestClass
; EXCLUDE_VARS: ---TYPES---
; EXCLUDE_VARS: Classes:
; EXCLUDE_VARS: class FilterTestClass
; EXCLUDE_VARS: private:
; EXCLUDE_VARS-NOT: IntMemberVar
; EXCLUDE_VARS-NOT: DoubleMemberVar
; EXCLUDE_VARS-DAG: GlobalEnum
; EXCLUDE_VARS-DAG: NestedEnum
; EXCLUDE_VARS: GlobalTypedef
; EXCLUDE_VARS: class FilterTestClass
; EXCLUDE_VARS: ---GLOBALS---
; EXCLUDE_VARS-NOT: DoubleGlobalVar
; EXCLUDE_VARS-NOT: IntGlobalVar

View File

@ -35,6 +35,9 @@ ClassDefinitionDumper::ClassDefinitionDumper(LinePrinter &P)
: PDBSymDumper(true), Printer(P) {}
void ClassDefinitionDumper::start(const PDBSymbolTypeUDT &Class) {
assert(opts::pretty::ClassFormat !=
opts::pretty::ClassDefinitionFormat::None);
std::string Name = Class.getName();
WithColor(Printer, PDB_ColorItem::Keyword).get() << Class.getUdtKind() << " ";
WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName();
@ -61,98 +64,50 @@ void ClassDefinitionDumper::start(const PDBSymbolTypeUDT &Class) {
Printer << " {";
auto Children = Class.findAllChildren();
if (Children->getChildCount() == 0) {
Printer << "}";
return;
}
// Try to dump symbols organized by member access level. Public members
// first, then protected, then private. This might be slow, so it's worth
// reconsidering the value of this if performance of large PDBs is a problem.
// NOTE: Access level of nested types is not recorded in the PDB, so we have
// a special case for them.
SymbolGroupByAccess Groups;
Groups.insert(std::make_pair(0, SymbolGroup()));
Groups.insert(std::make_pair((int)PDB_MemberAccess::Private, SymbolGroup()));
Groups.insert(
std::make_pair((int)PDB_MemberAccess::Protected, SymbolGroup()));
Groups.insert(std::make_pair((int)PDB_MemberAccess::Public, SymbolGroup()));
Printer.Indent();
int DumpedCount = 0;
while (auto Child = Children->getNext()) {
PDB_MemberAccess Access = Child->getRawSymbol().getAccess();
if (isa<PDBSymbolTypeBaseClass>(*Child))
continue;
auto &AccessGroup = Groups.find((int)Access)->second;
if (opts::pretty::ClassFormat ==
opts::pretty::ClassDefinitionFormat::LayoutOnly) {
if (auto Data = dyn_cast<PDBSymbolData>(Child.get())) {
switch (Data->getLocationType()) {
case PDB_LocType::ThisRel:
case PDB_LocType::BitField:
break;
default:
// All other types of data field do not occupy any storage (e.g. are
// const),
// so in layout mode we skip them.
continue;
}
} else {
// Only data symbols affect record layout, so skip any non-data symbols
// if
// we're in record layout mode.
continue;
}
}
if (auto Func = dyn_cast<PDBSymbolFunc>(Child.get())) {
if (Func->isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated)
continue;
if (Func->getLength() == 0 && !Func->isPureVirtual() &&
!Func->isIntroVirtualFunction())
continue;
Child.release();
AccessGroup.Functions.push_back(std::unique_ptr<PDBSymbolFunc>(Func));
} else if (auto Data = dyn_cast<PDBSymbolData>(Child.get())) {
Child.release();
AccessGroup.Data.push_back(std::unique_ptr<PDBSymbolData>(Data));
} else {
AccessGroup.Unknown.push_back(std::move(Child));
}
++DumpedCount;
Child->dump(*this);
}
int Count = 0;
Count += dumpAccessGroup((PDB_MemberAccess)0, Groups[0]);
Count += dumpAccessGroup(PDB_MemberAccess::Public,
Groups[(int)PDB_MemberAccess::Public]);
Count += dumpAccessGroup(PDB_MemberAccess::Protected,
Groups[(int)PDB_MemberAccess::Protected]);
Count += dumpAccessGroup(PDB_MemberAccess::Private,
Groups[(int)PDB_MemberAccess::Private]);
if (Count > 0)
Printer.Unindent();
if (DumpedCount > 0)
Printer.NewLine();
Printer << "}";
}
int ClassDefinitionDumper::dumpAccessGroup(PDB_MemberAccess Access,
const SymbolGroup &Group) {
if (Group.Functions.empty() && Group.Data.empty() && Group.Unknown.empty())
return 0;
int Count = 0;
if (Access == PDB_MemberAccess::Private) {
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::Keyword).get() << "private";
Printer << ":";
} else if (Access == PDB_MemberAccess::Protected) {
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::Keyword).get() << "protected";
Printer << ":";
} else if (Access == PDB_MemberAccess::Public) {
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::Keyword).get() << "public";
Printer << ":";
}
Printer.Indent();
for (auto iter = Group.Functions.begin(), end = Group.Functions.end();
iter != end; ++iter) {
++Count;
(*iter)->dump(*this);
}
for (auto iter = Group.Data.begin(), end = Group.Data.end(); iter != end;
++iter) {
++Count;
(*iter)->dump(*this);
}
for (auto iter = Group.Unknown.begin(), end = Group.Unknown.end();
iter != end; ++iter) {
++Count;
(*iter)->dump(*this);
}
Printer.Unindent();
return Count;
}
void ClassDefinitionDumper::dump(const PDBSymbolTypeBaseClass &Symbol) {}
void ClassDefinitionDumper::dump(const PDBSymbolData &Symbol) {

View File

@ -39,24 +39,6 @@ public:
private:
LinePrinter &Printer;
struct SymbolGroup {
SymbolGroup() {}
SymbolGroup(SymbolGroup &&Other) {
Functions = std::move(Other.Functions);
Data = std::move(Other.Data);
Unknown = std::move(Other.Unknown);
}
std::list<std::unique_ptr<PDBSymbolFunc>> Functions;
std::list<std::unique_ptr<PDBSymbolData>> Data;
std::list<std::unique_ptr<PDBSymbol>> Unknown;
SymbolGroup(const SymbolGroup &other) = delete;
SymbolGroup &operator=(const SymbolGroup &other) = delete;
};
typedef std::unordered_map<int, SymbolGroup> SymbolGroupByAccess;
int dumpAccessGroup(PDB_MemberAccess Access, const SymbolGroup &Group);
};
}
}

View File

@ -100,7 +100,7 @@ void TypeDumper::dump(const PDBSymbolTypeUDT &Symbol) {
Printer.NewLine();
if (opts::pretty::NoClassDefs) {
if (opts::pretty::ClassFormat == opts::pretty::ClassDefinitionFormat::None) {
WithColor(Printer, PDB_ColorItem::Keyword).get() << "class ";
WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName();
} else {

View File

@ -170,9 +170,17 @@ cl::opt<bool>
ExcludeSystemLibraries("no-system-libs",
cl::desc("Don't show symbols from system libraries"),
cl::cat(FilterCategory), cl::sub(PrettySubcommand));
cl::opt<bool> NoClassDefs("no-class-definitions",
cl::desc("Don't display full class definitions"),
cl::cat(FilterCategory), cl::sub(PrettySubcommand));
cl::opt<ClassDefinitionFormat> ClassFormat(
"class-definitions", cl::desc("Class definition format"),
cl::init(ClassDefinitionFormat::Full),
cl::values(clEnumValN(ClassDefinitionFormat::Full, "full",
"Display complete class definition"),
clEnumValN(ClassDefinitionFormat::LayoutOnly, "layout",
"Display only members which affect record layout"),
clEnumValN(ClassDefinitionFormat::None, "none",
"Don't display class definitions")),
cl::cat(FilterCategory), cl::sub(PrettySubcommand));
cl::opt<bool> NoEnumDefs("no-enum-definitions",
cl::desc("Don't display full enum definitions"),
cl::cat(FilterCategory), cl::sub(PrettySubcommand));

View File

@ -17,6 +17,8 @@
namespace opts {
namespace pretty {
enum class ClassDefinitionFormat { None, LayoutOnly, Full };
extern llvm::cl::opt<bool> Compilands;
extern llvm::cl::opt<bool> Symbols;
extern llvm::cl::opt<bool> Globals;
@ -26,7 +28,7 @@ extern llvm::cl::opt<bool> Typedefs;
extern llvm::cl::opt<bool> All;
extern llvm::cl::opt<bool> ExcludeCompilerGenerated;
extern llvm::cl::opt<bool> NoClassDefs;
extern llvm::cl::opt<ClassDefinitionFormat> ClassFormat;
extern llvm::cl::opt<bool> NoEnumDefs;
extern llvm::cl::list<std::string> ExcludeTypes;
extern llvm::cl::list<std::string> ExcludeSymbols;