summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZachary Turner <zturner@google.com>2017-09-01 20:06:56 +0000
committerZachary Turner <zturner@google.com>2017-09-01 20:06:56 +0000
commitabb17cc084d917a54b3e5021bbb3ecdfc1a49d23 (patch)
tree2ceb0b03a84f9954158397e0222afba212cf9227
parentc36039f462a8270a69b64309810f537e4991bb1c (diff)
downloadbcm5719-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.test4
-rw-r--r--llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h2
-rw-r--r--llvm/include/llvm/DebugInfo/CodeView/StringsAndChecksums.h9
-rw-r--r--llvm/include/llvm/DebugInfo/PDB/Native/ModuleDebugStream.h3
-rw-r--r--llvm/include/llvm/DebugInfo/PDB/Native/PDBFile.h2
-rw-r--r--llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp13
-rw-r--r--llvm/lib/DebugInfo/CodeView/StringsAndChecksums.cpp28
-rw-r--r--llvm/lib/DebugInfo/PDB/Native/PDBFile.cpp16
-rw-r--r--llvm/test/DebugInfo/COFF/udts.ll80
-rw-r--r--llvm/tools/llvm-pdbutil/CMakeLists.txt2
-rw-r--r--llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp640
-rw-r--r--llvm/tools/llvm-pdbutil/DumpOutputStyle.h17
-rw-r--r--llvm/tools/llvm-pdbutil/InputFile.cpp469
-rw-r--r--llvm/tools/llvm-pdbutil/InputFile.h147
-rw-r--r--llvm/tools/llvm-pdbutil/LinePrinter.h33
-rw-r--r--llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp7
-rw-r--r--llvm/tools/llvm-pdbutil/llvm-pdbutil.h7
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 {
OpenPOWER on IntegriCloud