#include "DumpOutputStyle.h"
#include "FormatUtil.h"
+#include "InputFile.h"
#include "MinimalSymbolDumper.h"
#include "MinimalTypeDumper.h"
#include "StreamUtil.h"
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())
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();
}
if (opts::dump::DumpSymbols) {
- if (auto EC = dumpModuleSyms())
+ auto EC = File.isPdb() ? dumpModuleSymsForPdb() : dumpModuleSymsForObj();
+ if (EC)
return EC;
}
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());
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());
}
}
}
-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"))
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.
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)),
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())
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}",
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();
}
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 |");
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();
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;
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;
P.format("line/addr entries = {0}", Count);
P.NewLine();
- typesetLinesAndColumns(File, P, Begin, Block);
+ typesetLinesAndColumns(P, Begin, Block);
}
});
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) {
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");
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) {
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());
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;
opts::dump::DumpIdIndex.end());
}
- AutoIndent Indent(P);
if (!Present) {
P.formatLine("Stream not present");
return Error::success();
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())
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);
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()));
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));
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) {
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");
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);
"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());
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;
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:
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();
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()) {
--- /dev/null
+//===- 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();
+}