diff options
author | Zachary Turner <zturner@google.com> | 2017-09-01 20:06:56 +0000 |
---|---|---|
committer | Zachary Turner <zturner@google.com> | 2017-09-01 20:06:56 +0000 |
commit | abb17cc084d917a54b3e5021bbb3ecdfc1a49d23 (patch) | |
tree | 2ceb0b03a84f9954158397e0222afba212cf9227 | |
parent | c36039f462a8270a69b64309810f537e4991bb1c (diff) | |
download | bcm5719-llvm-abb17cc084d917a54b3e5021bbb3ecdfc1a49d23.tar.gz bcm5719-llvm-abb17cc084d917a54b3e5021bbb3ecdfc1a49d23.zip |
[llvm-pdbutil] Support dumping CodeView from object files.
We have llvm-readobj for dumping CodeView from object files, and
llvm-pdbutil has always been more focused on PDB. However,
llvm-pdbutil has a lot of useful options for summarizing debug
information in aggregate and presenting high level statistical
views. Furthermore, it's arguably better as a testing tool since
we don't have to write tests to conform to a state-machine like
structure where you match multiple lines in succession, each
depending on a previous match. llvm-pdbutil dumps much more
concisely, so it's possible to use single-line matches in many
cases where as with readobj tests you have to use multi-line
matches with an implicit state machine.
Because of this, I'm adding object file support to llvm-pdbutil.
In fact, this mirrors the cvdump tool from Microsoft, which also
supports both object files and pdb files. In the future we could
perhaps rename this tool llvm-cvutil.
In the meantime, this allows us to deep dive into object files
the same way we already can with PDB files.
llvm-svn: 312358
-rw-r--r-- | lld/test/COFF/pdb-comdat.test | 4 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h | 2 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/CodeView/StringsAndChecksums.h | 9 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/PDB/Native/ModuleDebugStream.h | 3 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/PDB/Native/PDBFile.h | 2 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp | 13 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/CodeView/StringsAndChecksums.cpp | 28 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp | 16 | ||||
-rw-r--r-- | llvm/test/DebugInfo/COFF/udts.ll | 80 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/CMakeLists.txt | 2 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp | 640 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/DumpOutputStyle.h | 17 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/InputFile.cpp | 469 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/InputFile.h | 147 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/LinePrinter.h | 33 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp | 7 | ||||
-rw-r--r-- | llvm/tools/llvm-pdbutil/llvm-pdbutil.h | 7 |
17 files changed, 1103 insertions, 376 deletions
diff --git a/lld/test/COFF/pdb-comdat.test b/lld/test/COFF/pdb-comdat.test index 516828b005d..a3283d206ab 100644 --- a/lld/test/COFF/pdb-comdat.test +++ b/lld/test/COFF/pdb-comdat.test @@ -25,8 +25,8 @@ pdb_comdat_main.obj should be included in the PDB. RUN: rm -rf %t && mkdir -p %t && cd %t RUN: yaml2obj %S/Inputs/pdb_comdat_main.yaml -o pdb_comdat_main.obj RUN: yaml2obj %S/Inputs/pdb_comdat_bar.yaml -o pdb_comdat_bar.obj -RUN: lld-link pdb_comdat_main.obj pdb_comdat_bar.obj -out:t.exe -debug -pdb:t.pdb -nodefaultlib -entry:main -RUN: llvm-pdbutil dump -l -symbols -globals t.pdb | FileCheck %s +RUN: lld-link pdb_comdat_main.obj pdb_comdat_bar.obj -out:%t.exe -debug -pdb:%t.pdb -nodefaultlib -entry:main +RUN: llvm-pdbutil dump -l -symbols -globals %t.pdb | FileCheck %s CHECK: Lines CHECK: ============================================================ diff --git a/llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h b/llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h index cc0c24301d4..1d5117475bb 100644 --- a/llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h +++ b/llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h @@ -70,6 +70,8 @@ public: uint32_t getOffsetOfType(TypeIndex Index); + Optional<CVType> tryGetType(TypeIndex Index); + CVType getType(TypeIndex Index) override; StringRef getTypeName(TypeIndex Index) override; bool contains(TypeIndex Index) override; diff --git a/llvm/include/llvm/DebugInfo/CodeView/StringsAndChecksums.h b/llvm/include/llvm/DebugInfo/CodeView/StringsAndChecksums.h index 1a838822466..22a333e631a 100644 --- a/llvm/include/llvm/DebugInfo/CodeView/StringsAndChecksums.h +++ b/llvm/include/llvm/DebugInfo/CodeView/StringsAndChecksums.h @@ -31,8 +31,13 @@ public: StringsAndChecksumsRef(const DebugStringTableSubsectionRef &Strings, const DebugChecksumsSubsectionRef &Checksums); + void setStrings(const DebugStringTableSubsectionRef &Strings); void setChecksums(const DebugChecksumsSubsectionRef &CS); + void reset(); + void resetStrings(); + void resetChecksums(); + template <typename T> void initialize(T &&FragmentRange) { for (const DebugSubsectionRecord &R : FragmentRange) { if (Strings && Checksums) @@ -67,8 +72,8 @@ private: void initializeStrings(const DebugSubsectionRecord &SR); void initializeChecksums(const DebugSubsectionRecord &FCR); - std::unique_ptr<DebugStringTableSubsectionRef> OwnedStrings; - std::unique_ptr<DebugChecksumsSubsectionRef> OwnedChecksums; + std::shared_ptr<DebugStringTableSubsectionRef> OwnedStrings; + std::shared_ptr<DebugChecksumsSubsectionRef> OwnedChecksums; const DebugStringTableSubsectionRef *Strings = nullptr; const DebugChecksumsSubsectionRef *Checksums = nullptr; diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/ModuleDebugStream.h b/llvm/include/llvm/DebugInfo/PDB/Native/ModuleDebugStream.h index 5e81fbec838..6602264d1b7 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/ModuleDebugStream.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/ModuleDebugStream.h @@ -52,6 +52,9 @@ public: ModuleDebugStreamRef &operator=(ModuleDebugStreamRef &&Other) = default; iterator_range<DebugSubsectionIterator> subsections() const; + codeview::DebugSubsectionArray getSubsectionsArray() const { + return Subsections; + } bool hasDebugSubsections() const; diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/PDBFile.h b/llvm/include/llvm/DebugInfo/PDB/Native/PDBFile.h index 285f6c495a4..5e39ac3e37b 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/PDBFile.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/PDBFile.h @@ -105,7 +105,7 @@ public: bool hasPDBDbiStream() const; bool hasPDBGlobalsStream(); - bool hasPDBInfoStream(); + bool hasPDBInfoStream() const; bool hasPDBIpiStream() const; bool hasPDBPublicsStream(); bool hasPDBSymbolStream(); diff --git a/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp b/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp index 9ba45fd0519..bad291e8381 100644 --- a/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp +++ b/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp @@ -90,6 +90,16 @@ CVType LazyRandomTypeCollection::getType(TypeIndex Index) { return Records[Index.toArrayIndex()].Type; } +Optional<CVType> LazyRandomTypeCollection::tryGetType(TypeIndex Index) { + if (auto EC = ensureTypeExists(Index)) { + consumeError(std::move(EC)); + return None; + } + + assert(contains(Index)); + return Records[Index.toArrayIndex()].Type; +} + StringRef LazyRandomTypeCollection::getTypeName(TypeIndex Index) { if (Index.isNoneType() || Index.isSimple()) return TypeIndex::simpleTypeName(Index); @@ -113,6 +123,9 @@ StringRef LazyRandomTypeCollection::getTypeName(TypeIndex Index) { } bool LazyRandomTypeCollection::contains(TypeIndex Index) { + if (Index.isSimple() || Index.isNoneType()) + return false; + if (Records.size() <= Index.toArrayIndex()) return false; if (!Records[Index.toArrayIndex()].Type.valid()) diff --git a/llvm/lib/DebugInfo/CodeView/StringsAndChecksums.cpp b/llvm/lib/DebugInfo/CodeView/StringsAndChecksums.cpp index 306af1d1ef6..85d9dbb8c7d 100644 --- a/llvm/lib/DebugInfo/CodeView/StringsAndChecksums.cpp +++ b/llvm/lib/DebugInfo/CodeView/StringsAndChecksums.cpp @@ -35,14 +35,36 @@ void StringsAndChecksumsRef::initializeStrings( assert(SR.kind() == DebugSubsectionKind::StringTable); assert(!Strings && "Found a string table even though we already have one!"); - OwnedStrings = llvm::make_unique<DebugStringTableSubsectionRef>(); + OwnedStrings = std::make_shared<DebugStringTableSubsectionRef>(); consumeError(OwnedStrings->initialize(SR.getRecordData())); Strings = OwnedStrings.get(); } +void StringsAndChecksumsRef::reset() { + resetStrings(); + resetChecksums(); +} + +void StringsAndChecksumsRef::resetStrings() { + OwnedStrings.reset(); + Strings = nullptr; +} + +void StringsAndChecksumsRef::resetChecksums() { + OwnedChecksums.reset(); + Checksums = nullptr; +} + +void StringsAndChecksumsRef::setStrings( + const DebugStringTableSubsectionRef &StringsRef) { + OwnedStrings = std::make_shared<DebugStringTableSubsectionRef>(); + *OwnedStrings = StringsRef; + Strings = OwnedStrings.get(); +} + void StringsAndChecksumsRef::setChecksums( const DebugChecksumsSubsectionRef &CS) { - OwnedChecksums = llvm::make_unique<DebugChecksumsSubsectionRef>(); + OwnedChecksums = std::make_shared<DebugChecksumsSubsectionRef>(); *OwnedChecksums = CS; Checksums = OwnedChecksums.get(); } @@ -53,7 +75,7 @@ void StringsAndChecksumsRef::initializeChecksums( if (Checksums) return; - OwnedChecksums = llvm::make_unique<DebugChecksumsSubsectionRef>(); + OwnedChecksums = std::make_shared<DebugChecksumsSubsectionRef>(); consumeError(OwnedChecksums->initialize(FCR.getRecordData())); Checksums = OwnedChecksums.get(); } diff --git a/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp b/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp index 414de7be91f..15b31d821b1 100644 --- a/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp @@ -312,6 +312,9 @@ Expected<TpiStream &> PDBFile::getPDBTpiStream() { Expected<TpiStream &> PDBFile::getPDBIpiStream() { if (!Ipi) { + if (!hasPDBIpiStream()) + return make_error<RawError>(raw_error_code::no_stream); + auto IpiS = safelyCreateIndexedStream(ContainerLayout, *Buffer, StreamIPI); if (!IpiS) return IpiS.takeError(); @@ -407,9 +410,18 @@ bool PDBFile::hasPDBGlobalsStream() { return DbiS->getGlobalSymbolStreamIndex() < getNumStreams(); } -bool PDBFile::hasPDBInfoStream() { return StreamPDB < getNumStreams(); } +bool PDBFile::hasPDBInfoStream() const { return StreamPDB < getNumStreams(); } + +bool PDBFile::hasPDBIpiStream() const { + if (!hasPDBInfoStream()) + return false; + + if (StreamIPI >= getNumStreams()) + return false; -bool PDBFile::hasPDBIpiStream() const { return StreamIPI < getNumStreams(); } + auto &InfoStream = cantFail(const_cast<PDBFile *>(this)->getPDBInfoStream()); + return InfoStream.containsIdStream(); +} bool PDBFile::hasPDBPublicsStream() { auto DbiS = getPDBDbiStream(); diff --git a/llvm/test/DebugInfo/COFF/udts.ll b/llvm/test/DebugInfo/COFF/udts.ll index 63bdfc83683..a9a59ea5410 100644 --- a/llvm/test/DebugInfo/COFF/udts.ll +++ b/llvm/test/DebugInfo/COFF/udts.ll @@ -1,4 +1,7 @@ -; RUN: llc < %s -filetype=obj | llvm-readobj - -codeview | FileCheck %s +; RUN: llc < %s -filetype=obj > %t.obj +; RUN: llvm-readobj -codeview %t.obj | FileCheck --check-prefix=READOBJ %s +; RUN: llvm-pdbutil dump -udt-stats %t.obj | FileCheck --check-prefix=PDBUTIL %s + source_filename = "test/DebugInfo/COFF/udts.ll" target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32" target triple = "i686-pc-windows-msvc18.0.0" @@ -18,39 +21,52 @@ target triple = "i686-pc-windows-msvc18.0.0" ; typedef struct { int x; } U; ; U u; -; CHECK: {{.*}}Proc{{.*}}Sym { -; CHECK: DisplayName: f -; CHECK: LinkageName: ?f@@YAXXZ -; CHECK: } -; CHECK: UDTSym { -; CHECK-NEXT: Kind: S_UDT (0x1108) -; CHECK-NEXT: Type: int (0x74) -; CHECK-NEXT: UDTName: f::FOO -; CHECK-NEXT: } -; CHECK-NEXT: ProcEnd { +; READOBJ: {{.*}}Proc{{.*}}Sym { +; READOBJ: DisplayName: f +; READOBJ: LinkageName: ?f@@YAXXZ +; READOBJ: } +; READOBJ: UDTSym { +; READOBJ-NEXT: Kind: S_UDT (0x1108) +; READOBJ-NEXT: Type: int (0x74) +; READOBJ-NEXT: UDTName: f::FOO +; READOBJ-NEXT: } +; READOBJ-NEXT: ProcEnd { + +; READOBJ: {{.*}}Proc{{.*}}Sym { +; READOBJ: DisplayName: g +; READOBJ: LinkageName: ?g@@YAMPEAUS@@@Z +; READOBJ: } +; READOBJ: UDTSym { +; READOBJ-NEXT: Kind: S_UDT (0x1108) +; READOBJ-NEXT: Type: g::pun (0x{{[0-9A-F]+}}) +; READOBJ-NEXT: UDTName: g::pun +; READOBJ-NEXT: } +; READOBJ-NEXT: ProcEnd { -; CHECK: {{.*}}Proc{{.*}}Sym { -; CHECK: DisplayName: g -; CHECK: LinkageName: ?g@@YAMPEAUS@@@Z -; CHECK: } -; CHECK: UDTSym { -; CHECK-NEXT: Kind: S_UDT (0x1108) -; CHECK-NEXT: Type: g::pun (0x{{[0-9A-F]+}}) -; CHECK-NEXT: UDTName: g::pun -; CHECK-NEXT: } -; CHECK-NEXT: ProcEnd { +; READOBJ: Subsection +; READOBJ-NOT: {{.*}}Proc{{.*}}Sym +; READOBJ: UDTSym { +; READOBJ-NEXT: Kind: S_UDT (0x1108) +; READOBJ-NEXT: Type: S (0x{{[0-9A-F]+}}) +; READOBJ-NEXT: UDTName: S +; READOBJ: UDTSym { +; READOBJ-NEXT: Kind: S_UDT (0x1108) +; READOBJ-NEXT: Type: <unnamed-tag> (0x{{[0-9A-F]+}}) +; READOBJ-NEXT: UDTName: U +; READOBJ-NOT: UDTSym { -; CHECK: Subsection -; CHECK-NOT: {{.*}}Proc{{.*}}Sym -; CHECK: UDTSym { -; CHECK-NEXT: Kind: S_UDT (0x1108) -; CHECK-NEXT: Type: S (0x{{[0-9A-F]+}}) -; CHECK-NEXT: UDTName: S -; CHECK: UDTSym { -; CHECK-NEXT: Kind: S_UDT (0x1108) -; CHECK-NEXT: Type: <unnamed-tag> (0x{{[0-9A-F]+}}) -; CHECK-NEXT: UDTName: U -; CHECK-NOT: UDTSym { +; PDBUTIL: S_UDT Record Stats +; PDBUTIL-NEXT: ============================================================ +; PDBUTIL: Record Kind | Count Size +; PDBUTIL-NEXT: ----------------------------- +; PDBUTIL-NEXT: LF_UNION | 1 24 +; PDBUTIL-NEXT: <simple type> | 1 0 +; PDBUTIL-NEXT: LF_STRUCTURE | 2 76 +; PDBUTIL-NEXT: ----------------------------- +; PDBUTIL-NEXT: Total (S_UDT) | 4 50 +; PDBUTIL-NEXT: ----------------------------- +; PDBUTIL-NEXT: namespace 'f' | 1 0 +; PDBUTIL-NEXT: namespace 'g' | 1 24 %struct.U = type { i32 } %struct.S = type { i32 } diff --git a/llvm/tools/llvm-pdbutil/CMakeLists.txt b/llvm/tools/llvm-pdbutil/CMakeLists.txt index 36ea5904530..ab4ce1ac30e 100644 --- a/llvm/tools/llvm-pdbutil/CMakeLists.txt +++ b/llvm/tools/llvm-pdbutil/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + BinaryFormat DebugInfoCodeView DebugInfoMSF DebugInfoPDB @@ -13,6 +14,7 @@ add_llvm_tool(llvm-pdbutil Diff.cpp DiffPrinter.cpp DumpOutputStyle.cpp + InputFile.cpp llvm-pdbutil.cpp FormatUtil.cpp LinePrinter.cpp diff --git a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp index bc3b5b94a5f..e25d53cb6a7 100644 --- a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp +++ b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp @@ -10,6 +10,7 @@ #include "DumpOutputStyle.h" #include "FormatUtil.h" +#include "InputFile.h" #include "MinimalSymbolDumper.h" #include "MinimalTypeDumper.h" #include "StreamUtil.h" @@ -67,9 +68,12 @@ using namespace llvm::codeview; using namespace llvm::msf; using namespace llvm::pdb; -DumpOutputStyle::DumpOutputStyle(PDBFile &File) +DumpOutputStyle::DumpOutputStyle(InputFile &File) : File(File), P(2, false, outs()) {} +PDBFile &DumpOutputStyle::getPdb() { return File.pdb(); } +object::COFFObjectFile &DumpOutputStyle::getObj() { return File.obj(); } + Error DumpOutputStyle::dump() { if (opts::dump::DumpSummary) { if (auto EC = dumpFileSummary()) @@ -83,13 +87,13 @@ Error DumpOutputStyle::dump() { P.NewLine(); } - if (opts::dump::DumpSymbolStats.getNumOccurrences() > 0) { + if (opts::dump::DumpSymbolStats) { if (auto EC = dumpSymbolStats()) return EC; P.NewLine(); } - if (opts::dump::DumpUdtStats.getNumOccurrences() > 0) { + if (opts::dump::DumpUdtStats) { if (auto EC = dumpUdtStats()) return EC; P.NewLine(); @@ -154,7 +158,8 @@ Error DumpOutputStyle::dump() { } if (opts::dump::DumpSymbols) { - if (auto EC = dumpModuleSyms()) + auto EC = File.isPdb() ? dumpModuleSymsForPdb() : dumpModuleSymsForObj(); + if (EC) return EC; } @@ -188,22 +193,27 @@ Error DumpOutputStyle::dumpFileSummary() { ExitOnError Err("Invalid PDB Format: "); AutoIndent Indent(P); - P.formatLine("Block Size: {0}", File.getBlockSize()); - P.formatLine("Number of blocks: {0}", File.getBlockCount()); - P.formatLine("Number of streams: {0}", File.getNumStreams()); + if (File.isObj()) { + P.formatLine("Dumping File summary is not valid for object files"); + return Error::success(); + } - auto &PS = Err(File.getPDBInfoStream()); + P.formatLine("Block Size: {0}", getPdb().getBlockSize()); + P.formatLine("Number of blocks: {0}", getPdb().getBlockCount()); + P.formatLine("Number of streams: {0}", getPdb().getNumStreams()); + + auto &PS = Err(getPdb().getPDBInfoStream()); P.formatLine("Signature: {0}", PS.getSignature()); P.formatLine("Age: {0}", PS.getAge()); P.formatLine("GUID: {0}", fmt_guid(PS.getGuid().Guid)); P.formatLine("Features: {0:x+}", static_cast<uint32_t>(PS.getFeatures())); - P.formatLine("Has Debug Info: {0}", File.hasPDBDbiStream()); - P.formatLine("Has Types: {0}", File.hasPDBTpiStream()); - P.formatLine("Has IDs: {0}", File.hasPDBIpiStream()); - P.formatLine("Has Globals: {0}", File.hasPDBGlobalsStream()); - P.formatLine("Has Publics: {0}", File.hasPDBPublicsStream()); - if (File.hasPDBDbiStream()) { - auto &DBI = Err(File.getPDBDbiStream()); + P.formatLine("Has Debug Info: {0}", getPdb().hasPDBDbiStream()); + P.formatLine("Has Types: {0}", getPdb().hasPDBTpiStream()); + P.formatLine("Has IDs: {0}", getPdb().hasPDBIpiStream()); + P.formatLine("Has Globals: {0}", getPdb().hasPDBGlobalsStream()); + P.formatLine("Has Publics: {0}", getPdb().hasPDBPublicsStream()); + if (getPdb().hasPDBDbiStream()) { + auto &DBI = Err(getPdb().getPDBDbiStream()); P.formatLine("Is incrementally linked: {0}", DBI.isIncrementallyLinked()); P.formatLine("Has conflicting types: {0}", DBI.hasCTypes()); P.formatLine("Is stripped: {0}", DBI.isStripped()); @@ -212,20 +222,38 @@ Error DumpOutputStyle::dumpFileSummary() { return Error::success(); } -static StatCollection getSymbolStats(ModuleDebugStreamRef MDS, +static StatCollection getSymbolStats(const SymbolGroup &SG, StatCollection &CumulativeStats) { StatCollection Stats; - for (const auto &S : MDS.symbols(nullptr)) { - Stats.update(S.kind(), S.length()); - CumulativeStats.update(S.kind(), S.length()); + if (SG.getFile().isPdb()) { + // For PDB files, all symbols are packed into one stream. + for (const auto &S : SG.getPdbModuleStream().symbols(nullptr)) { + Stats.update(S.kind(), S.length()); + CumulativeStats.update(S.kind(), S.length()); + } + return Stats; + } + + for (const auto &SS : SG.getDebugSubsections()) { + // For object files, all symbols are spread across multiple Symbol + // subsections of a given .debug$S section. + if (SS.kind() != DebugSubsectionKind::Symbols) + continue; + DebugSymbolsSubsectionRef Symbols; + BinaryStreamReader Reader(SS.getRecordData()); + cantFail(Symbols.initialize(Reader)); + for (const auto &S : Symbols) { + Stats.update(S.kind(), S.length()); + CumulativeStats.update(S.kind(), S.length()); + } } return Stats; } -static StatCollection getChunkStats(ModuleDebugStreamRef MDS, +static StatCollection getChunkStats(const SymbolGroup &SG, StatCollection &CumulativeStats) { StatCollection Stats; - for (const auto &Chunk : MDS.subsections()) { + for (const auto &Chunk : SG.getDebugSubsections()) { Stats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength()); CumulativeStats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength()); } @@ -256,8 +284,11 @@ static void printModuleDetailStats(LinePrinter &P, StringRef Label, } } -static bool isMyCode(const DbiModuleDescriptor &Desc) { - StringRef Name = Desc.getModuleName(); +static bool isMyCode(const SymbolGroup &Group) { + if (Group.getFile().isObj()) + return true; + + StringRef Name = Group.name(); if (Name.startswith("Import:")) return false; if (Name.endswith_lower(".dll")) @@ -271,8 +302,8 @@ static bool isMyCode(const DbiModuleDescriptor &Desc) { return true; } -static bool shouldDumpModule(uint32_t Modi, const DbiModuleDescriptor &Desc) { - if (opts::dump::JustMyCode && !isMyCode(Desc)) +static bool shouldDumpSymbolGroup(uint32_t Idx, const SymbolGroup &Group) { + if (opts::dump::JustMyCode && !isMyCode(Group)) return false; // If the arg was not specified on the command line, always dump all modules. @@ -280,29 +311,34 @@ static bool shouldDumpModule(uint32_t Modi, const DbiModuleDescriptor &Desc) { return true; // Otherwise, only dump if this is the same module specified. - return (opts::dump::DumpModi == Modi); + return (opts::dump::DumpModi == Idx); } Error DumpOutputStyle::dumpStreamSummary() { printHeader(P, "Streams"); + AutoIndent Indent(P); + if (File.isObj()) { + P.formatLine("Dumping streams is not valid for object files"); + return Error::success(); + } + if (StreamPurposes.empty()) - discoverStreamPurposes(File, StreamPurposes); + discoverStreamPurposes(getPdb(), StreamPurposes); - AutoIndent Indent(P); - uint32_t StreamCount = File.getNumStreams(); - uint32_t MaxStreamSize = File.getMaxStreamSize(); + uint32_t StreamCount = getPdb().getNumStreams(); + uint32_t MaxStreamSize = getPdb().getMaxStreamSize(); for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { P.formatLine( "Stream {0} ({1} bytes): [{2}]", fmt_align(StreamIdx, AlignStyle::Right, NumDigits(StreamCount)), - fmt_align(File.getStreamByteSize(StreamIdx), AlignStyle::Right, + fmt_align(getPdb().getStreamByteSize(StreamIdx), AlignStyle::Right, NumDigits(MaxStreamSize)), StreamPurposes[StreamIdx].getLongName()); if (opts::dump::DumpStreamBlocks) { - auto Blocks = File.getStreamBlockList(StreamIdx); + auto Blocks = getPdb().getStreamBlockList(StreamIdx); std::vector<uint32_t> BV(Blocks.begin(), Blocks.end()); P.formatLine(" {0} Blocks: [{1}]", fmt_repeat(' ', NumDigits(StreamCount)), @@ -326,9 +362,7 @@ static Expected<ModuleDebugStreamRef> getModuleDebugStream(PDBFile &File, return make_error<RawError>(raw_error_code::no_stream, "Module stream not present"); - auto ModStreamData = MappedBlockStream::createIndexedStream( - File.getMsfLayout(), File.getMsfBuffer(), ModiStream, - File.getAllocator()); + auto ModStreamData = File.createIndexedStream(ModiStream); ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData)); if (auto EC = ModS.reload()) @@ -338,198 +372,91 @@ static Expected<ModuleDebugStreamRef> getModuleDebugStream(PDBFile &File, return std::move(ModS); } -static std::string formatChecksumKind(FileChecksumKind Kind) { - switch (Kind) { - RETURN_CASE(FileChecksumKind, None, "None"); - RETURN_CASE(FileChecksumKind, MD5, "MD5"); - RETURN_CASE(FileChecksumKind, SHA1, "SHA-1"); - RETURN_CASE(FileChecksumKind, SHA256, "SHA-256"); - } - return formatUnknownEnum(Kind); -} - -namespace { -class StringsAndChecksumsPrinter { - const DebugStringTableSubsectionRef &extractStringTable(PDBFile &File) { - ExitOnError Err("Unexpected error processing modules: "); - return Err(File.getStringTable()).getStringTable(); - } - - template <typename... Args> - void formatInternal(LinePrinter &Printer, bool Append, - Args &&... args) const { - if (Append) - Printer.format(std::forward<Args>(args)...); - else - Printer.formatLine(std::forward<Args>(args)...); - } - -public: - StringsAndChecksumsPrinter(PDBFile &File, uint32_t Modi) - : Records(extractStringTable(File)) { - auto MDS = getModuleDebugStream(File, Modi); - if (!MDS) { - consumeError(MDS.takeError()); - return; - } - - DebugStream = llvm::make_unique<ModuleDebugStreamRef>(std::move(*MDS)); - Records.initialize(MDS->subsections()); - if (Records.hasChecksums()) { - for (const auto &Entry : Records.checksums()) { - auto S = Records.strings().getString(Entry.FileNameOffset); - if (!S) - continue; - ChecksumsByFile[*S] = Entry; - } - } - } - - Expected<StringRef> getNameFromStringTable(uint32_t Offset) const { - return Records.strings().getString(Offset); - } - - void formatFromFileName(LinePrinter &Printer, StringRef File, - bool Append = false) const { - auto FC = ChecksumsByFile.find(File); - if (FC == ChecksumsByFile.end()) { - formatInternal(Printer, Append, "- (no checksum) {0}", File); - return; - } - - formatInternal(Printer, Append, "- ({0}: {1}) {2}", - formatChecksumKind(FC->getValue().Kind), - toHex(FC->getValue().Checksum), File); - } - - void formatFromChecksumsOffset(LinePrinter &Printer, uint32_t Offset, - bool Append = false) const { - if (!Records.hasChecksums()) { - formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); - return; - } - - auto Iter = Records.checksums().getArray().at(Offset); - if (Iter == Records.checksums().getArray().end()) { - formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); - return; - } - - uint32_t FO = Iter->FileNameOffset; - auto ExpectedFile = getNameFromStringTable(FO); - if (!ExpectedFile) { - formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); - consumeError(ExpectedFile.takeError()); - return; - } - if (Iter->Kind == FileChecksumKind::None) { - formatInternal(Printer, Append, "{0} (no checksum)", *ExpectedFile); - } else { - formatInternal(Printer, Append, "{0} ({1}: {2})", *ExpectedFile, - formatChecksumKind(Iter->Kind), toHex(Iter->Checksum)); - } +template <typename CallbackT> +static void +iterateOneModule(InputFile &File, const Optional<PrintScope> &HeaderScope, + const SymbolGroup &SG, uint32_t Modi, CallbackT Callback) { + if (HeaderScope) { + HeaderScope->P.formatLine( + "Mod {0:4} | `{1}`: ", + fmt_align(Modi, AlignStyle::Right, HeaderScope->LabelWidth), SG.name()); } - std::unique_ptr<ModuleDebugStreamRef> DebugStream; - StringsAndChecksumsRef Records; - StringMap<FileChecksumEntry> ChecksumsByFile; -}; -} // namespace - -template <typename CallbackT> -static void iterateOneModule(PDBFile &File, LinePrinter &P, - const DbiModuleDescriptor &Descriptor, - uint32_t Modi, uint32_t IndentLevel, - uint32_t Digits, CallbackT Callback) { - P.formatLine( - "Mod {0:4} | `{1}`: ", fmt_align(Modi, AlignStyle::Right, Digits), - Descriptor.getModuleName()); - - StringsAndChecksumsPrinter Strings(File, Modi); - AutoIndent Indent2(P, IndentLevel); - Callback(Modi, Strings); + AutoIndent Indent(HeaderScope); + Callback(Modi, SG); } template <typename CallbackT> -static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel, - CallbackT Callback) { - AutoIndent Indent(P); - if (!File.hasPDBDbiStream()) { - P.formatLine("DBI Stream not present"); - return; - } +static void iterateSymbolGroups(InputFile &Input, + const Optional<PrintScope> &HeaderScope, + CallbackT Callback) { + AutoIndent Indent(HeaderScope); ExitOnError Err("Unexpected error processing modules: "); - auto &Stream = Err(File.getPDBDbiStream()); - - const DbiModuleList &Modules = Stream.modules(); - if (opts::dump::DumpModi.getNumOccurrences() > 0) { assert(opts::dump::DumpModi.getNumOccurrences() == 1); uint32_t Modi = opts::dump::DumpModi; - auto Descriptor = Modules.getModuleDescriptor(Modi); - iterateOneModule(File, P, Descriptor, Modi, IndentLevel, NumDigits(Modi), - Callback); + SymbolGroup SG(&Input, Modi); + iterateOneModule(Input, withLabelWidth(HeaderScope, NumDigits(Modi)), SG, + Modi, Callback); return; } - uint32_t Count = Modules.getModuleCount(); - uint32_t Digits = NumDigits(Count); - for (uint32_t I = 0; I < Count; ++I) { - auto Desc = Modules.getModuleDescriptor(I); - if (!shouldDumpModule(I, Desc)) - continue; - iterateOneModule(File, P, Desc, I, IndentLevel, Digits, Callback); + uint32_t I = 0; + + for (const auto &SG : Input.symbol_groups()) { + if (shouldDumpSymbolGroup(I, SG)) + iterateOneModule(Input, withLabelWidth(HeaderScope, NumDigits(I)), SG, I, + Callback); + + ++I; } } template <typename SubsectionT> static void iterateModuleSubsections( - PDBFile &File, LinePrinter &P, uint32_t IndentLevel, - llvm::function_ref<void(uint32_t, StringsAndChecksumsPrinter &, - SubsectionT &)> + InputFile &File, const Optional<PrintScope> &HeaderScope, + llvm::function_ref<void(uint32_t, const SymbolGroup &, SubsectionT &)> Callback) { - iterateModules( - File, P, IndentLevel, - [&File, &Callback](uint32_t Modi, StringsAndChecksumsPrinter &Strings) { - auto MDS = getModuleDebugStream(File, Modi); - if (!MDS) { - consumeError(MDS.takeError()); - return; - } - - for (const auto &SS : MDS->subsections()) { - SubsectionT Subsection; + iterateSymbolGroups(File, HeaderScope, + [&](uint32_t Modi, const SymbolGroup &SG) { + for (const auto &SS : SG.getDebugSubsections()) { + SubsectionT Subsection; - if (SS.kind() != Subsection.kind()) - continue; + if (SS.kind() != Subsection.kind()) + continue; - BinaryStreamReader Reader(SS.getRecordData()); - if (auto EC = Subsection.initialize(Reader)) - continue; - Callback(Modi, Strings, Subsection); - } - }); + BinaryStreamReader Reader(SS.getRecordData()); + if (auto EC = Subsection.initialize(Reader)) + continue; + Callback(Modi, SG, Subsection); + } + }); } Error DumpOutputStyle::dumpModules() { printHeader(P, "Modules"); - AutoIndent Indent(P); - if (!File.hasPDBDbiStream()) { + + if (File.isObj()) { + P.formatLine("Dumping modules is not supported for object files"); + return Error::success(); + } + + if (!getPdb().hasPDBDbiStream()) { P.formatLine("DBI Stream not present"); return Error::success(); } ExitOnError Err("Unexpected error processing modules: "); - auto &Stream = Err(File.getPDBDbiStream()); + auto &Stream = Err(getPdb().getPDBDbiStream()); const DbiModuleList &Modules = Stream.modules(); - iterateModules( - File, P, 11, [&](uint32_t Modi, StringsAndChecksumsPrinter &Strings) { + iterateSymbolGroups( + File, PrintScope{P, 11}, [&](uint32_t Modi, const SymbolGroup &Strings) { auto Desc = Modules.getModuleDescriptor(Modi); P.formatLine("Obj: `{0}`: ", Desc.getObjFileName()); P.formatLine("debug stream: {0}, # files: {1}, has ec info: {2}", @@ -549,18 +476,22 @@ Error DumpOutputStyle::dumpModules() { Error DumpOutputStyle::dumpModuleFiles() { printHeader(P, "Files"); + if (File.isObj()) { + P.formatLine("Dumping files is not valid for object files"); + return Error::success(); + } + ExitOnError Err("Unexpected error processing modules: "); - iterateModules( - File, P, 11, - [this, &Err](uint32_t Modi, StringsAndChecksumsPrinter &Strings) { - auto &Stream = Err(File.getPDBDbiStream()); + iterateSymbolGroups(File, PrintScope{P, 11}, + [this, &Err](uint32_t Modi, const SymbolGroup &Strings) { + auto &Stream = Err(getPdb().getPDBDbiStream()); - const DbiModuleList &Modules = Stream.modules(); - for (const auto &F : Modules.source_files(Modi)) { - Strings.formatFromFileName(P, F); - } - }); + const DbiModuleList &Modules = Stream.modules(); + for (const auto &F : Modules.source_files(Modi)) { + Strings.formatFromFileName(P, F); + } + }); return Error::success(); } @@ -571,37 +502,30 @@ Error DumpOutputStyle::dumpSymbolStats() { StatCollection SymStats; StatCollection ChunkStats; - auto &Stream = Err(File.getPDBDbiStream()); - - const DbiModuleList &Modules = Stream.modules(); - uint32_t ModCount = Modules.getModuleCount(); - - iterateModules(File, P, 0, [&](uint32_t Modi, - StringsAndChecksumsPrinter &Strings) { - DbiModuleDescriptor Desc = Modules.getModuleDescriptor(Modi); - uint32_t StreamIdx = Desc.getModuleStreamIndex(); - if (StreamIdx == kInvalidStreamIndex) { - P.formatLine("Mod {0} (debug info not present): [{1}]", - fmt_align(Modi, AlignStyle::Right, NumDigits(ModCount)), - Desc.getModuleName()); - return; - } + iterateSymbolGroups(File, None, [&](uint32_t Modi, const SymbolGroup &SG) { + StatCollection SS = getSymbolStats(SG, SymStats); + StatCollection CS = getChunkStats(SG, ChunkStats); + + if (SG.getFile().isPdb()) { + auto Modules = cantFail(File.pdb().getPDBDbiStream()).modules(); + uint32_t ModCount = Modules.getModuleCount(); + DbiModuleDescriptor Desc = Modules.getModuleDescriptor(Modi); + uint32_t StreamIdx = Desc.getModuleStreamIndex(); + + if (StreamIdx == kInvalidStreamIndex) { + P.formatLine("Mod {0} (debug info not present): [{1}]", + fmt_align(Modi, AlignStyle::Right, NumDigits(ModCount)), + Desc.getModuleName()); + return; + } - P.formatLine("Stream {0}, {1} bytes", StreamIdx, - File.getStreamByteSize(StreamIdx)); + P.formatLine("Stream {0}, {1} bytes", StreamIdx, + getPdb().getStreamByteSize(StreamIdx)); - ModuleDebugStreamRef MDS(Desc, File.createIndexedStream(StreamIdx)); - if (auto EC = MDS.reload()) { - P.printLine("- Error parsing debug info stream"); - consumeError(std::move(EC)); - return; + printModuleDetailStats<SymbolKind>(P, "Symbols", SS); + printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", CS); } - - printModuleDetailStats<SymbolKind>(P, "Symbols", - getSymbolStats(MDS, SymStats)); - printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", - getChunkStats(MDS, ChunkStats)); }); P.printLine(" Summary |"); @@ -661,58 +585,75 @@ Error DumpOutputStyle::dumpUdtStats() { StatCollection UdtStats; StatCollection UdtTargetStats; - if (!File.hasPDBGlobalsStream()) { - P.printLine("- Error: globals stream not present"); - return Error::success(); - } - AutoIndent Indent(P, 4); - auto &SymbolRecords = cantFail(File.getPDBSymbolStream()); - auto &Globals = cantFail(File.getPDBGlobalsStream()); - auto &TpiTypes = cantFail(initializeTypes(StreamTPI)); + auto &TpiTypes = File.types(); StringMap<StatCollection::Stat> NamespacedStats; - P.NewLine(); - size_t LongestNamespace = 0; - for (uint32_t PubSymOff : Globals.getGlobalsTable()) { - CVSymbol Sym = SymbolRecords.readRecord(PubSymOff); + auto HandleOneSymbol = [&](const CVSymbol &Sym) { if (Sym.kind() != SymbolKind::S_UDT) - continue; + return; UdtStats.update(SymbolKind::S_UDT, Sym.length()); UDTSym UDT = cantFail(SymbolDeserializer::deserializeAs<UDTSym>(Sym)); uint32_t Kind = 0; uint32_t RecordSize = 0; - if (UDT.Type.isSimple() || - (UDT.Type.toArrayIndex() >= TpiTypes.capacity())) { - if (UDT.Type.isNoneType()) - Kind = kNoneUdtKind; - else if (UDT.Type.isSimple()) - Kind = kSimpleUdtKind; - else - Kind = kUnknownUdtKind; - } else { - CVType T = TpiTypes.getType(UDT.Type); - Kind = T.kind(); - RecordSize = T.length(); - } + + if (UDT.Type.isNoneType()) + Kind = kNoneUdtKind; + else if (UDT.Type.isSimple()) + Kind = kSimpleUdtKind; + else if (Optional<CVType> T = TpiTypes.tryGetType(UDT.Type)) { + Kind = T->kind(); + RecordSize = T->length(); + } else + Kind = kUnknownUdtKind; UdtTargetStats.update(Kind, RecordSize); size_t Pos = UDT.Name.find("::"); if (Pos == StringRef::npos) - continue; + return; StringRef Scope = UDT.Name.take_front(Pos); if (Scope.empty() || !isValidNamespaceIdentifier(Scope)) - continue; + return; LongestNamespace = std::max(LongestNamespace, Scope.size()); NamespacedStats[Scope].update(RecordSize); + }; + + P.NewLine(); + + if (File.isPdb()) { + if (!getPdb().hasPDBGlobalsStream()) { + P.printLine("- Error: globals stream not present"); + return Error::success(); + } + + auto &SymbolRecords = cantFail(getPdb().getPDBSymbolStream()); + auto &Globals = cantFail(getPdb().getPDBGlobalsStream()); + + for (uint32_t PubSymOff : Globals.getGlobalsTable()) { + CVSymbol Sym = SymbolRecords.readRecord(PubSymOff); + HandleOneSymbol(Sym); + } + } else { + for (const auto &Sec : File.symbol_groups()) { + for (const auto &SS : Sec.getDebugSubsections()) { + if (SS.kind() != DebugSubsectionKind::Symbols) + continue; + + DebugSymbolsSubsectionRef Symbols; + BinaryStreamReader Reader(SS.getRecordData()); + cantFail(Symbols.initialize(Reader)); + for (const auto &S : Symbols) + HandleOneSymbol(S); + } + } } LongestNamespace += StringRef(" namespace ''").size(); @@ -762,8 +703,8 @@ Error DumpOutputStyle::dumpUdtStats() { return Error::success(); } -static void typesetLinesAndColumns(PDBFile &File, LinePrinter &P, - uint32_t Start, const LineColumnEntry &E) { +static void typesetLinesAndColumns(LinePrinter &P, uint32_t Start, + const LineColumnEntry &E) { const uint32_t kMaxCharsPerLineNumber = 4; // 4 digit line number uint32_t MinColumnWidth = kMaxCharsPerLineNumber + 5; @@ -802,9 +743,9 @@ Error DumpOutputStyle::dumpLines() { uint32_t LastModi = UINT32_MAX; uint32_t LastNameIndex = UINT32_MAX; iterateModuleSubsections<DebugLinesSubsectionRef>( - File, P, 4, + File, PrintScope{P, 4}, [this, &LastModi, &LastNameIndex](uint32_t Modi, - StringsAndChecksumsPrinter &Strings, + const SymbolGroup &Strings, DebugLinesSubsectionRef &Lines) { uint16_t Segment = Lines.header()->RelocSegment; uint32_t Begin = Lines.header()->RelocOffset; @@ -825,7 +766,7 @@ Error DumpOutputStyle::dumpLines() { P.format("line/addr entries = {0}", Count); P.NewLine(); - typesetLinesAndColumns(File, P, Begin, Block); + typesetLinesAndColumns(P, Begin, Block); } }); @@ -836,8 +777,8 @@ Error DumpOutputStyle::dumpInlineeLines() { printHeader(P, "Inlinee Lines"); iterateModuleSubsections<DebugInlineeLinesSubsectionRef>( - File, P, 2, - [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings, + File, PrintScope{P, 2}, + [this](uint32_t Modi, const SymbolGroup &Strings, DebugInlineeLinesSubsectionRef &Lines) { P.formatLine("{0,+8} | {1,+5} | {2}", "Inlinee", "Line", "Source File"); for (const auto &Entry : Lines) { @@ -854,8 +795,8 @@ Error DumpOutputStyle::dumpInlineeLines() { Error DumpOutputStyle::dumpXmi() { printHeader(P, "Cross Module Imports"); iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>( - File, P, 2, - [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings, + File, PrintScope{P, 2}, + [this](uint32_t Modi, const SymbolGroup &Strings, DebugCrossModuleImportsSubsectionRef &Imports) { P.formatLine("{0,=32} | {1}", "Imported Module", "Type IDs"); @@ -890,8 +831,8 @@ Error DumpOutputStyle::dumpXme() { printHeader(P, "Cross Module Exports"); iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>( - File, P, 2, - [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings, + File, PrintScope{P, 2}, + [this](uint32_t Modi, const SymbolGroup &Strings, DebugCrossModuleExportsSubsectionRef &Exports) { P.formatLine("{0,-10} | {1}", "Local ID", "Global ID"); for (const auto &Export : Exports) { @@ -906,8 +847,13 @@ Error DumpOutputStyle::dumpXme() { Error DumpOutputStyle::dumpStringTable() { printHeader(P, "String Table"); + if (File.isObj()) { + P.formatLine("Dumping string table is not supported for object files"); + return Error::success(); + } + AutoIndent Indent(P); - auto IS = File.getStringTable(); + auto IS = getPdb().getStringTable(); if (!IS) { P.formatLine("Not present in file"); consumeError(IS.takeError()); @@ -1018,22 +964,32 @@ static void dumpPartialTypeStream(LinePrinter &Printer, Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI); + if (StreamIdx == StreamTPI) { + printHeader(P, "Types (TPI Stream)"); + } else if (StreamIdx == StreamIPI) { + printHeader(P, "Types (IPI Stream)"); + } + + AutoIndent Indent(P); + if (File.isObj()) { + P.formatLine("Dumping types is not supported for object files"); + return Error::success(); + } + bool Present = false; bool DumpTypes = false; bool DumpBytes = false; bool DumpExtras = false; std::vector<uint32_t> Indices; if (StreamIdx == StreamTPI) { - printHeader(P, "Types (TPI Stream)"); - Present = File.hasPDBTpiStream(); + Present = getPdb().hasPDBTpiStream(); DumpTypes = opts::dump::DumpTypes; DumpBytes = opts::dump::DumpTypeData; DumpExtras = opts::dump::DumpTypeExtras; Indices.assign(opts::dump::DumpTypeIndex.begin(), opts::dump::DumpTypeIndex.end()); } else if (StreamIdx == StreamIPI) { - printHeader(P, "Types (IPI Stream)"); - Present = File.hasPDBIpiStream(); + Present = getPdb().hasPDBIpiStream(); DumpTypes = opts::dump::DumpIds; DumpBytes = opts::dump::DumpIdData; DumpExtras = opts::dump::DumpIdExtras; @@ -1041,7 +997,6 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { opts::dump::DumpIdIndex.end()); } - AutoIndent Indent(P); if (!Present) { P.formatLine("Stream not present"); return Error::success(); @@ -1049,10 +1004,10 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { ExitOnError Err("Unexpected error processing types: "); - auto &Stream = Err((StreamIdx == StreamTPI) ? File.getPDBTpiStream() - : File.getPDBIpiStream()); + auto &Stream = Err((StreamIdx == StreamTPI) ? getPdb().getPDBTpiStream() + : getPdb().getPDBIpiStream()); - auto &Types = Err(initializeTypes(StreamIdx)); + auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids(); if (DumpTypes || !Indices.empty()) { if (Indices.empty()) @@ -1076,7 +1031,7 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { P.NewLine(); P.formatLine("Hash Adjusters:"); auto &Adjusters = Stream.getHashAdjusters(); - auto &Strings = Err(File.getStringTable()); + auto &Strings = Err(getPdb().getStringTable()); for (const auto &A : Adjusters) { AutoIndent Indent2(P); auto ExpectedStr = Strings.getStringForID(A.first); @@ -1092,42 +1047,60 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { return Error::success(); } -Expected<codeview::LazyRandomTypeCollection &> -DumpOutputStyle::initializeTypes(uint32_t SN) { - auto &TypeCollection = (SN == StreamTPI) ? TpiTypes : IpiTypes; - auto Tpi = - (SN == StreamTPI) ? File.getPDBTpiStream() : File.getPDBIpiStream(); - if (!Tpi) - return Tpi.takeError(); +Error DumpOutputStyle::dumpModuleSymsForObj() { + printHeader(P, "Symbols"); - if (!TypeCollection) { - auto &Types = Tpi->typeArray(); - uint32_t Count = Tpi->getNumTypeRecords(); - auto Offsets = Tpi->getTypeIndexOffsets(); - TypeCollection = - llvm::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets); - } + AutoIndent Indent(P); + + ExitOnError Err("Unexpected error processing symbols: "); - return *TypeCollection; + auto &Types = File.types(); + + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(nullptr, CodeViewContainer::ObjectFile); + MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types, Types); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Dumper); + CVSymbolVisitor Visitor(Pipeline); + + std::unique_ptr<llvm::Error> SymbolError; + + iterateModuleSubsections<DebugSymbolsSubsectionRef>( + File, PrintScope{P, 2}, + [&](uint32_t Modi, const SymbolGroup &Strings, + DebugSymbolsSubsectionRef &Symbols) { + for (auto Symbol : Symbols) { + if (auto EC = Visitor.visitSymbolRecord(Symbol)) { + SymbolError = llvm::make_unique<Error>(std::move(EC)); + return; + } + } + }); + + if (SymbolError) + return std::move(*SymbolError); + + return Error::success(); } -Error DumpOutputStyle::dumpModuleSyms() { +Error DumpOutputStyle::dumpModuleSymsForPdb() { printHeader(P, "Symbols"); AutoIndent Indent(P); - if (!File.hasPDBDbiStream()) { + if (!getPdb().hasPDBDbiStream()) { P.formatLine("DBI Stream not present"); return Error::success(); } ExitOnError Err("Unexpected error processing symbols: "); - auto &Ids = Err(initializeTypes(StreamIPI)); - auto &Types = Err(initializeTypes(StreamTPI)); + auto &Ids = File.ids(); + auto &Types = File.types(); - iterateModules( - File, P, 2, [&](uint32_t I, StringsAndChecksumsPrinter &Strings) { - auto ExpectedModS = getModuleDebugStream(File, I); + iterateSymbolGroups( + File, PrintScope{P, 2}, [&](uint32_t I, const SymbolGroup &Strings) { + auto ExpectedModS = getModuleDebugStream(File.pdb(), I); if (!ExpectedModS) { P.formatLine("Error loading module stream {0}. {1}", I, toString(ExpectedModS.takeError())); @@ -1158,12 +1131,18 @@ Error DumpOutputStyle::dumpModuleSyms() { Error DumpOutputStyle::dumpGlobals() { printHeader(P, "Global Symbols"); AutoIndent Indent(P); - if (!File.hasPDBGlobalsStream()) { + + if (File.isObj()) { + P.formatLine("Dumping Globals is not supported for object files"); + return Error::success(); + } + + if (!getPdb().hasPDBGlobalsStream()) { P.formatLine("Globals stream not present"); return Error::success(); } ExitOnError Err("Error dumping globals stream: "); - auto &Globals = Err(File.getPDBGlobalsStream()); + auto &Globals = Err(getPdb().getPDBGlobalsStream()); const GSIHashTable &Table = Globals.getGlobalsTable(); Err(dumpSymbolsFromGSI(Table, opts::dump::DumpGlobalExtras)); @@ -1173,12 +1152,18 @@ Error DumpOutputStyle::dumpGlobals() { Error DumpOutputStyle::dumpPublics() { printHeader(P, "Public Symbols"); AutoIndent Indent(P); - if (!File.hasPDBPublicsStream()) { + + if (File.isObj()) { + P.formatLine("Dumping Globals is not supported for object files"); + return Error::success(); + } + + if (!getPdb().hasPDBPublicsStream()) { P.formatLine("Publics stream not present"); return Error::success(); } ExitOnError Err("Error dumping publics stream: "); - auto &Publics = Err(File.getPDBPublicsStream()); + auto &Publics = Err(getPdb().getPDBPublicsStream()); const GSIHashTable &PublicsTable = Publics.getPublicsTable(); if (opts::dump::DumpPublicExtras) { @@ -1224,15 +1209,11 @@ Error DumpOutputStyle::dumpPublics() { Error DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable &Table, bool HashExtras) { - auto ExpectedSyms = File.getPDBSymbolStream(); + auto ExpectedSyms = getPdb().getPDBSymbolStream(); if (!ExpectedSyms) return ExpectedSyms.takeError(); - auto ExpectedTypes = initializeTypes(StreamTPI); - if (!ExpectedTypes) - return ExpectedTypes.takeError(); - auto ExpectedIds = initializeTypes(StreamIPI); - if (!ExpectedIds) - return ExpectedIds.takeError(); + auto &Types = File.types(); + auto &Ids = File.ids(); if (HashExtras) { P.printLine("GSI Header"); @@ -1246,8 +1227,7 @@ Error DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable &Table, P.printLine("Records"); SymbolVisitorCallbackPipeline Pipeline; SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); - MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, *ExpectedIds, - *ExpectedTypes); + MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types); Pipeline.addCallbackToPipeline(Deserializer); Pipeline.addCallbackToPipeline(Dumper); @@ -1326,8 +1306,7 @@ loadSectionHeaders(PDBFile &File, DbgHeaderType Type) { "PDB does not contain the requested image section header type", inconvertibleErrorCode()); - auto Stream = MappedBlockStream::createIndexedStream( - File.getMsfLayout(), File.getMsfBuffer(), SI, File.getAllocator()); + auto Stream = File.createIndexedStream(SI); if (!Stream) return make_error<StringError>("Could not load the required stream data", inconvertibleErrorCode()); @@ -1346,12 +1325,17 @@ loadSectionHeaders(PDBFile &File, DbgHeaderType Type) { void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) { printHeader(P, Label); - ExitOnError Err("Error dumping publics stream: "); AutoIndent Indent(P); + if (File.isObj()) { + P.formatLine("Dumping Section Headers is not supported for object files"); + return; + } + + ExitOnError Err("Error dumping section headers: "); std::unique_ptr<MappedBlockStream> Stream; ArrayRef<object::coff_section> Headers; - auto ExpectedHeaders = loadSectionHeaders(File, Type); + auto ExpectedHeaders = loadSectionHeaders(getPdb(), Type); if (!ExpectedHeaders) { P.printLine(toString(ExpectedHeaders.takeError())); return; @@ -1401,16 +1385,22 @@ std::vector<std::string> getSectionNames(PDBFile &File) { Error DumpOutputStyle::dumpSectionContribs() { printHeader(P, "Section Contributions"); - ExitOnError Err("Error dumping publics stream: "); AutoIndent Indent(P); - if (!File.hasPDBDbiStream()) { + if (File.isObj()) { + P.formatLine( + "Dumping section contributions is not supported for object files"); + return Error::success(); + } + + ExitOnError Err("Error dumping section contributions: "); + if (!getPdb().hasPDBDbiStream()) { P.formatLine( "Section contribs require a DBI Stream, which could not be loaded"); return Error::success(); } - auto &Dbi = Err(File.getPDBDbiStream()); + auto &Dbi = Err(getPdb().getPDBDbiStream()); class Visitor : public ISectionContribVisitor { public: @@ -1456,7 +1446,7 @@ Error DumpOutputStyle::dumpSectionContribs() { ArrayRef<std::string> Names; }; - std::vector<std::string> Names = getSectionNames(File); + std::vector<std::string> Names = getSectionNames(getPdb()); Visitor V(P, makeArrayRef(Names)); Dbi.visitSectionContributions(V); return Error::success(); @@ -1464,16 +1454,22 @@ Error DumpOutputStyle::dumpSectionContribs() { Error DumpOutputStyle::dumpSectionMap() { printHeader(P, "Section Map"); + AutoIndent Indent(P); + + if (File.isObj()) { + P.formatLine("Dumping section map is not supported for object files"); + return Error::success(); + } + ExitOnError Err("Error dumping section map: "); - AutoIndent Indent(P); - if (!File.hasPDBDbiStream()) { + if (!getPdb().hasPDBDbiStream()) { P.formatLine("Dumping the section map requires a DBI Stream, which could " "not be loaded"); return Error::success(); } - auto &Dbi = Err(File.getPDBDbiStream()); + auto &Dbi = Err(getPdb().getPDBDbiStream()); uint32_t I = 0; for (auto &M : Dbi.getSectionMap()) { diff --git a/llvm/tools/llvm-pdbutil/DumpOutputStyle.h b/llvm/tools/llvm-pdbutil/DumpOutputStyle.h index 7dd717c3788..85598307863 100644 --- a/llvm/tools/llvm-pdbutil/DumpOutputStyle.h +++ b/llvm/tools/llvm-pdbutil/DumpOutputStyle.h @@ -28,8 +28,13 @@ namespace codeview { class LazyRandomTypeCollection; } +namespace object { +class COFFObjectFile; +} + namespace pdb { class GSIHashTable; +class InputFile; struct StatCollection { struct Stat { @@ -57,12 +62,13 @@ struct StatCollection { class DumpOutputStyle : public OutputStyle { public: - DumpOutputStyle(PDBFile &File); + DumpOutputStyle(InputFile &File); Error dump() override; private: - Expected<codeview::LazyRandomTypeCollection &> initializeTypes(uint32_t SN); + PDBFile &getPdb(); + object::COFFObjectFile &getObj(); Error dumpFileSummary(); Error dumpStreamSummary(); @@ -76,7 +82,8 @@ private: Error dumpTpiStream(uint32_t StreamIdx); Error dumpModules(); Error dumpModuleFiles(); - Error dumpModuleSyms(); + Error dumpModuleSymsForPdb(); + Error dumpModuleSymsForObj(); Error dumpGlobals(); Error dumpPublics(); Error dumpSymbolsFromGSI(const GSIHashTable &Table, bool HashExtras); @@ -86,10 +93,8 @@ private: void dumpSectionHeaders(StringRef Label, DbgHeaderType Type); - PDBFile &File; + InputFile &File; LinePrinter P; - std::unique_ptr<codeview::LazyRandomTypeCollection> TpiTypes; - std::unique_ptr<codeview::LazyRandomTypeCollection> IpiTypes; SmallVector<StreamInfo, 32> StreamPurposes; }; } // namespace pdb diff --git a/llvm/tools/llvm-pdbutil/InputFile.cpp b/llvm/tools/llvm-pdbutil/InputFile.cpp new file mode 100644 index 00000000000..f080a4cb284 --- /dev/null +++ b/llvm/tools/llvm-pdbutil/InputFile.cpp @@ -0,0 +1,469 @@ +//===- InputFile.cpp ------------------------------------------ *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "InputFile.h" + +#include "FormatUtil.h" +#include "LinePrinter.h" + +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::object; +using namespace llvm::pdb; + +InputFile::InputFile() {} +InputFile::~InputFile() {} + +static Expected<ModuleDebugStreamRef> +getModuleDebugStream(PDBFile &File, StringRef &ModuleName, uint32_t Index) { + ExitOnError Err("Unexpected error: "); + + auto &Dbi = Err(File.getPDBDbiStream()); + const auto &Modules = Dbi.modules(); + auto Modi = Modules.getModuleDescriptor(Index); + + ModuleName = Modi.getModuleName(); + + uint16_t ModiStream = Modi.getModuleStreamIndex(); + if (ModiStream == kInvalidStreamIndex) + return make_error<RawError>(raw_error_code::no_stream, + "Module stream not present"); + + auto ModStreamData = File.createIndexedStream(ModiStream); + + ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData)); + if (auto EC = ModS.reload()) + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid module stream"); + + return std::move(ModS); +} + +static inline bool isCodeViewDebugSubsection(object::SectionRef Section, + StringRef Name, + BinaryStreamReader &Reader) { + StringRef SectionName, Contents; + if (auto EC = Section.getName(SectionName)) + return false; + + if (SectionName != Name) + return false; + + if (auto EC = Section.getContents(Contents)) + return false; + + Reader = BinaryStreamReader(Contents, support::little); + uint32_t Magic; + if (Reader.bytesRemaining() < sizeof(uint32_t)) + return false; + cantFail(Reader.readInteger(Magic)); + if (Magic != COFF::DEBUG_SECTION_MAGIC) + return false; + return true; +} + +static inline bool isDebugSSection(object::SectionRef Section, + DebugSubsectionArray &Subsections) { + BinaryStreamReader Reader; + if (!isCodeViewDebugSubsection(Section, ".debug$S", Reader)) + return false; + + cantFail(Reader.readArray(Subsections, Reader.bytesRemaining())); + return true; +} + +static bool isDebugTSection(SectionRef Section, CVTypeArray &Types) { + BinaryStreamReader Reader; + if (!isCodeViewDebugSubsection(Section, ".debug$T", Reader)) + return false; + cantFail(Reader.readArray(Types, Reader.bytesRemaining())); + return true; +} + +static std::string formatChecksumKind(FileChecksumKind Kind) { + switch (Kind) { + RETURN_CASE(FileChecksumKind, None, "None"); + RETURN_CASE(FileChecksumKind, MD5, "MD5"); + RETURN_CASE(FileChecksumKind, SHA1, "SHA-1"); + RETURN_CASE(FileChecksumKind, SHA256, "SHA-256"); + } + return formatUnknownEnum(Kind); +} + +static const DebugStringTableSubsectionRef &extractStringTable(PDBFile &File) { + return cantFail(File.getStringTable()).getStringTable(); +} + +template <typename... Args> +static void formatInternal(LinePrinter &Printer, bool Append, Args &&... args) { + if (Append) + Printer.format(std::forward<Args>(args)...); + else + Printer.formatLine(std::forward<Args>(args)...); +} + +SymbolGroup::SymbolGroup(InputFile *File, uint32_t GroupIndex) : File(File) { + if (!File) + return; + + if (File->isPdb()) + initializeForPdb(GroupIndex); + else { + Name = ".debug$S"; + uint32_t I = 0; + for (const auto &S : File->obj().sections()) { + DebugSubsectionArray SS; + if (!isDebugSSection(S, SS)) + continue; + + if (!SC.hasChecksums() || !SC.hasStrings()) + SC.initialize(SS); + + if (I == GroupIndex) + Subsections = SS; + + if (SC.hasChecksums() && SC.hasStrings()) + break; + } + rebuildChecksumMap(); + } +} + +StringRef SymbolGroup::name() const { return Name; } + +void SymbolGroup::updateDebugS(const codeview::DebugSubsectionArray &SS) { + Subsections = SS; +} + +void SymbolGroup::updatePdbModi(uint32_t Modi) { initializeForPdb(Modi); } + +void SymbolGroup::initializeForPdb(uint32_t Modi) { + assert(File && File->isPdb()); + + // PDB always uses the same string table, but each module has its own + // checksums. So we only set the strings if they're not already set. + if (!SC.hasStrings()) + SC.setStrings(extractStringTable(File->pdb())); + + SC.resetChecksums(); + auto MDS = getModuleDebugStream(File->pdb(), Name, Modi); + if (!MDS) { + consumeError(MDS.takeError()); + return; + } + + DebugStream = std::make_shared<ModuleDebugStreamRef>(std::move(*MDS)); + Subsections = DebugStream->getSubsectionsArray(); + SC.initialize(Subsections); + rebuildChecksumMap(); +} + +void SymbolGroup::rebuildChecksumMap() { + if (!SC.hasChecksums()) + return; + + for (const auto &Entry : SC.checksums()) { + auto S = SC.strings().getString(Entry.FileNameOffset); + if (!S) + continue; + ChecksumsByFile[*S] = Entry; + } +} + +const ModuleDebugStreamRef &SymbolGroup::getPdbModuleStream() const { + assert(File && File->isPdb() && DebugStream); + return *DebugStream; +} + +Expected<StringRef> SymbolGroup::getNameFromStringTable(uint32_t Offset) const { + return SC.strings().getString(Offset); +} + +void SymbolGroup::formatFromFileName(LinePrinter &Printer, StringRef File, + bool Append) const { + auto FC = ChecksumsByFile.find(File); + if (FC == ChecksumsByFile.end()) { + formatInternal(Printer, Append, "- (no checksum) {0}", File); + return; + } + + formatInternal(Printer, Append, "- ({0}: {1}) {2}", + formatChecksumKind(FC->getValue().Kind), + toHex(FC->getValue().Checksum), File); +} + +void SymbolGroup::formatFromChecksumsOffset(LinePrinter &Printer, + uint32_t Offset, + bool Append) const { + if (!SC.hasChecksums()) { + formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); + return; + } + + auto Iter = SC.checksums().getArray().at(Offset); + if (Iter == SC.checksums().getArray().end()) { + formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); + return; + } + + uint32_t FO = Iter->FileNameOffset; + auto ExpectedFile = getNameFromStringTable(FO); + if (!ExpectedFile) { + formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); + consumeError(ExpectedFile.takeError()); + return; + } + if (Iter->Kind == FileChecksumKind::None) { + formatInternal(Printer, Append, "{0} (no checksum)", *ExpectedFile); + } else { + formatInternal(Printer, Append, "{0} ({1}: {2})", *ExpectedFile, + formatChecksumKind(Iter->Kind), toHex(Iter->Checksum)); + } +} + +Expected<InputFile> InputFile::open(StringRef Path) { + InputFile IF; + if (!llvm::sys::fs::exists(Path)) + return make_error<StringError>(formatv("File {0} not found", Path), + inconvertibleErrorCode()); + + file_magic Magic; + if (auto EC = identify_magic(Path, Magic)) + return make_error<StringError>( + formatv("Unable to identify file type for file {0}", Path), EC); + + if (Magic == file_magic::coff_object) { + Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(Path); + if (!BinaryOrErr) + return BinaryOrErr.takeError(); + + IF.CoffObject = std::move(*BinaryOrErr); + IF.PdbOrObj = llvm::cast<COFFObjectFile>(IF.CoffObject.getBinary()); + return std::move(IF); + } + + if (Magic == file_magic::unknown) { + std::unique_ptr<IPDBSession> Session; + if (auto Err = loadDataForPDB(PDB_ReaderType::Native, Path, Session)) + return std::move(Err); + + IF.PdbSession.reset(static_cast<NativeSession *>(Session.release())); + IF.PdbOrObj = &IF.PdbSession->getPDBFile(); + + return std::move(IF); + } + + return make_error<StringError>( + formatv("File {0} is not a supported file type", Path), + inconvertibleErrorCode()); +} + +PDBFile &InputFile::pdb() { + assert(isPdb()); + return *PdbOrObj.get<PDBFile *>(); +} + +const PDBFile &InputFile::pdb() const { + assert(isPdb()); + return *PdbOrObj.get<PDBFile *>(); +} + +object::COFFObjectFile &InputFile::obj() { + assert(isObj()); + return *PdbOrObj.get<object::COFFObjectFile *>(); +} + +const object::COFFObjectFile &InputFile::obj() const { + assert(isObj()); + return *PdbOrObj.get<object::COFFObjectFile *>(); +} + +bool InputFile::hasTypes() const { + if (isPdb()) + return pdb().hasPDBTpiStream(); + + for (const auto &Section : obj().sections()) { + CVTypeArray Types; + if (isDebugTSection(Section, Types)) + return true; + } + return false; +} + +bool InputFile::hasIds() const { + if (isObj()) + return false; + return pdb().hasPDBIpiStream(); +} + +bool InputFile::isPdb() const { return PdbOrObj.is<PDBFile *>(); } + +bool InputFile::isObj() const { + return PdbOrObj.is<object::COFFObjectFile *>(); +} + +codeview::LazyRandomTypeCollection & +InputFile::getOrCreateTypeCollection(TypeCollectionKind Kind) { + if (Types && Kind == kTypes) + return *Types; + if (Ids && Kind == kIds) + return *Ids; + + if (Kind == kIds) { + assert(isPdb() && pdb().hasPDBIpiStream()); + } + + // If the collection was already initialized, we should have just returned it + // in step 1. + if (isPdb()) { + TypeCollectionPtr &Collection = (Kind == kIds) ? Ids : Types; + auto &Stream = cantFail((Kind == kIds) ? pdb().getPDBIpiStream() + : pdb().getPDBTpiStream()); + + auto &Array = Stream.typeArray(); + uint32_t Count = Stream.getNumTypeRecords(); + auto Offsets = Stream.getTypeIndexOffsets(); + Collection = + llvm::make_unique<LazyRandomTypeCollection>(Array, Count, Offsets); + return *Collection; + } + + assert(isObj()); + assert(Kind == kTypes); + assert(!Types); + + for (const auto &Section : obj().sections()) { + CVTypeArray Records; + if (!isDebugTSection(Section, Records)) + continue; + + Types = llvm::make_unique<LazyRandomTypeCollection>(Records, 100); + return *Types; + } + + Types = llvm::make_unique<LazyRandomTypeCollection>(100); + return *Types; +} + +codeview::LazyRandomTypeCollection &InputFile::types() { + return getOrCreateTypeCollection(kTypes); +} + +codeview::LazyRandomTypeCollection &InputFile::ids() { + // Object files have only one type stream that contains both types and ids. + // Similarly, some PDBs don't contain an IPI stream, and for those both types + // and IDs are in the same stream. + if (isObj() || !pdb().hasPDBIpiStream()) + return types(); + + return getOrCreateTypeCollection(kIds); +} + +iterator_range<SymbolGroupIterator> InputFile::symbol_groups() { + return make_range<SymbolGroupIterator>(symbol_groups_begin(), + symbol_groups_end()); +} + +SymbolGroupIterator InputFile::symbol_groups_begin() { + return SymbolGroupIterator(*this); +} + +SymbolGroupIterator InputFile::symbol_groups_end() { + return SymbolGroupIterator(); +} + +SymbolGroupIterator::SymbolGroupIterator() : Value(nullptr) {} + +SymbolGroupIterator::SymbolGroupIterator(InputFile &File) : Value(&File) { + if (File.isObj()) { + SectionIter = File.obj().section_begin(); + scanToNextDebugS(); + } +} + +bool SymbolGroupIterator::operator==(const SymbolGroupIterator &R) const { + bool E = isEnd(); + bool RE = R.isEnd(); + if (E || RE) + return E == RE; + + if (Value.File != R.Value.File) + return false; + return Index == R.Index; +} + +const SymbolGroup &SymbolGroupIterator::operator*() const { + assert(!isEnd()); + return Value; +} +SymbolGroup &SymbolGroupIterator::operator*() { + assert(!isEnd()); + return Value; +} + +SymbolGroupIterator &SymbolGroupIterator::operator++() { + assert(Value.File && !isEnd()); + ++Index; + if (isEnd()) + return *this; + + if (Value.File->isPdb()) { + Value.updatePdbModi(Index); + return *this; + } + + scanToNextDebugS(); + return *this; +} + +void SymbolGroupIterator::scanToNextDebugS() { + assert(SectionIter.hasValue()); + auto End = Value.File->obj().section_end(); + auto &Iter = *SectionIter; + assert(!isEnd()); + + while (++Iter != End) { + DebugSubsectionArray SS; + SectionRef SR = *Iter; + if (!isDebugSSection(SR, SS)) + continue; + + Value.updateDebugS(SS); + return; + } +} + +bool SymbolGroupIterator::isEnd() const { + if (!Value.File) + return true; + if (Value.File->isPdb()) { + auto &Dbi = cantFail(Value.File->pdb().getPDBDbiStream()); + uint32_t Count = Dbi.modules().getModuleCount(); + assert(Index <= Count); + return Index == Count; + } + + assert(SectionIter.hasValue()); + return *SectionIter == Value.File->obj().section_end(); +} diff --git a/llvm/tools/llvm-pdbutil/InputFile.h b/llvm/tools/llvm-pdbutil/InputFile.h new file mode 100644 index 00000000000..8063439133c --- /dev/null +++ b/llvm/tools/llvm-pdbutil/InputFile.h @@ -0,0 +1,147 @@ +//===- InputFile.h -------------------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_INPUTFILE_H +#define LLVM_TOOLS_LLVMPDBDUMP_INPUTFILE_H + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/iterator.h" +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace codeview { +class LazyRandomTypeCollection; +} +namespace object { +class COFFObjectFile; +class SectionRef; +} // namespace object + +namespace pdb { +class InputFile; +class LinePrinter; +class PDBFile; +class NativeSession; +class SymbolGroupIterator; +class SymbolGroup; + +class InputFile { + InputFile(); + + std::unique_ptr<NativeSession> PdbSession; + object::OwningBinary<object::Binary> CoffObject; + PointerUnion<PDBFile *, object::COFFObjectFile *> PdbOrObj; + + using TypeCollectionPtr = std::unique_ptr<codeview::LazyRandomTypeCollection>; + + TypeCollectionPtr Types; + TypeCollectionPtr Ids; + + enum TypeCollectionKind { kTypes, kIds }; + codeview::LazyRandomTypeCollection & + getOrCreateTypeCollection(TypeCollectionKind Kind); + +public: + ~InputFile(); + InputFile(InputFile &&Other) = default; + + static Expected<InputFile> open(StringRef Path); + + PDBFile &pdb(); + const PDBFile &pdb() const; + object::COFFObjectFile &obj(); + const object::COFFObjectFile &obj() const; + + bool hasTypes() const; + bool hasIds() const; + + codeview::LazyRandomTypeCollection &types(); + codeview::LazyRandomTypeCollection &ids(); + + iterator_range<SymbolGroupIterator> symbol_groups(); + SymbolGroupIterator symbol_groups_begin(); + SymbolGroupIterator symbol_groups_end(); + + bool isPdb() const; + bool isObj() const; +}; + +class SymbolGroup { + friend class SymbolGroupIterator; + +public: + explicit SymbolGroup(InputFile *File, uint32_t GroupIndex = 0); + + Expected<StringRef> getNameFromStringTable(uint32_t Offset) const; + + void formatFromFileName(LinePrinter &Printer, StringRef File, + bool Append = false) const; + + void formatFromChecksumsOffset(LinePrinter &Printer, uint32_t Offset, + bool Append = false) const; + + StringRef name() const; + + codeview::DebugSubsectionArray getDebugSubsections() const { + return Subsections; + } + const ModuleDebugStreamRef &getPdbModuleStream() const; + + const InputFile &getFile() const { return *File; } + InputFile &getFile() { return *File; } + +private: + void initializeForPdb(uint32_t Modi); + void updatePdbModi(uint32_t Modi); + void updateDebugS(const codeview::DebugSubsectionArray &SS); + + void rebuildChecksumMap(); + InputFile *File = nullptr; + StringRef Name; + codeview::DebugSubsectionArray Subsections; + std::shared_ptr<ModuleDebugStreamRef> DebugStream; + codeview::StringsAndChecksumsRef SC; + StringMap<codeview::FileChecksumEntry> ChecksumsByFile; +}; + +class SymbolGroupIterator + : public iterator_facade_base<SymbolGroupIterator, + std::forward_iterator_tag, SymbolGroup> { +public: + SymbolGroupIterator(); + explicit SymbolGroupIterator(InputFile &File); + SymbolGroupIterator(const SymbolGroupIterator &Other) = default; + SymbolGroupIterator &operator=(const SymbolGroupIterator &R) = default; + + const SymbolGroup &operator*() const; + SymbolGroup &operator*(); + + bool operator==(const SymbolGroupIterator &R) const; + SymbolGroupIterator &operator++(); + +private: + void scanToNextDebugS(); + bool isEnd() const; + + uint32_t Index = 0; + Optional<object::section_iterator> SectionIter; + SymbolGroup Value; +}; + +} // namespace pdb +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-pdbutil/LinePrinter.h b/llvm/tools/llvm-pdbutil/LinePrinter.h index a831dbd9e6e..09bde28f516 100644 --- a/llvm/tools/llvm-pdbutil/LinePrinter.h +++ b/llvm/tools/llvm-pdbutil/LinePrinter.h @@ -93,14 +93,41 @@ private: std::list<Regex> IncludeSymbolFilters; }; +struct PrintScope { + explicit PrintScope(LinePrinter &P, uint32_t IndentLevel) + : P(P), IndentLevel(IndentLevel) {} + explicit PrintScope(const PrintScope &Other, uint32_t LabelWidth) + : P(Other.P), IndentLevel(Other.IndentLevel), LabelWidth(LabelWidth) {} + + LinePrinter &P; + uint32_t IndentLevel; + uint32_t LabelWidth = 0; +}; + +inline Optional<PrintScope> withLabelWidth(const Optional<PrintScope> &Scope, + uint32_t W) { + if (!Scope) + return None; + return PrintScope{*Scope, W}; +} + struct AutoIndent { explicit AutoIndent(LinePrinter &L, uint32_t Amount = 0) - : L(L), Amount(Amount) { + : L(&L), Amount(Amount) { L.Indent(Amount); } - ~AutoIndent() { L.Unindent(Amount); } + explicit AutoIndent(const Optional<PrintScope> &Scope) { + if (Scope.hasValue()) { + L = &Scope->P; + Amount = Scope->IndentLevel; + } + } + ~AutoIndent() { + if (L) + L->Unindent(Amount); + } - LinePrinter &L; + LinePrinter *L = nullptr; uint32_t Amount = 0; }; diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp index 3f77813be9e..1883450bf1b 100644 --- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -17,6 +17,7 @@ #include "BytesOutputStyle.h" #include "Diff.h" #include "DumpOutputStyle.h" +#include "InputFile.h" #include "LinePrinter.h" #include "OutputStyle.h" #include "PrettyCompilandDumper.h" @@ -32,6 +33,7 @@ #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/BinaryFormat/Magic.h" #include "llvm/Config/config.h" #include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" #include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" @@ -761,11 +763,10 @@ static void pdb2Yaml(StringRef Path) { } static void dumpRaw(StringRef Path) { - std::unique_ptr<IPDBSession> Session; - auto &File = loadPDB(Path, Session); - auto O = llvm::make_unique<DumpOutputStyle>(File); + InputFile IF = ExitOnErr(InputFile::open(Path)); + auto O = llvm::make_unique<DumpOutputStyle>(IF); ExitOnErr(O->dump()); } diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.h b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h index 98619da9513..3ce03d5880a 100644 --- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.h +++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h @@ -12,6 +12,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerUnion.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/raw_ostream.h" @@ -19,11 +20,17 @@ #include <stdint.h> namespace llvm { +namespace object { +class COFFObjectFile; +} namespace pdb { class PDBSymbolData; class PDBSymbolFunc; +class PDBFile; uint32_t getTypeLength(const PDBSymbolData &Symbol); } +typedef llvm::PointerUnion<object::COFFObjectFile *, pdb::PDBFile *> + PdbOrCoffObj; } namespace opts { |