From b17277efb8bf2f4eaa820b42d39693f623db9238 Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Thu, 20 Sep 2018 15:50:13 +0000 Subject: [PATCH] [PDB] Add the ability to map forward references to full decls. Some records point to an LF_CLASS, LF_UNION, LF_STRUCTURE, or LF_ENUM which is a forward reference and doesn't contain complete debug information. In these cases, we'd like to be able to quickly locate the full record. The TPI stream stores an array of pre-computed record hash values, one for each type record. If we pre-process this on startup, we can build a mapping from hash value -> {list of possible matching type indices}. Since hashes of full records are only based on the name and or unique name and not the full record contents, we can then use forward ref record to compute the hash of what *would* be the full record by just hashing the name, use this to get the list of possible matches, and iterate those looking for a match on name or unique name. llvm-pdbutil is updated to resolve forward references for the purposes of testing (plus it's just useful). Differential Revision: https://reviews.llvm.org/D52283 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@342656 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/DebugInfo/PDB/Native/RawTypes.h | 1 - include/llvm/DebugInfo/PDB/Native/TpiHashing.h | 48 +++++++++++ include/llvm/DebugInfo/PDB/Native/TpiStream.h | 9 +++ lib/DebugInfo/PDB/Native/TpiHashing.cpp | 43 ++++++++++ lib/DebugInfo/PDB/Native/TpiStream.cpp | 90 +++++++++++++++++++++ test/DebugInfo/PDB/Inputs/every-class.cpp | 61 ++++++++++++++ test/DebugInfo/PDB/Inputs/every-class.pdb | Bin 0 -> 102400 bytes test/DebugInfo/PDB/every-type.test | 6 +- test/DebugInfo/PDB/pdb-resolve-forward-refs.test | 98 +++++++++++++++++++++++ test/DebugInfo/PDB/pdbdump-headers.test | 6 +- tools/llvm-pdbutil/DumpOutputStyle.cpp | 15 ++-- tools/llvm-pdbutil/MinimalTypeDumper.cpp | 38 +++++++-- tools/llvm-pdbutil/MinimalTypeDumper.h | 9 ++- tools/llvm-pdbutil/llvm-pdbutil.cpp | 8 ++ tools/llvm-pdbutil/llvm-pdbutil.h | 1 + 15 files changed, 416 insertions(+), 17 deletions(-) create mode 100644 test/DebugInfo/PDB/Inputs/every-class.cpp create mode 100644 test/DebugInfo/PDB/Inputs/every-class.pdb create mode 100644 test/DebugInfo/PDB/pdb-resolve-forward-refs.test diff --git a/include/llvm/DebugInfo/PDB/Native/RawTypes.h b/include/llvm/DebugInfo/PDB/Native/RawTypes.h index 19f592d562e..8f6d6611c03 100644 --- a/include/llvm/DebugInfo/PDB/Native/RawTypes.h +++ b/include/llvm/DebugInfo/PDB/Native/RawTypes.h @@ -343,7 +343,6 @@ struct SrcHeaderBlockEntry { char Reserved[8]; }; -constexpr int I = sizeof(SrcHeaderBlockEntry); static_assert(sizeof(SrcHeaderBlockEntry) == 40, "Incorrect struct size!"); } // namespace pdb diff --git a/include/llvm/DebugInfo/PDB/Native/TpiHashing.h b/include/llvm/DebugInfo/PDB/Native/TpiHashing.h index c1edec7a26f..5c98be1bb93 100644 --- a/include/llvm/DebugInfo/PDB/Native/TpiHashing.h +++ b/include/llvm/DebugInfo/PDB/Native/TpiHashing.h @@ -18,6 +18,54 @@ namespace pdb { Expected hashTypeRecord(const llvm::codeview::CVType &Type); +struct TagRecordHash { + explicit TagRecordHash(codeview::ClassRecord CR, uint32_t Full, + uint32_t Forward) + : Class(std::move(CR)), FullRecordHash(Full), ForwardDeclHash(Forward) { + State = 0; + } + + explicit TagRecordHash(codeview::EnumRecord ER, uint32_t Full, + uint32_t Forward) + : Enum(std::move(ER)), FullRecordHash(Full), ForwardDeclHash(Forward) { + State = 1; + } + + explicit TagRecordHash(codeview::UnionRecord UR, uint32_t Full, + uint32_t Forward) + : Union(std::move(UR)), FullRecordHash(Full), ForwardDeclHash(Forward) { + State = 2; + } + + uint32_t FullRecordHash; + uint32_t ForwardDeclHash; + + codeview::TagRecord &getRecord() { + switch (State) { + case 0: + return Class; + case 1: + return Enum; + case 2: + return Union; + } + llvm_unreachable("unreachable!"); + } + +private: + union { + codeview::ClassRecord Class; + codeview::EnumRecord Enum; + codeview::UnionRecord Union; + }; + + uint8_t State = 0; +}; + +/// Given a CVType referring to a class, structure, union, or enum, compute +/// the hash of its forward decl and full decl. +Expected hashTagRecord(const codeview::CVType &Type); + } // end namespace pdb } // end namespace llvm diff --git a/include/llvm/DebugInfo/PDB/Native/TpiStream.h b/include/llvm/DebugInfo/PDB/Native/TpiStream.h index b77939929ec..00cc720336c 100644 --- a/include/llvm/DebugInfo/PDB/Native/TpiStream.h +++ b/include/llvm/DebugInfo/PDB/Native/TpiStream.h @@ -58,10 +58,17 @@ public: codeview::LazyRandomTypeCollection &typeCollection() { return *Types; } + Expected + findFullDeclForForwardRef(codeview::TypeIndex ForwardRefTI) const; + BinarySubstreamRef getTypeRecordsSubstream() const; Error commit(); + void buildHashMap(); + + bool supportsTypeLookup() const; + private: PDBFile &Pdb; std::unique_ptr Stream; @@ -77,6 +84,8 @@ private: FixedStreamArray TypeIndexOffsets; HashTable HashAdjusters; + std::vector> HashMap; + const TpiStreamHeader *Header; }; } diff --git a/lib/DebugInfo/PDB/Native/TpiHashing.cpp b/lib/DebugInfo/PDB/Native/TpiHashing.cpp index 77a2d57a836..18708826ffc 100644 --- a/lib/DebugInfo/PDB/Native/TpiHashing.cpp +++ b/lib/DebugInfo/PDB/Native/TpiHashing.cpp @@ -50,6 +50,32 @@ static Expected getHashForUdt(const CVType &Rec) { } template +static Expected getTagRecordHashForUdt(const CVType &Rec) { + T Deserialized; + if (auto E = TypeDeserializer::deserializeAs(const_cast(Rec), + Deserialized)) + return std::move(E); + + ClassOptions Opts = Deserialized.getOptions(); + + bool ForwardRef = bool(Opts & ClassOptions::ForwardReference); + + uint32_t ThisRecordHash = getHashForUdt(Deserialized, Rec.data()); + + // If we don't have a forward ref we can't compute the hash of it from the + // full record because it requires hashing the entire buffer. + if (!ForwardRef) + return TagRecordHash{std::move(Deserialized), ThisRecordHash, 0}; + + bool Scoped = bool(Opts & ClassOptions::Scoped); + + StringRef NameToHash = + Scoped ? Deserialized.getUniqueName() : Deserialized.getName(); + uint32_t FullHash = hashStringV1(NameToHash); + return TagRecordHash{std::move(Deserialized), FullHash, ThisRecordHash}; +} + +template static Expected getSourceLineHash(const CVType &Rec) { T Deserialized; if (auto E = TypeDeserializer::deserializeAs(const_cast(Rec), @@ -60,6 +86,23 @@ static Expected getSourceLineHash(const CVType &Rec) { return hashStringV1(StringRef(Buf, 4)); } +Expected llvm::pdb::hashTagRecord(const codeview::CVType &Type) { + switch (Type.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_INTERFACE: + return getTagRecordHashForUdt(Type); + case LF_UNION: + return getTagRecordHashForUdt(Type); + case LF_ENUM: + return getTagRecordHashForUdt(Type); + default: + assert(false && "Type is not a tag record!"); + } + return make_error("Invalid record type", + inconvertibleErrorCode()); +} + Expected llvm::pdb::hashTypeRecord(const CVType &Rec) { switch (Rec.kind()) { case LF_CLASS: diff --git a/lib/DebugInfo/PDB/Native/TpiStream.cpp b/lib/DebugInfo/PDB/Native/TpiStream.cpp index 0680b673380..de0f888137f 100644 --- a/lib/DebugInfo/PDB/Native/TpiStream.cpp +++ b/lib/DebugInfo/PDB/Native/TpiStream.cpp @@ -11,8 +11,11 @@ #include "llvm/ADT/iterator_range.h" #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/Hash.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/RawConstants.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" @@ -140,6 +143,93 @@ uint16_t TpiStream::getTypeHashStreamAuxIndex() const { uint32_t TpiStream::getNumHashBuckets() const { return Header->NumHashBuckets; } uint32_t TpiStream::getHashKeySize() const { return Header->HashKeySize; } +void TpiStream::buildHashMap() { + if (!HashMap.empty()) + return; + if (HashValues.empty()) + return; + + HashMap.resize(Header->NumHashBuckets); + + TypeIndex TIB{Header->TypeIndexBegin}; + TypeIndex TIE{Header->TypeIndexEnd}; + while (TIB < TIE) { + uint32_t HV = HashValues[TIB.toArrayIndex()]; + HashMap[HV].push_back(TIB++); + } +} + +bool TpiStream::supportsTypeLookup() const { return !HashMap.empty(); } + +template static ClassOptions getUdtOptions(CVType CVT) { + RecordT Record; + if (auto EC = TypeDeserializer::deserializeAs(CVT, Record)) { + consumeError(std::move(EC)); + return ClassOptions::None; + } + return Record.getOptions(); +} + +static bool isUdtForwardRef(CVType CVT) { + ClassOptions UdtOptions = ClassOptions::None; + switch (CVT.kind()) { + case LF_STRUCTURE: + case LF_CLASS: + case LF_INTERFACE: + UdtOptions = getUdtOptions(std::move(CVT)); + break; + case LF_ENUM: + UdtOptions = getUdtOptions(std::move(CVT)); + break; + case LF_UNION: + UdtOptions = getUdtOptions(std::move(CVT)); + break; + default: + return false; + } + return (UdtOptions & ClassOptions::ForwardReference) != ClassOptions::None; +} + +Expected +TpiStream::findFullDeclForForwardRef(TypeIndex ForwardRefTI) const { + CVType F = Types->getType(ForwardRefTI); + if (!isUdtForwardRef(F)) + return ForwardRefTI; + + Expected ForwardTRH = hashTagRecord(F); + if (!ForwardTRH) + return ForwardTRH.takeError(); + + TagRecordHash Copy = std::move(*ForwardTRH); + uint32_t BucketIdx = ForwardTRH->FullRecordHash % Header->NumHashBuckets; + + for (TypeIndex TI : HashMap[BucketIdx]) { + CVType CVT = Types->getType(TI); + if (CVT.kind() != F.kind()) + continue; + + Expected FullTRH = hashTagRecord(CVT); + if (!FullTRH) + return FullTRH.takeError(); + if (ForwardTRH->FullRecordHash != FullTRH->FullRecordHash) + continue; + TagRecord &ForwardTR = ForwardTRH->getRecord(); + TagRecord &FullTR = FullTRH->getRecord(); + + if (!ForwardTR.hasUniqueName()) { + if (ForwardTR.getName() == FullTR.getName()) + return TI; + continue; + } + + if (!FullTR.hasUniqueName()) + continue; + if (ForwardTR.getUniqueName() == FullTR.getUniqueName()) + return TI; + } + return ForwardRefTI; +} + BinarySubstreamRef TpiStream::getTypeRecordsSubstream() const { return TypeRecordsSubstream; } diff --git a/test/DebugInfo/PDB/Inputs/every-class.cpp b/test/DebugInfo/PDB/Inputs/every-class.cpp new file mode 100644 index 00000000000..c439bc2f365 --- /dev/null +++ b/test/DebugInfo/PDB/Inputs/every-class.cpp @@ -0,0 +1,61 @@ +// Build with "cl.exe /Z7 /GR- /GS- /GX- every-class.cpp /link /debug:full /nodefaultlib /incremental:no /entry:main" + +#include + +// clang-format off +void *__purecall = 0; + +void __cdecl operator delete(void *, unsigned int) {} +void __cdecl operator delete(void *, unsigned __int64) {} + +struct Nothing {}; +struct Constructor { Constructor() {} }; +struct Assignment { + Assignment &operator=(Assignment Other) { return *this; } +}; +struct Cast { + operator int() { return 42; } +}; + +struct Nested { + struct F {}; +}; +struct Operator { + int operator+(int X) { return 42; } +}; + +class Class {}; + +union Union {}; + +enum class Enum {A}; + + +template void f(T t) {} + +int main(int argc, char **argv) { + struct Scoped {}; + + struct { } Anonymous; + + f(Nothing{}); + f(Constructor{}); + f(Assignment{}); + f(Cast{}); + f(Nested{}); + f(Operator{}); + f(Nested::F{}); + f(Scoped{}); + f(Class{}); + f(Union{}); + f(Anonymous); + f(Enum::A); + + + f(Nothing{}); + f(Nothing{}); + f(Nothing{}); + f<__unaligned Nothing>(Nothing{}); + + return 0; +} diff --git a/test/DebugInfo/PDB/Inputs/every-class.pdb b/test/DebugInfo/PDB/Inputs/every-class.pdb new file mode 100644 index 0000000000000000000000000000000000000000..6462a705c86de5235018181b1abe408cafcb33a9 GIT binary patch literal 102400 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 %t.pdb.yaml RUN: llvm-pdbutil yaml2pdb -pdb=%t.yaml.pdb %t.pdb.yaml RUN: llvm-pdbutil dump -type-index=0x101A,0x102C,0x103D,0x104D,0x1098,0x10AA,0x10AC \ -RUN: -dependents %t.yaml.pdb | FileCheck --check-prefix=TYPES %s +RUN: -dependents -dont-resolve-forward-refs %t.yaml.pdb \ +RUN: | FileCheck --check-prefix=TYPES %s TYPES: Types (TPI Stream) TYPES-NEXT: ============================================================ diff --git a/test/DebugInfo/PDB/pdb-resolve-forward-refs.test b/test/DebugInfo/PDB/pdb-resolve-forward-refs.test new file mode 100644 index 00000000000..adb1c61d862 --- /dev/null +++ b/test/DebugInfo/PDB/pdb-resolve-forward-refs.test @@ -0,0 +1,98 @@ +; RUN: llvm-pdbutil dump -types %p/Inputs/every-class.pdb \ +; RUN: | FileCheck %s + +; CHECK: Types (TPI Stream) +; CHECK: ============================================================ +; CHECK: Showing 157 records +; CHECK: 0x1008 | LF_STRUCTURE [size = 124] `main::__l2::` +; CHECK: unique name: `.?AU@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +; CHECK: vtable: , base list: , field list: 0x1007 +; CHECK: options: has unique name | scoped, sizeof 1 +; CHECK: 0x1009 | LF_STRUCTURE [size = 88] `main::__l2::Scoped` +; CHECK: unique name: `.?AUScoped@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +; CHECK: vtable: , base list: , field list: 0x1007 +; CHECK: options: has unique name | scoped, sizeof 1 +; CHECK: 0x1054 | LF_STRUCTURE [size = 48] `Nested::F` +; CHECK: unique name: `.?AUF@Nested@@` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (-> 0x1057) | has unique name | is nested, sizeof 0 +; CHECK: 0x1056 | LF_STRUCTURE [size = 44] `Nested` +; CHECK: unique name: `.?AUNested@@` +; CHECK: vtable: , base list: , field list: 0x1055 +; CHECK: options: contains nested class | has unique name, sizeof 1 +; CHECK: 0x1057 | LF_STRUCTURE [size = 48] `Nested::F` +; CHECK: unique name: `.?AUF@Nested@@` +; CHECK: vtable: , base list: , field list: 0x1007 +; CHECK: options: has unique name | is nested, sizeof 1 +; CHECK: 0x1058 | LF_STRUCTURE [size = 52] `Constructor` +; CHECK: unique name: `.?AUConstructor@@` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (-> 0x105C) | has unique name, sizeof 0 +; CHECK: 0x105C | LF_STRUCTURE [size = 52] `Constructor` +; CHECK: unique name: `.?AUConstructor@@` +; CHECK: vtable: , base list: , field list: 0x105B +; CHECK: options: has ctor / dtor | has unique name, sizeof 1 +; CHECK: 0x105D | LF_CLASS [size = 40] `Class` +; CHECK: unique name: `.?AVClass@@` +; CHECK: vtable: , base list: , field list: 0x1007 +; CHECK: options: has unique name, sizeof 1 +; CHECK: 0x105E | LF_UNION [size = 32] `Union` +; CHECK: unique name: `.?ATUnion@@` +; CHECK: field list: 0x1007 +; CHECK: options: has unique name | sealed, sizeof 1 +; CHECK: 0x105F | LF_STRUCTURE [size = 48] `Operator` +; CHECK: unique name: `.?AUOperator@@` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (-> 0x1064) | has unique name, sizeof 0 +; CHECK: 0x1064 | LF_STRUCTURE [size = 48] `Operator` +; CHECK: unique name: `.?AUOperator@@` +; CHECK: vtable: , base list: , field list: 0x1063 +; CHECK: options: has unique name | overloaded operator, sizeof 1 +; CHECK: 0x1066 | LF_ENUM [size = 36] `Enum` +; CHECK: unique name: `.?AW4Enum@@` +; CHECK: field list: 0x1065, underlying type: 0x0074 (int) +; CHECK: options: has unique name +; CHECK: 0x1067 | LF_STRUCTURE [size = 40] `Cast` +; CHECK: unique name: `.?AUCast@@` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (-> 0x106B) | has unique name, sizeof 0 +; CHECK: 0x106B | LF_STRUCTURE [size = 40] `Cast` +; CHECK: unique name: `.?AUCast@@` +; CHECK: vtable: , base list: , field list: 0x106A +; CHECK: options: conversion operator | has unique name | overloaded operator, sizeof 1 +; CHECK: 0x106C | LF_STRUCTURE [size = 44] `Nothing` +; CHECK: unique name: `.?AUNothing@@` +; CHECK: vtable: , base list: , field list: 0x1007 +; CHECK: options: has unique name, sizeof 1 +; CHECK: 0x106D | LF_STRUCTURE [size = 52] `Assignment` +; CHECK: unique name: `.?AUAssignment@@` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (-> 0x1073) | has unique name, sizeof 0 +; CHECK: 0x1073 | LF_STRUCTURE [size = 52] `Assignment` +; CHECK: unique name: `.?AUAssignment@@` +; CHECK: vtable: , base list: , field list: 0x1072 +; CHECK: options: has unique name | overloaded operator | overloaded operator=, sizeof 1 +; CHECK: 0x1074 | LF_STRUCTURE [size = 44] `Nothing` +; CHECK: unique name: `.?AUNothing@@` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (<- 0x106C) | has unique name, sizeof 0 +; CHECK: 0x1081 | LF_UNION [size = 32] `Union` +; CHECK: unique name: `.?ATUnion@@` +; CHECK: field list: +; CHECK: options: forward ref (<- 0x105E) | has unique name, sizeof 0 +; CHECK: 0x1084 | LF_STRUCTURE [size = 124] `main::__l2::` +; CHECK: unique name: `.?AU@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (<- 0x1008) | has unique name | scoped, sizeof 0 +; CHECK: 0x108E | LF_STRUCTURE [size = 44] `Nested` +; CHECK: unique name: `.?AUNested@@` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (<- 0x1056) | has unique name, sizeof 0 +; CHECK: 0x1095 | LF_STRUCTURE [size = 88] `main::__l2::Scoped` +; CHECK: unique name: `.?AUScoped@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (<- 0x1009) | has unique name | scoped, sizeof 0 +; CHECK: 0x1098 | LF_CLASS [size = 40] `Class` +; CHECK: unique name: `.?AVClass@@` +; CHECK: vtable: , base list: , field list: +; CHECK: options: forward ref (<- 0x105D) | has unique name, sizeof 0 diff --git a/test/DebugInfo/PDB/pdbdump-headers.test b/test/DebugInfo/PDB/pdbdump-headers.test index 99c37218e92..2a6c862ac2a 100644 --- a/test/DebugInfo/PDB/pdbdump-headers.test +++ b/test/DebugInfo/PDB/pdbdump-headers.test @@ -1,7 +1,9 @@ -; RUN: llvm-pdbutil dump -all %p/Inputs/empty.pdb | FileCheck -check-prefix=ALL %s +; RUN: llvm-pdbutil dump -all -dont-resolve-forward-refs %p/Inputs/empty.pdb \ +; RUN: | FileCheck -check-prefix=ALL %s ; RUN: llvm-pdbutil dump -summary -modules -files \ ; RUN: %p/Inputs/big-read.pdb | FileCheck -check-prefix=BIG %s -; RUN: not llvm-pdbutil dump -summary %p/Inputs/bad-block-size.pdb 2>&1 | FileCheck -check-prefix=BAD-BLOCK-SIZE %s +; RUN: not llvm-pdbutil dump -summary %p/Inputs/bad-block-size.pdb 2>&1 \ +; RUN: | FileCheck -check-prefix=BAD-BLOCK-SIZE %s ALL: Summary ALL-NEXT: ============================================================ diff --git a/tools/llvm-pdbutil/DumpOutputStyle.cpp b/tools/llvm-pdbutil/DumpOutputStyle.cpp index 9869b3ae4d2..812aab5bbc2 100644 --- a/tools/llvm-pdbutil/DumpOutputStyle.cpp +++ b/tools/llvm-pdbutil/DumpOutputStyle.cpp @@ -1241,13 +1241,13 @@ static void dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types, uint32_t NumTypeRecords, uint32_t NumHashBuckets, FixedStreamArray HashValues, - bool Bytes, bool Extras) { + TpiStream *Stream, bool Bytes, bool Extras) { Printer.formatLine("Showing {0:N} records", NumTypeRecords); uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + NumTypeRecords); MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, - NumHashBuckets, HashValues); + NumHashBuckets, HashValues, Stream); if (auto EC = codeview::visitTypeStream(Types, V)) { Printer.formatLine("An error occurred dumping type records: {0}", @@ -1263,7 +1263,8 @@ static void dumpPartialTypeStream(LinePrinter &Printer, NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords()); MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, - Stream.getNumHashBuckets(), Stream.getHashValues()); + Stream.getNumHashBuckets(), Stream.getHashValues(), + &Stream); if (opts::dump::DumpTypeDependents) { // If we need to dump all dependents, then iterate each index and find @@ -1325,7 +1326,8 @@ Error DumpOutputStyle::dumpTypesFromObjectFile() { Types.reset(Reader, 100); if (opts::dump::DumpTypes) { - dumpFullTypeStream(P, Types, 0, 0, {}, opts::dump::DumpTypeData, false); + dumpFullTypeStream(P, Types, 0, 0, {}, nullptr, opts::dump::DumpTypeData, + false); } else if (opts::dump::DumpTypeExtras) { auto LocalHashes = LocallyHashedType::hashTypeCollection(Types); auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types); @@ -1394,11 +1396,14 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids(); + // Enable resolving forward decls. + Stream.buildHashMap(); + if (DumpTypes || !Indices.empty()) { if (Indices.empty()) dumpFullTypeStream(P, Types, Stream.getNumTypeRecords(), Stream.getNumHashBuckets(), Stream.getHashValues(), - DumpBytes, DumpExtras); + &Stream, DumpBytes, DumpExtras); else { std::vector TiList(Indices.begin(), Indices.end()); dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras, diff --git a/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/tools/llvm-pdbutil/MinimalTypeDumper.cpp index 569bca7490f..8a5e283dfad 100644 --- a/tools/llvm-pdbutil/MinimalTypeDumper.cpp +++ b/tools/llvm-pdbutil/MinimalTypeDumper.cpp @@ -12,6 +12,7 @@ #include "FormatUtil.h" #include "LinePrinter.h" +#include "llvm-pdbutil.h" #include "llvm/DebugInfo/CodeView/CVRecord.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/CodeView.h" @@ -19,6 +20,7 @@ #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/PDB/Native/TpiHashing.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MathExtras.h" @@ -27,15 +29,37 @@ using namespace llvm::codeview; using namespace llvm::pdb; static std::string formatClassOptions(uint32_t IndentLevel, - ClassOptions Options) { + ClassOptions Options, TpiStream *Stream, + TypeIndex CurrentTypeIndex) { std::vector Opts; + + if (Stream && Stream->supportsTypeLookup() && + !opts::dump::DontResolveForwardRefs && + ((Options & ClassOptions::ForwardReference) != ClassOptions::None)) { + // If we're able to resolve forward references, do that. + Expected ETI = + Stream->findFullDeclForForwardRef(CurrentTypeIndex); + if (!ETI) { + consumeError(ETI.takeError()); + PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref (???)"); + } else { + const char *Direction = (*ETI == CurrentTypeIndex) + ? "=" + : ((*ETI < CurrentTypeIndex) ? "<-" : "->"); + std::string Formatted = + formatv("forward ref ({0} {1})", Direction, *ETI).str(); + PUSH_FLAG(ClassOptions, ForwardReference, Options, std::move(Formatted)); + } + } else { + PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref"); + } + PUSH_FLAG(ClassOptions, HasConstructorOrDestructor, Options, "has ctor / dtor"); PUSH_FLAG(ClassOptions, ContainsNestedClass, Options, "contains nested class"); PUSH_FLAG(ClassOptions, HasConversionOperator, Options, "conversion operator"); - PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref"); PUSH_FLAG(ClassOptions, HasUniqueName, Options, "has unique name"); PUSH_FLAG(ClassOptions, Intrinsic, Options, "intrin"); PUSH_FLAG(ClassOptions, Nested, Options, "is nested"); @@ -194,6 +218,7 @@ static std::string formatFunctionOptions(FunctionOptions Options) { } Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) { + CurrentTypeIndex = Index; // formatLine puts the newline at the beginning, so we use formatLine here // to start a new line, and then individual visit methods use format to // append to the existing line. @@ -304,7 +329,8 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, P.formatLine("vtable: {0}, base list: {1}, field list: {2}", Class.VTableShape, Class.DerivationList, Class.FieldList); P.formatLine("options: {0}, sizeof {1}", - formatClassOptions(P.getIndentLevel(), Class.Options), + formatClassOptions(P.getIndentLevel(), Class.Options, Stream, + CurrentTypeIndex), Class.Size); return Error::success(); } @@ -316,7 +342,8 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, P.formatLine("unique name: `{0}`", Union.UniqueName); P.formatLine("field list: {0}", Union.FieldList); P.formatLine("options: {0}, sizeof {1}", - formatClassOptions(P.getIndentLevel(), Union.Options), + formatClassOptions(P.getIndentLevel(), Union.Options, Stream, + CurrentTypeIndex), Union.Size); return Error::success(); } @@ -328,7 +355,8 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) { P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList, Enum.UnderlyingType); P.formatLine("options: {0}", - formatClassOptions(P.getIndentLevel(), Enum.Options)); + formatClassOptions(P.getIndentLevel(), Enum.Options, Stream, + CurrentTypeIndex)); return Error::success(); } diff --git a/tools/llvm-pdbutil/MinimalTypeDumper.h b/tools/llvm-pdbutil/MinimalTypeDumper.h index 4227688f0f7..8f6bdc6110a 100644 --- a/tools/llvm-pdbutil/MinimalTypeDumper.h +++ b/tools/llvm-pdbutil/MinimalTypeDumper.h @@ -20,15 +20,18 @@ class LazyRandomTypeCollection; namespace pdb { class LinePrinter; +class TpiStream; class MinimalTypeDumpVisitor : public codeview::TypeVisitorCallbacks { public: MinimalTypeDumpVisitor(LinePrinter &P, uint32_t Width, bool RecordBytes, bool Hashes, codeview::LazyRandomTypeCollection &Types, uint32_t NumHashBuckets, - FixedStreamArray HashValues) + FixedStreamArray HashValues, + pdb::TpiStream *Stream) : P(P), Width(Width), RecordBytes(RecordBytes), Hashes(Hashes), - Types(Types), NumHashBuckets(NumHashBuckets), HashValues(HashValues) {} + Types(Types), NumHashBuckets(NumHashBuckets), HashValues(HashValues), + Stream(Stream) {} Error visitTypeBegin(codeview::CVType &Record, codeview::TypeIndex Index) override; @@ -55,7 +58,9 @@ private: bool Hashes = false; codeview::LazyRandomTypeCollection &Types; uint32_t NumHashBuckets; + codeview::TypeIndex CurrentTypeIndex; FixedStreamArray HashValues; + pdb::TpiStream *Stream = nullptr; }; } // namespace pdb } // namespace llvm diff --git a/tools/llvm-pdbutil/llvm-pdbutil.cpp b/tools/llvm-pdbutil/llvm-pdbutil.cpp index 51dcd977a32..ab9b4593c2b 100644 --- a/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -183,6 +183,8 @@ static cl::opt Enums("enums", cl::desc("Dump enum types"), cl::sub(DiaDumpSubcommand)); static cl::opt Pointers("pointers", cl::desc("Dump enum types"), cl::sub(DiaDumpSubcommand)); +static cl::opt UDTs("udts", cl::desc("Dump udt types"), + cl::sub(DiaDumpSubcommand)); static cl::opt Compilands("compilands", cl::desc("Dump compiland information"), cl::sub(DiaDumpSubcommand)); @@ -465,6 +467,12 @@ cl::opt DumpTypeExtras("type-extras", cl::desc("dump type hashes and index offsets"), cl::cat(TypeOptions), cl::sub(DumpSubcommand)); +cl::opt DontResolveForwardRefs( + "dont-resolve-forward-refs", + cl::desc("When dumping type records for classes, unions, enums, and " + "structs, don't try to resolve forward references"), + cl::cat(TypeOptions), cl::sub(DumpSubcommand)); + cl::list DumpTypeIndex( "type-index", cl::ZeroOrMore, cl::CommaSeparated, cl::desc("only dump types with the specified hexadecimal type index"), diff --git a/tools/llvm-pdbutil/llvm-pdbutil.h b/tools/llvm-pdbutil/llvm-pdbutil.h index 1524f83560c..d25b0b6e8eb 100644 --- a/tools/llvm-pdbutil/llvm-pdbutil.h +++ b/tools/llvm-pdbutil/llvm-pdbutil.h @@ -160,6 +160,7 @@ extern llvm::cl::opt DumpIdExtras; extern llvm::cl::list DumpIdIndex; extern llvm::cl::opt DumpModi; extern llvm::cl::opt JustMyCode; +extern llvm::cl::opt DontResolveForwardRefs; extern llvm::cl::opt DumpSymbols; extern llvm::cl::opt DumpSymRecordBytes; extern llvm::cl::opt DumpGSIRecords; -- 2.11.0