Make llvm-pdbdump print CV type records

This reuses the CVTypeDumper from libcodeview to dump full
information about type records within a PDB file.

Differential Revision: http://reviews.llvm.org/D20022
Reviewed By: rnk

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@268808 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Zachary Turner 2016-05-06 22:15:42 +00:00
parent 815a884f59
commit b7d84117e3
7 changed files with 122 additions and 56 deletions

View File

@ -51,36 +51,38 @@ public:
#define MEMBER_RECORD_ALIAS(ClassName, LeafEnum) #define MEMBER_RECORD_ALIAS(ClassName, LeafEnum)
#include "TypeRecords.def" #include "TypeRecords.def"
/// Visits the type records in Data and returns remaining data. Sets the void visitTypeRecord(const TypeIterator::TypeRecord &Record) {
/// error flag on parse failures. ArrayRef<uint8_t> LeafData = Record.LeafData;
void visitTypeStream(ArrayRef<uint8_t> Data) { ArrayRef<uint8_t> RecordData = LeafData;
for (const auto &I : makeTypeRange(Data)) { auto *DerivedThis = static_cast<Derived *>(this);
ArrayRef<uint8_t> LeafData = I.LeafData; DerivedThis->visitTypeBegin(Record.Leaf, RecordData);
ArrayRef<uint8_t> RecordData = LeafData; switch (Record.Leaf) {
auto *DerivedThis = static_cast<Derived *>(this); default:
DerivedThis->visitTypeBegin(I.Leaf, RecordData); DerivedThis->visitUnknownType(Record.Leaf);
switch (I.Leaf) { break;
default: case LF_FIELDLIST:
DerivedThis->visitUnknownType(I.Leaf); DerivedThis->visitFieldList(Record.Leaf, LeafData);
break; break;
case LF_FIELDLIST: case LF_METHODLIST:
DerivedThis->visitFieldList(I.Leaf, LeafData); DerivedThis->visitMethodList(Record.Leaf, LeafData);
break; break;
case LF_METHODLIST:
DerivedThis->visitMethodList(I.Leaf, LeafData);
break;
#define TYPE_RECORD(ClassName, LeafEnum) \ #define TYPE_RECORD(ClassName, LeafEnum) \
case LeafEnum: { \ case LeafEnum: { \
const ClassName *Rec; \ const ClassName *Rec; \
if (!CVTypeVisitor::consumeObject(LeafData, Rec)) \ if (!CVTypeVisitor::consumeObject(LeafData, Rec)) \
return; \ return; \
DerivedThis->visit##ClassName(I.Leaf, Rec, LeafData); \ DerivedThis->visit##ClassName(Record.Leaf, Rec, LeafData); \
break; \ break; \
} }
#include "TypeRecords.def" #include "TypeRecords.def"
} }
DerivedThis->visitTypeEnd(I.Leaf, RecordData); DerivedThis->visitTypeEnd(Record.Leaf, RecordData);
} }
/// Visits the type records in Data. Sets the error flag on parse failures.
void visitTypeStream(ArrayRef<uint8_t> Data) {
for (const auto &I : makeTypeRange(Data))
visitTypeRecord(I);
} }
/// Action to take on unknown types. By default, they are ignored. /// Action to take on unknown types. By default, they are ignored.

View File

