[llvm-pdbutil] Add the ability to dump the dependency tree for a type

Previously we had the -type-index option which would dump the record of
a single, but we had no way to follow the dependency graph backwards and
also dump all dependent types.

Having this option makes test-writing better, because we can limit the
test to only those records that are of importance for the thing we're
trying to test, which allows us to use things like CHECK-NEXT to reduce
fragility.

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

llvm-svn: 306852
This commit is contained in:
Zachary Turner 2017-06-30 18:15:47 +00:00
parent 0d22b63ef8
commit 13ebe41aea
8 changed files with 142 additions and 26 deletions

View File

@ -28,6 +28,8 @@ void discoverTypeIndices(ArrayRef<uint8_t> RecordData,
SmallVectorImpl<TiReference> &Refs);
void discoverTypeIndices(const CVType &Type,
SmallVectorImpl<TiReference> &Refs);
void discoverTypeIndices(const CVType &Type,
SmallVectorImpl<TypeIndex> &Indices);
/// Discover type indices in symbol records. Returns false if this is an unknown
/// record.

View File

@ -438,6 +438,25 @@ void llvm::codeview::discoverTypeIndices(const CVType &Type,
::discoverTypeIndices(Type.content(), Type.kind(), Refs);
}
void llvm::codeview::discoverTypeIndices(const CVType &Type,
SmallVectorImpl<TypeIndex> &Indices) {
Indices.clear();
SmallVector<TiReference, 4> Refs;
discoverTypeIndices(Type, Refs);
if (Refs.empty())
return;
BinaryStreamReader Reader(Type.content(), support::little);
for (const auto &Ref : Refs) {
Reader.setOffset(Ref.Offset);
FixedStreamArray<TypeIndex> Run;
cantFail(Reader.readArray(Run, Ref.Count));
Indices.append(Run.begin(), Run.end());
}
}
void llvm::codeview::discoverTypeIndices(ArrayRef<uint8_t> RecordData,
SmallVectorImpl<TiReference> &Refs) {
const RecordPrefix *P =

View File

@ -0,0 +1,30 @@
; RUN: llvm-pdbutil dump -type-index=0x1019 %p/Inputs/ClassLayoutTest.pdb \
; RUN: | FileCheck --check-prefix=NODEPS %s
; RUN: llvm-pdbutil dump -type-index=0x1019 -dependents %p/Inputs/ClassLayoutTest.pdb \
; RUN: | FileCheck --check-prefix=DEPS %s
NODEPS: Types (TPI Stream)
NODEPS-NEXT: ============================================================
NODEPS-NEXT: Showing 1 records.
NODEPS-NEXT: 0x1019 | LF_MFUNCTION [size = 28]
NODEPS-NEXT: return type = 0x0003 (void), # args = 0, param list = 0x100E
NODEPS-NEXT: class type = 0x1017, this type = 0x1018, this adjust = 0
NODEPS-NEXT: calling conv = thiscall, options = None
DEPS: Types (TPI Stream)
DEPS-NEXT: ============================================================
DEPS-NEXT: Showing 1 records and their dependents (4 records total)
DEPS-NEXT: 0x100E | LF_ARGLIST [size = 8]
DEPS-NEXT: 0x1017 | LF_CLASS [size = 60]
DEPS-NEXT: class name: `MembersTest::A`
DEPS-NEXT: unique name: `.?AVA@MembersTest@@`
DEPS-NEXT: vtable: <no type>, base list: <no type>, field list: <no type>
DEPS-NEXT: options: forward ref | has unique name
DEPS-NEXT: 0x1018 | LF_POINTER [size = 12]
DEPS-NEXT: referent = 0x1017, mode = pointer, opts = const, kind = ptr32
DEPS-NEXT: 0x1019 | LF_MFUNCTION [size = 28]
DEPS-NEXT: return type = 0x0003 (void), # args = 0, param list = 0x100E
DEPS-NEXT: class type = 0x1017, this type = 0x1018, this adjust = 0
DEPS-NEXT: calling conv = thiscall, options = None

View File

@ -37,6 +37,7 @@
#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h"
#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h"
#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"
@ -116,12 +117,14 @@ Error DumpOutputStyle::dump() {
return EC;
}
if (opts::dump::DumpTypes || opts::dump::DumpTypeExtras) {
if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() ||
opts::dump::DumpTypeExtras) {
if (auto EC = dumpTpiStream(StreamTPI))
return EC;
}
if (opts::dump::DumpIds || opts::dump::DumpIdExtras) {
if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() ||
opts::dump::DumpIdExtras) {
if (auto EC = dumpTpiStream(StreamIPI))
return EC;
}
@ -620,6 +623,76 @@ Error DumpOutputStyle::dumpStringTable() {
return Error::success();
}
static void buildDepSet(LazyRandomTypeCollection &Types,
ArrayRef<TypeIndex> Indices,
std::map<TypeIndex, CVType> &DepSet) {
SmallVector<TypeIndex, 4> DepList;
for (const auto &I : Indices) {
TypeIndex TI(I);
if (DepSet.find(TI) != DepSet.end() || TI.isSimple() || TI.isNoneType())
continue;
CVType Type = Types.getType(TI);
DepSet[TI] = Type;
codeview::discoverTypeIndices(Type, DepList);
buildDepSet(Types, DepList, DepSet);
}
}
static void dumpFullTypeStream(LinePrinter &Printer,
LazyRandomTypeCollection &Types,
TpiStream &Stream, bool Bytes, bool Extras) {
Printer.formatLine("Showing {0:N} records", Stream.getNumTypeRecords());
uint32_t Width =
NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords());
MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types,
Stream.getHashValues());
if (auto EC = codeview::visitTypeStream(Types, V)) {
Printer.formatLine("An error occurred dumping type records: {0}",
toString(std::move(EC)));
}
}
static void dumpPartialTypeStream(LinePrinter &Printer,
LazyRandomTypeCollection &Types,
TpiStream &Stream, ArrayRef<TypeIndex> TiList,
bool Bytes, bool Extras, bool Deps) {
uint32_t Width =
NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords());
MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types,
Stream.getHashValues());
if (opts::dump::DumpTypeDependents) {
// If we need to dump all dependents, then iterate each index and find
// all dependents, adding them to a map ordered by TypeIndex.
std::map<TypeIndex, CVType> DepSet;
buildDepSet(Types, TiList, DepSet);
Printer.formatLine(
"Showing {0:N} records and their dependents ({1:N} records total)",
TiList.size(), DepSet.size());
for (auto &Dep : DepSet) {
if (auto EC = codeview::visitTypeRecord(Dep.second, Dep.first, V))
Printer.formatLine("An error occurred dumping type record {0}: {1}",
Dep.first, toString(std::move(EC)));
}
} else {
Printer.formatLine("Showing {0:N} records.", TiList.size());
for (const auto &I : TiList) {
TypeIndex TI(I);
CVType Type = Types.getType(TI);
if (auto EC = codeview::visitTypeRecord(Type, TI, V))
Printer.formatLine("An error occurred dumping type record {0}: {1}", TI,
toString(std::move(EC)));
}
}
}
Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);
@ -659,27 +732,13 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
auto &Types = Err(initializeTypes(StreamIdx));
if (DumpTypes) {
P.formatLine("Showing {0:N} records", Stream.getNumTypeRecords());
uint32_t Width =
NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords());
MinimalTypeDumpVisitor V(P, Width + 2, DumpBytes, DumpExtras, Types,
Stream.getHashValues());
if (Indices.empty()) {
if (auto EC = codeview::visitTypeStream(Types, V)) {
P.formatLine("An error occurred dumping type records: {0}",
toString(std::move(EC)));
}
} else {
for (const auto &I : Indices) {
TypeIndex TI(I);
CVType Type = Types.getType(TI);
if (auto EC = codeview::visitTypeRecord(Type, TI, V))
P.formatLine("An error occurred dumping type record {0}: {1}", TI,
toString(std::move(EC)));
}
if (DumpTypes || !Indices.empty()) {
if (Indices.empty())
dumpFullTypeStream(P, Types, Stream, DumpBytes, DumpExtras);
else {
std::vector<TypeIndex> TiList(Indices.begin(), Indices.end());
dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras,
opts::dump::DumpTypeDependents);
}
}

View File

@ -37,8 +37,6 @@ private:
Error dumpFileSummary();
Error dumpStreamSummary();
Error dumpBlockRanges();
Error dumpStreamBytes();
Error dumpStringTable();
Error dumpLines();
Error dumpInlineeLines();

View File

@ -377,7 +377,7 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
MemberFunctionRecord &MF) {
P.formatLine("return type = {0}, # args = {1}, param list = {2}",
MF.ParameterCount, MF.ArgumentList, MF.ReturnType);
MF.ReturnType, MF.ParameterCount, MF.ArgumentList);
P.formatLine("class type = {0}, this type = {1}, this adjust = {2}",
MF.ClassType, MF.ThisType, MF.ThisPointerAdjustment);
P.formatLine("calling conv = {0}, options = {1}",

View File

@ -419,6 +419,13 @@ cl::list<uint32_t> DumpIdIndex(
cl::desc("only dump ids with the specified hexadecimal type index"),
cl::cat(TypeOptions), cl::sub(DumpSubcommand));
cl::opt<bool> DumpTypeDependents(
"dependents",
cl::desc("In conjunection with -type-index and -id-index, dumps the entire "
"dependency graph for the specified index instead of "
"just the single record with the specified index"),
cl::cat(TypeOptions), cl::sub(DumpSubcommand));
// SYMBOL OPTIONS
cl::opt<bool> DumpPublics("publics", cl::desc("dump Publics stream data"),
cl::cat(SymbolOptions), cl::sub(DumpSubcommand));

View File

@ -135,6 +135,7 @@ extern llvm::cl::opt<bool> DumpTypes;
extern llvm::cl::opt<bool> DumpTypeData;
extern llvm::cl::opt<bool> DumpTypeExtras;
extern llvm::cl::list<uint32_t> DumpTypeIndex;
extern llvm::cl::opt<bool> DumpTypeDependents;
extern llvm::cl::opt<bool> DumpIds;
extern llvm::cl::opt<bool> DumpIdData;