@ -14,6 +14,7 @@
#include "llvm/ADT/StringSet.h" #include "llvm/ADT/StringSet.h"
#include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/DebugInfo/CodeView/TypeStream.h"
namespace llvm { namespace llvm {
class ScopedPrinter; class ScopedPrinter;
@ -29,6 +30,12 @@ public:
StringRef getTypeName(TypeIndex TI); StringRef getTypeName(TypeIndex TI);
void printTypeIndex(StringRef FieldName, TypeIndex TI); void printTypeIndex(StringRef FieldName, TypeIndex TI);
/// Dumps one type record. Returns false if there was a type parsing error,
/// and true otherwise. This should be called in order, since the dumper
/// maintains state about previous records which are necessary for cross
/// type references.
bool dump(const TypeIterator::TypeRecord &Record);
/// Dumps the type records in Data. Returns false if there was a type stream /// Dumps the type records in Data. Returns false if there was a type stream
/// parse error, and true otherwise. /// parse error, and true otherwise.
bool dump(ArrayRef<uint8_t> Data); bool dump(ArrayRef<uint8_t> Data);

View File

@ -755,6 +755,12 @@ void CVTypeDumper::printTypeIndex(StringRef FieldName, TypeIndex TI) {
W.printHex(FieldName, TI.getIndex()); W.printHex(FieldName, TI.getIndex());
} }
bool CVTypeDumper::dump(const TypeIterator::TypeRecord &Record) {
CVTypeDumperImpl Dumper(*this, W, PrintRecordBytes);
Dumper.visitTypeRecord(Record);
return !Dumper.hadError();
}
bool CVTypeDumper::dump(ArrayRef<uint8_t> Data) { bool CVTypeDumper::dump(ArrayRef<uint8_t> Data) {
CVTypeDumperImpl Dumper(*this, W, PrintRecordBytes); CVTypeDumperImpl Dumper(*this, W, PrintRecordBytes);
Dumper.visitTypeStream(Data); Dumper.visitTypeStream(Data);

View File

@ -19,5 +19,5 @@
type = Library type = Library
name = DebugInfoPDB name = DebugInfoPDB
parent = DebugInfo parent = DebugInfo
required_libraries = Object Support required_libraries = Object Support DebugInfoCodeView

View File

@ -1,4 +1,5 @@
; RUN: llvm-pdbdump --dump-headers -dump-tpi-stream -dump-tpi-record-bytes %p/Inputs/empty.pdb | FileCheck -check-prefix=EMPTY %s ; RUN: llvm-pdbdump --dump-headers -dump-tpi-records -dump-tpi-record-bytes \
; RUN: %p/Inputs/empty.pdb | FileCheck -check-prefix=EMPTY %s
; RUN: llvm-pdbdump --dump-headers %p/Inputs/big-read.pdb | FileCheck -check-prefix=BIG %s ; RUN: llvm-pdbdump --dump-headers %p/Inputs/big-read.pdb | FileCheck -check-prefix=BIG %s
; RUN: llvm-pdbdump --dump-headers %p/Inputs/bad-block-size.pdb | FileCheck -check-prefix=BAD-BLOCK-SIZE %s ; RUN: llvm-pdbdump --dump-headers %p/Inputs/bad-block-size.pdb | FileCheck -check-prefix=BAD-BLOCK-SIZE %s
@ -78,28 +79,71 @@
; EMPTY-NEXT: TPI Version: 20040203 ; EMPTY-NEXT: TPI Version: 20040203
; EMPTY-NEXT: Record count: 75 ; EMPTY-NEXT: Record count: 75
; EMPTY-NEXT: Records [ ; EMPTY-NEXT: Records [
; EMPTY-NEXT: { ; EMPTY-NEXT: {
; EMPTY-NEXT: Kind: 0x1201 ; EMPTY-NEXT: ArgList {
; EMPTY-NEXT: Bytes ( ; EMPTY-NEXT: TypeLeafKind: LF_ARGLIST (0x1201)
; EMPTY-NEXT: 0000: 00000000 |....| ; EMPTY-NEXT: TypeIndex: 0x1000
; EMPTY-NEXT: ) ; EMPTY-NEXT: NumArgs: 0
; EMPTY-NEXT: } ; EMPTY-NEXT: Arguments [
; EMPTY-NEXT: { ; EMPTY-NEXT: ]
; EMPTY-NEXT: Kind: 0x1008 ; EMPTY-NEXT: }
; EMPTY-NEXT: Bytes ( ; EMPTY-NEXT: Bytes (
; EMPTY-NEXT: 0000: 74000000 00000000 00100000 |t...........| ; EMPTY-NEXT: 0000: 00000000 |....|
; EMPTY-NEXT: ) ; EMPTY-NEXT: )
; EMPTY-NEXT: } ; EMPTY-NEXT: }
; EMPTY-NEXT: { ; EMPTY-NEXT: {
; EMPTY-NEXT: Kind: 0x1203 ; EMPTY-NEXT: ProcedureType {
; EMPTY-NEXT: Bytes ( ; EMPTY-NEXT: TypeLeafKind: LF_PROCEDURE (0x1008)
; EMPTY-NEXT: 0000: 02150300 01006170 6172746D 656E7400 |......apartment.| ; EMPTY-NEXT: TypeIndex: 0x1001
; EMPTY-NEXT: 0010: 02150300 02007369 6E676C65 00F3F2F1 |......single....| ; EMPTY-NEXT: ReturnType: int (0x74)
; EMPTY-NEXT: 0020: 02150300 03006672 656500F1 02150300 |......free......| ; EMPTY-NEXT: CallingConvention: NearC (0x0)
; EMPTY-NEXT: 0030: 04006E65 75747261 6C00F2F1 02150300 |..neutral.......| ; EMPTY-NEXT: FunctionOptions [ (0x0)
; EMPTY-NEXT: 0040: 0500626F 746800F1 |..both..| ; EMPTY-NEXT: ]
; EMPTY-NEXT: ) ; EMPTY-NEXT: NumParameters: 0
; EMPTY-NEXT: } ; EMPTY-NEXT: ArgListType: () (0x1000)
; EMPTY-NEXT: }
; EMPTY-NEXT: Bytes (
; EMPTY-NEXT: 0000: 74000000 00000000 00100000 |t...........|
; EMPTY-NEXT: )
; EMPTY-NEXT: }
; EMPTY-NEXT: {
; EMPTY-NEXT: UnknownLeaf {
; EMPTY-NEXT: TypeLeafKind: LF_FIELDLIST (0x1203)
; EMPTY-NEXT: TypeIndex: 0x1002
; EMPTY-NEXT: Enumerator {
; EMPTY-NEXT: AccessSpecifier: Public (0x3)
; EMPTY-NEXT: EnumValue: 1
; EMPTY-NEXT: Name: apartment
; EMPTY-NEXT: }
; EMPTY-NEXT: Enumerator {
; EMPTY-NEXT: AccessSpecifier: Public (0x3)
; EMPTY-NEXT: EnumValue: 2
; EMPTY-NEXT: Name: single
; EMPTY-NEXT: }
; EMPTY-NEXT: Enumerator {
; EMPTY-NEXT: AccessSpecifier: Public (0x3)
; EMPTY-NEXT: EnumValue: 3
; EMPTY-NEXT: Name: free
; EMPTY-NEXT: }
; EMPTY-NEXT: Enumerator {
; EMPTY-NEXT: AccessSpecifier: Public (0x3)
; EMPTY-NEXT: EnumValue: 4
; EMPTY-NEXT: Name: neutral
; EMPTY-NEXT: }
; EMPTY-NEXT: Enumerator {
; EMPTY-NEXT: AccessSpecifier: Public (0x3)
; EMPTY-NEXT: EnumValue: 5
; EMPTY-NEXT: Name: both
; EMPTY-NEXT: }
; EMPTY-NEXT: }
; EMPTY-NEXT: Bytes (
; EMPTY-NEXT: 0000: 02150300 01006170 6172746D 656E7400 |......apartment.|
; EMPTY-NEXT: 0010: 02150300 02007369 6E676C65 00F3F2F1 |......single....|
; EMPTY-NEXT: 0020: 02150300 03006672 656500F1 02150300 |......free......|
; EMPTY-NEXT: 0030: 04006E65 75747261 6C00F2F1 02150300 |..neutral.......|
; EMPTY-NEXT: 0040: 0500626F 746800F1 |..both..|
; EMPTY-NEXT: )
; EMPTY-NEXT: }
; BIG: FileHeaders { ; BIG: FileHeaders {
; BIG-NEXT: BlockSize: 4096 ; BIG-NEXT: BlockSize: 4096
@ -1238,4 +1282,4 @@
; BIG-NEXT: ] ; BIG-NEXT: ]
; BIG-NEXT: } ; BIG-NEXT: }
; BAD-BLOCK-SIZE: The PDB file is corrupt. Does not contain superblock ; BAD-BLOCK-SIZE: Native PDB Error: The PDB file is corrupt. Does not contain superblock

View File

@ -1,4 +1,5 @@
set(LLVM_LINK_COMPONENTS set(LLVM_LINK_COMPONENTS
DebugInfoCodeView
DebugInfoPDB DebugInfoPDB
Object Object
Support Support

View File

@ -26,6 +26,7 @@
#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringExtras.h"
#include "llvm/Config/config.h" #include "llvm/Config/config.h"
#include "llvm/DebugInfo/CodeView/TypeDumper.h"
#include "llvm/DebugInfo/PDB/GenericError.h" #include "llvm/DebugInfo/PDB/GenericError.h"
#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" #include "llvm/DebugInfo/PDB/IPDBRawSymbol.h"
@ -106,8 +107,8 @@ cl::opt<bool> DumpStreamSizes("dump-stream-sizes",
cl::opt<bool> DumpStreamBlocks("dump-stream-blocks", cl::opt<bool> DumpStreamBlocks("dump-stream-blocks",
cl::desc("dump PDB stream blocks"), cl::desc("dump PDB stream blocks"),
cl::cat(OtherOptions)); cl::cat(OtherOptions));
cl::opt<bool> DumpTypeStream("dump-tpi-stream", cl::opt<bool> DumpTpiRecords("dump-tpi-records",
cl::desc("dump PDB TPI (Type Info) stream"), cl::desc("dump CodeView type records"),
cl::cat(OtherOptions)); cl::cat(OtherOptions));
cl::opt<bool> cl::opt<bool>
DumpTpiRecordBytes("dump-tpi-record-bytes", DumpTpiRecordBytes("dump-tpi-record-bytes",
@ -338,7 +339,7 @@ static Error dumpDbiStream(ScopedPrinter &P, PDBFile &File) {
} }
static Error dumpTpiStream(ScopedPrinter &P, PDBFile &File) { static Error dumpTpiStream(ScopedPrinter &P, PDBFile &File) {
if (!opts::DumpTypeStream) if (!opts::DumpTpiRecordBytes && !opts::DumpTpiRecords)
return Error::success(); return Error::success();
DictScope D(P, "Type Info Stream"); DictScope D(P, "Type Info Stream");
@ -351,14 +352,19 @@ static Error dumpTpiStream(ScopedPrinter &P, PDBFile &File) {
P.printNumber("TPI Version", Tpi.getTpiVersion()); P.printNumber("TPI Version", Tpi.getTpiVersion());
P.printNumber("Record count", Tpi.NumTypeRecords()); P.printNumber("Record count", Tpi.NumTypeRecords());
if (!opts::DumpTpiRecordBytes) if (opts::DumpTpiRecordBytes || opts::DumpTpiRecords) {
return Error::success(); ListScope L(P, "Records");
codeview::CVTypeDumper TD(P, false);
ListScope L(P, "Records"); for (auto &Type : Tpi.types()) {
for (auto &Type : Tpi.types()) { DictScope DD(P, "");
DictScope DD(P, "");
P.printHex("Kind", unsigned(Type.Leaf)); if (opts::DumpTpiRecords)
P.printBinaryBlock("Bytes", Type.LeafData); TD.dump(Type);
if (opts::DumpTpiRecordBytes)
P.printBinaryBlock("Bytes", Type.LeafData);
}
} }
return Error::success(); return Error::success();
} }