diff options
author | monster <monster@ydb.tech> | 2022-07-07 14:41:37 +0300 |
---|---|---|
committer | monster <monster@ydb.tech> | 2022-07-07 14:41:37 +0300 |
commit | 06e5c21a835c0e923506c4ff27929f34e00761c2 (patch) | |
tree | 75efcbc6854ef9bd476eb8bf00cc5c900da436a2 /contrib/libs/llvm12/tools/llvm-pdbutil/DumpOutputStyle.cpp | |
parent | 03f024c4412e3aa613bb543cf1660176320ba8f4 (diff) | |
download | ydb-06e5c21a835c0e923506c4ff27929f34e00761c2.tar.gz |
fix ya.make
Diffstat (limited to 'contrib/libs/llvm12/tools/llvm-pdbutil/DumpOutputStyle.cpp')
-rw-r--r-- | contrib/libs/llvm12/tools/llvm-pdbutil/DumpOutputStyle.cpp | 1989 |
1 files changed, 1989 insertions, 0 deletions
diff --git a/contrib/libs/llvm12/tools/llvm-pdbutil/DumpOutputStyle.cpp b/contrib/libs/llvm12/tools/llvm-pdbutil/DumpOutputStyle.cpp new file mode 100644 index 0000000000..babdb56a71 --- /dev/null +++ b/contrib/libs/llvm12/tools/llvm-pdbutil/DumpOutputStyle.cpp @@ -0,0 +1,1989 @@ +//===- DumpOutputStyle.cpp ------------------------------------ *- C++ --*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DumpOutputStyle.h" + +#include "FormatUtil.h" +#include "InputFile.h" +#include "MinimalSymbolDumper.h" +#include "MinimalTypeDumper.h" +#include "StreamUtil.h" +#include "TypeReferenceTracker.h" +#include "llvm-pdbutil.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h" +#include "llvm/DebugInfo/CodeView/Formatters.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/Line.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" +#include "llvm/DebugInfo/CodeView/TypeHashing.h" +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" + +#include <cctype> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +DumpOutputStyle::DumpOutputStyle(InputFile &File) + : File(File), P(2, false, outs()) { + if (opts::dump::DumpTypeRefStats) + RefTracker.reset(new TypeReferenceTracker(File)); +} + +DumpOutputStyle::~DumpOutputStyle() {} + +PDBFile &DumpOutputStyle::getPdb() { return File.pdb(); } +object::COFFObjectFile &DumpOutputStyle::getObj() { return File.obj(); } + +void DumpOutputStyle::printStreamNotValidForObj() { + AutoIndent Indent(P, 4); + P.formatLine("Dumping this stream is not valid for object files"); +} + +void DumpOutputStyle::printStreamNotPresent(StringRef StreamName) { + AutoIndent Indent(P, 4); + P.formatLine("{0} stream not present", StreamName); +} + +Error DumpOutputStyle::dump() { + // Walk symbols & globals if we are supposed to mark types referenced. + if (opts::dump::DumpTypeRefStats) + RefTracker->mark(); + + if (opts::dump::DumpSummary) { + if (auto EC = dumpFileSummary()) + return EC; + P.NewLine(); + } + + if (opts::dump::DumpStreams) { + if (auto EC = dumpStreamSummary()) + return EC; + P.NewLine(); + } + + if (opts::dump::DumpSymbolStats) { + if (auto EC = dumpSymbolStats()) + return EC; + P.NewLine(); + } + + if (opts::dump::DumpUdtStats) { + if (auto EC = dumpUdtStats()) + return EC; + P.NewLine(); + } + + if (opts::dump::DumpTypeStats || opts::dump::DumpIDStats) { + if (auto EC = dumpTypeStats()) + return EC; + P.NewLine(); + } + + if (opts::dump::DumpNamedStreams) { + if (auto EC = dumpNamedStreams()) + return EC; + P.NewLine(); + } + + if (opts::dump::DumpStringTable || opts::dump::DumpStringTableDetails) { + if (auto EC = dumpStringTable()) + return EC; + P.NewLine(); + } + + if (opts::dump::DumpModules) { + if (auto EC = dumpModules()) + return EC; + } + + if (opts::dump::DumpModuleFiles) { + if (auto EC = dumpModuleFiles()) + return EC; + } + + if (opts::dump::DumpLines) { + if (auto EC = dumpLines()) + return EC; + } + + if (opts::dump::DumpInlineeLines) { + if (auto EC = dumpInlineeLines()) + return EC; + } + + if (opts::dump::DumpXmi) { + if (auto EC = dumpXmi()) + return EC; + } + + if (opts::dump::DumpXme) { + if (auto EC = dumpXme()) + return EC; + } + + if (opts::dump::DumpFpo) { + if (auto EC = dumpFpo()) + return EC; + } + + if (File.isObj()) { + if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() || + opts::dump::DumpTypeExtras) + if (auto EC = dumpTypesFromObjectFile()) + return EC; + } else { + if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() || + opts::dump::DumpTypeExtras) { + if (auto EC = dumpTpiStream(StreamTPI)) + return EC; + } + + if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() || + opts::dump::DumpIdExtras) { + if (auto EC = dumpTpiStream(StreamIPI)) + return EC; + } + } + + if (opts::dump::DumpGSIRecords) { + if (auto EC = dumpGSIRecords()) + return EC; + } + + if (opts::dump::DumpGlobals) { + if (auto EC = dumpGlobals()) + return EC; + } + + if (opts::dump::DumpPublics) { + if (auto EC = dumpPublics()) + return EC; + } + + if (opts::dump::DumpSymbols) { + auto EC = File.isPdb() ? dumpModuleSymsForPdb() : dumpModuleSymsForObj(); + if (EC) + return EC; + } + + if (opts::dump::DumpTypeRefStats) { + if (auto EC = dumpTypeRefStats()) + return EC; + } + + if (opts::dump::DumpSectionHeaders) { + if (auto EC = dumpSectionHeaders()) + return EC; + } + + if (opts::dump::DumpSectionContribs) { + if (auto EC = dumpSectionContribs()) + return EC; + } + + if (opts::dump::DumpSectionMap) { + if (auto EC = dumpSectionMap()) + return EC; + } + + P.NewLine(); + + return Error::success(); +} + +static void printHeader(LinePrinter &P, const Twine &S) { + P.NewLine(); + P.formatLine("{0,=60}", S); + P.formatLine("{0}", fmt_repeat('=', 60)); +} + +Error DumpOutputStyle::dumpFileSummary() { + printHeader(P, "Summary"); + + if (File.isObj()) { + printStreamNotValidForObj(); + return Error::success(); + } + + AutoIndent Indent(P); + ExitOnError Err("Invalid PDB Format: "); + + 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}", 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(const SymbolGroup &SG, + StatCollection &CumulativeStats) { + StatCollection Stats; + if (SG.getFile().isPdb() && SG.hasDebugStream()) { + // 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(const SymbolGroup &SG, + StatCollection &CumulativeStats) { + StatCollection Stats; + for (const auto &Chunk : SG.getDebugSubsections()) { + Stats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength()); + CumulativeStats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength()); + } + return Stats; +} + +static inline std::string formatModuleDetailKind(DebugSubsectionKind K) { + return formatChunkKind(K, false); +} + +static inline std::string formatModuleDetailKind(SymbolKind K) { + return formatSymbolKind(K); +} + +// Get the stats sorted by size, descending. +std::vector<StatCollection::KindAndStat> +StatCollection::getStatsSortedBySize() const { + std::vector<KindAndStat> SortedStats(Individual.begin(), Individual.end()); + llvm::stable_sort(SortedStats, + [](const KindAndStat &LHS, const KindAndStat &RHS) { + return LHS.second.Size > RHS.second.Size; + }); + return SortedStats; +} + +template <typename Kind> +static void printModuleDetailStats(LinePrinter &P, StringRef Label, + const StatCollection &Stats) { + P.NewLine(); + P.formatLine(" {0}", Label); + AutoIndent Indent(P); + P.formatLine("{0,40}: {1,7} entries ({2,12:N} bytes)", "Total", + Stats.Totals.Count, Stats.Totals.Size); + P.formatLine("{0}", fmt_repeat('-', 74)); + + for (const auto &K : Stats.getStatsSortedBySize()) { + std::string KindName = formatModuleDetailKind(Kind(K.first)); + P.formatLine("{0,40}: {1,7} entries ({2,12:N} bytes)", KindName, + K.second.Count, K.second.Size); + } +} + +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 false; + if (Name.equals_lower("* linker *")) + return false; + if (Name.startswith_lower("f:\\binaries\\Intermediate\\vctools")) + return false; + if (Name.startswith_lower("f:\\dd\\vctools\\crt")) + return false; + return true; +} + +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. + if (opts::dump::DumpModi.getNumOccurrences() == 0) + return true; + + // Otherwise, only dump if this is the same module specified. + return (opts::dump::DumpModi == Idx); +} + +Error DumpOutputStyle::dumpStreamSummary() { + printHeader(P, "Streams"); + + if (File.isObj()) { + printStreamNotValidForObj(); + return Error::success(); + } + + AutoIndent Indent(P); + + if (StreamPurposes.empty()) + discoverStreamPurposes(getPdb(), StreamPurposes); + + 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(getPdb().getStreamByteSize(StreamIdx), AlignStyle::Right, + NumDigits(MaxStreamSize)), + StreamPurposes[StreamIdx].getLongName()); + + if (opts::dump::DumpStreamBlocks) { + auto Blocks = getPdb().getStreamBlockList(StreamIdx); + std::vector<uint32_t> BV(Blocks.begin(), Blocks.end()); + P.formatLine(" {0} Blocks: [{1}]", + fmt_repeat(' ', NumDigits(StreamCount)), + make_range(BV.begin(), BV.end())); + } + } + + return Error::success(); +} + +static Expected<ModuleDebugStreamRef> getModuleDebugStream(PDBFile &File, + uint32_t Index) { + ExitOnError Err("Unexpected error: "); + + auto &Dbi = Err(File.getPDBDbiStream()); + const auto &Modules = Dbi.modules(); + auto Modi = Modules.getModuleDescriptor(Index); + + 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); +} + +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()); + } + + AutoIndent Indent(HeaderScope); + Callback(Modi, SG); +} + +template <typename CallbackT> +static void iterateSymbolGroups(InputFile &Input, + const Optional<PrintScope> &HeaderScope, + CallbackT Callback) { + AutoIndent Indent(HeaderScope); + + ExitOnError Err("Unexpected error processing modules: "); + + if (opts::dump::DumpModi.getNumOccurrences() > 0) { + assert(opts::dump::DumpModi.getNumOccurrences() == 1); + uint32_t Modi = opts::dump::DumpModi; + SymbolGroup SG(&Input, Modi); + iterateOneModule(Input, withLabelWidth(HeaderScope, NumDigits(Modi)), SG, + Modi, Callback); + return; + } + + 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( + InputFile &File, const Optional<PrintScope> &HeaderScope, + llvm::function_ref<void(uint32_t, const SymbolGroup &, SubsectionT &)> + Callback) { + + iterateSymbolGroups(File, HeaderScope, + [&](uint32_t Modi, const SymbolGroup &SG) { + for (const auto &SS : SG.getDebugSubsections()) { + SubsectionT Subsection; + + if (SS.kind() != Subsection.kind()) + continue; + + BinaryStreamReader Reader(SS.getRecordData()); + if (auto EC = Subsection.initialize(Reader)) + continue; + Callback(Modi, SG, Subsection); + } + }); +} + +static Expected<std::pair<std::unique_ptr<MappedBlockStream>, + ArrayRef<llvm::object::coff_section>>> +loadSectionHeaders(PDBFile &File, DbgHeaderType Type) { + if (!File.hasPDBDbiStream()) + return make_error<StringError>( + "Section headers require a DBI Stream, which could not be loaded", + inconvertibleErrorCode()); + + auto &Dbi = cantFail(File.getPDBDbiStream()); + uint32_t SI = Dbi.getDebugStreamIndex(Type); + + if (SI == kInvalidStreamIndex) + return make_error<StringError>( + "PDB does not contain the requested image section header type", + inconvertibleErrorCode()); + + auto Stream = File.createIndexedStream(SI); + if (!Stream) + return make_error<StringError>("Could not load the required stream data", + inconvertibleErrorCode()); + + ArrayRef<object::coff_section> Headers; + if (Stream->getLength() % sizeof(object::coff_section) != 0) + return make_error<StringError>( + "Section header array size is not a multiple of section header size", + inconvertibleErrorCode()); + + uint32_t NumHeaders = Stream->getLength() / sizeof(object::coff_section); + BinaryStreamReader Reader(*Stream); + cantFail(Reader.readArray(Headers, NumHeaders)); + return std::make_pair(std::move(Stream), Headers); +} + +static std::vector<std::string> getSectionNames(PDBFile &File) { + auto ExpectedHeaders = loadSectionHeaders(File, DbgHeaderType::SectionHdr); + if (!ExpectedHeaders) + return {}; + + std::unique_ptr<MappedBlockStream> Stream; + ArrayRef<object::coff_section> Headers; + std::tie(Stream, Headers) = std::move(*ExpectedHeaders); + std::vector<std::string> Names; + for (const auto &H : Headers) + Names.push_back(H.Name); + return Names; +} + +static void dumpSectionContrib(LinePrinter &P, const SectionContrib &SC, + ArrayRef<std::string> SectionNames, + uint32_t FieldWidth) { + std::string NameInsert; + if (SC.ISect > 0 && SC.ISect <= SectionNames.size()) { + StringRef SectionName = SectionNames[SC.ISect - 1]; + NameInsert = formatv("[{0}]", SectionName).str(); + } else + NameInsert = "[???]"; + P.formatLine("SC{5} | mod = {2}, {0}, size = {1}, data crc = {3}, reloc " + "crc = {4}", + formatSegmentOffset(SC.ISect, SC.Off), fmtle(SC.Size), + fmtle(SC.Imod), fmtle(SC.DataCrc), fmtle(SC.RelocCrc), + fmt_align(NameInsert, AlignStyle::Left, FieldWidth + 2)); + AutoIndent Indent(P, FieldWidth + 2); + P.formatLine(" {0}", + formatSectionCharacteristics(P.getIndentLevel() + 6, + SC.Characteristics, 3, " | ")); +} + +static void dumpSectionContrib(LinePrinter &P, const SectionContrib2 &SC, + ArrayRef<std::string> SectionNames, + uint32_t FieldWidth) { + P.formatLine("SC2[{6}] | mod = {2}, {0}, size = {1}, data crc = {3}, reloc " + "crc = {4}, coff section = {5}", + formatSegmentOffset(SC.Base.ISect, SC.Base.Off), + fmtle(SC.Base.Size), fmtle(SC.Base.Imod), fmtle(SC.Base.DataCrc), + fmtle(SC.Base.RelocCrc), fmtle(SC.ISectCoff)); + P.formatLine(" {0}", + formatSectionCharacteristics(P.getIndentLevel() + 6, + SC.Base.Characteristics, 3, " | ")); +} + +Error DumpOutputStyle::dumpModules() { + printHeader(P, "Modules"); + + if (File.isObj()) { + printStreamNotValidForObj(); + return Error::success(); + } + + if (!getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + + AutoIndent Indent(P); + ExitOnError Err("Unexpected error processing modules: "); + + auto &Stream = Err(getPdb().getPDBDbiStream()); + + const DbiModuleList &Modules = Stream.modules(); + iterateSymbolGroups( + File, PrintScope{P, 11}, [&](uint32_t Modi, const SymbolGroup &Strings) { + auto Desc = Modules.getModuleDescriptor(Modi); + if (opts::dump::DumpSectionContribs) { + std::vector<std::string> Sections = getSectionNames(getPdb()); + dumpSectionContrib(P, Desc.getSectionContrib(), Sections, 0); + } + P.formatLine("Obj: `{0}`: ", Desc.getObjFileName()); + P.formatLine("debug stream: {0}, # files: {1}, has ec info: {2}", + Desc.getModuleStreamIndex(), Desc.getNumberOfFiles(), + Desc.hasECInfo()); + StringRef PdbFilePath = + Err(Stream.getECName(Desc.getPdbFilePathNameIndex())); + StringRef SrcFilePath = + Err(Stream.getECName(Desc.getSourceFileNameIndex())); + P.formatLine("pdb file ni: {0} `{1}`, src file ni: {2} `{3}`", + Desc.getPdbFilePathNameIndex(), PdbFilePath, + Desc.getSourceFileNameIndex(), SrcFilePath); + }); + return Error::success(); +} + +Error DumpOutputStyle::dumpModuleFiles() { + printHeader(P, "Files"); + + if (File.isObj()) { + printStreamNotValidForObj(); + return Error::success(); + } + + if (!getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + + ExitOnError Err("Unexpected error processing modules: "); + + 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); + } + }); + return Error::success(); +} + +Error DumpOutputStyle::dumpSymbolStats() { + printHeader(P, "Module Stats"); + + if (File.isPdb() && !getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + + ExitOnError Err("Unexpected error processing modules: "); + + StatCollection SymStats; + StatCollection ChunkStats; + + Optional<PrintScope> Scope; + if (File.isPdb()) + Scope.emplace(P, 2); + + iterateSymbolGroups(File, Scope, [&](uint32_t Modi, const SymbolGroup &SG) { + StatCollection SS = getSymbolStats(SG, SymStats); + StatCollection CS = getChunkStats(SG, ChunkStats); + + if (SG.getFile().isPdb()) { + AutoIndent Indent(P); + 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, + getPdb().getStreamByteSize(StreamIdx)); + + printModuleDetailStats<SymbolKind>(P, "Symbols", SS); + printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", CS); + } + }); + + if (SymStats.Totals.Count > 0) { + P.printLine(" Summary |"); + AutoIndent Indent(P, 4); + printModuleDetailStats<SymbolKind>(P, "Symbols", SymStats); + printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", ChunkStats); + } + + return Error::success(); +} + +Error DumpOutputStyle::dumpTypeStats() { + printHeader(P, "Type Record Stats"); + + // Iterate the types, categorize by kind, accumulate size stats. + StatCollection TypeStats; + LazyRandomTypeCollection &Types = + opts::dump::DumpTypeStats ? File.types() : File.ids(); + for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI)) { + CVType Type = Types.getType(*TI); + TypeStats.update(uint32_t(Type.kind()), Type.length()); + } + + P.NewLine(); + P.formatLine(" Types"); + AutoIndent Indent(P); + P.formatLine("{0,16}: {1,7} entries ({2,12:N} bytes, {3,7} avg)", "Total", + TypeStats.Totals.Count, TypeStats.Totals.Size, + (double)TypeStats.Totals.Size / TypeStats.Totals.Count); + P.formatLine("{0}", fmt_repeat('-', 74)); + + for (const auto &K : TypeStats.getStatsSortedBySize()) { + P.formatLine("{0,16}: {1,7} entries ({2,12:N} bytes, {3,7} avg)", + formatTypeLeafKind(TypeLeafKind(K.first)), K.second.Count, + K.second.Size, (double)K.second.Size / K.second.Count); + } + return Error::success(); +} + +static bool isValidNamespaceIdentifier(StringRef S) { + if (S.empty()) + return false; + + if (std::isdigit(S[0])) + return false; + + return llvm::all_of(S, [](char C) { return std::isalnum(C); }); +} + +namespace { +constexpr uint32_t kNoneUdtKind = 0; +constexpr uint32_t kSimpleUdtKind = 1; +constexpr uint32_t kUnknownUdtKind = 2; +} // namespace + +static std::string getUdtStatLabel(uint32_t Kind) { + if (Kind == kNoneUdtKind) + return "<none type>"; + + if (Kind == kSimpleUdtKind) + return "<simple type>"; + + if (Kind == kUnknownUdtKind) + return "<unknown type>"; + + return formatTypeLeafKind(static_cast<TypeLeafKind>(Kind)); +} + +static uint32_t getLongestTypeLeafName(const StatCollection &Stats) { + size_t L = 0; + for (const auto &Stat : Stats.Individual) { + std::string Label = getUdtStatLabel(Stat.first); + L = std::max(L, Label.size()); + } + return static_cast<uint32_t>(L); +} + +Error DumpOutputStyle::dumpUdtStats() { + printHeader(P, "S_UDT Record Stats"); + + if (File.isPdb() && !getPdb().hasPDBGlobalsStream()) { + printStreamNotPresent("Globals"); + return Error::success(); + } + + StatCollection UdtStats; + StatCollection UdtTargetStats; + AutoIndent Indent(P, 4); + + auto &TpiTypes = File.types(); + + StringMap<StatCollection::Stat> NamespacedStats; + + size_t LongestNamespace = 0; + auto HandleOneSymbol = [&](const CVSymbol &Sym) { + if (Sym.kind() != SymbolKind::S_UDT) + 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.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) + return; + + StringRef Scope = UDT.Name.take_front(Pos); + if (Scope.empty() || !isValidNamespaceIdentifier(Scope)) + return; + + LongestNamespace = std::max(LongestNamespace, Scope.size()); + NamespacedStats[Scope].update(RecordSize); + }; + + P.NewLine(); + + if (File.isPdb()) { + auto &SymbolRecords = cantFail(getPdb().getPDBSymbolStream()); + auto ExpGlobals = getPdb().getPDBGlobalsStream(); + if (!ExpGlobals) + return ExpGlobals.takeError(); + + for (uint32_t PubSymOff : ExpGlobals->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(); + size_t LongestTypeLeafKind = getLongestTypeLeafName(UdtTargetStats); + size_t FieldWidth = std::max(LongestNamespace, LongestTypeLeafKind); + + // Compute the max number of digits for count and size fields, including comma + // separators. + StringRef CountHeader("Count"); + StringRef SizeHeader("Size"); + size_t CD = NumDigits(UdtStats.Totals.Count); + CD += (CD - 1) / 3; + CD = std::max(CD, CountHeader.size()); + + size_t SD = NumDigits(UdtStats.Totals.Size); + SD += (SD - 1) / 3; + SD = std::max(SD, SizeHeader.size()); + + uint32_t TableWidth = FieldWidth + 3 + CD + 2 + SD + 1; + + P.formatLine("{0} | {1} {2}", + fmt_align("Record Kind", AlignStyle::Right, FieldWidth), + fmt_align(CountHeader, AlignStyle::Right, CD), + fmt_align(SizeHeader, AlignStyle::Right, SD)); + + P.formatLine("{0}", fmt_repeat('-', TableWidth)); + for (const auto &Stat : UdtTargetStats.getStatsSortedBySize()) { + std::string Label = getUdtStatLabel(Stat.first); + P.formatLine("{0} | {1:N} {2:N}", + fmt_align(Label, AlignStyle::Right, FieldWidth), + fmt_align(Stat.second.Count, AlignStyle::Right, CD), + fmt_align(Stat.second.Size, AlignStyle::Right, SD)); + } + P.formatLine("{0}", fmt_repeat('-', TableWidth)); + P.formatLine("{0} | {1:N} {2:N}", + fmt_align("Total (S_UDT)", AlignStyle::Right, FieldWidth), + fmt_align(UdtStats.Totals.Count, AlignStyle::Right, CD), + fmt_align(UdtStats.Totals.Size, AlignStyle::Right, SD)); + P.formatLine("{0}", fmt_repeat('-', TableWidth)); + struct StrAndStat { + StringRef Key; + StatCollection::Stat Stat; + }; + + // Print namespace stats in descending order of size. + std::vector<StrAndStat> NamespacedStatsSorted; + for (const auto &Stat : NamespacedStats) + NamespacedStatsSorted.push_back({Stat.getKey(), Stat.second}); + llvm::stable_sort(NamespacedStatsSorted, + [](const StrAndStat &L, const StrAndStat &R) { + return L.Stat.Size > R.Stat.Size; + }); + for (const auto &Stat : NamespacedStatsSorted) { + std::string Label = std::string(formatv("namespace '{0}'", Stat.Key)); + P.formatLine("{0} | {1:N} {2:N}", + fmt_align(Label, AlignStyle::Right, FieldWidth), + fmt_align(Stat.Stat.Count, AlignStyle::Right, CD), + fmt_align(Stat.Stat.Size, AlignStyle::Right, SD)); + } + return Error::success(); +} + +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; + + // Let's try to keep it under 100 characters + constexpr uint32_t kMaxRowLength = 100; + // At least 3 spaces between columns. + uint32_t ColumnsPerRow = kMaxRowLength / (MinColumnWidth + 3); + uint32_t ItemsLeft = E.LineNumbers.size(); + auto LineIter = E.LineNumbers.begin(); + while (ItemsLeft != 0) { + uint32_t RowColumns = std::min(ItemsLeft, ColumnsPerRow); + for (uint32_t I = 0; I < RowColumns; ++I) { + LineInfo Line(LineIter->Flags); + std::string LineStr; + if (Line.isAlwaysStepInto()) + LineStr = "ASI"; + else if (Line.isNeverStepInto()) + LineStr = "NSI"; + else + LineStr = utostr(Line.getStartLine()); + char Statement = Line.isStatement() ? ' ' : '!'; + P.format("{0} {1:X-} {2} ", + fmt_align(LineStr, AlignStyle::Right, kMaxCharsPerLineNumber), + fmt_align(Start + LineIter->Offset, AlignStyle::Right, 8, '0'), + Statement); + ++LineIter; + --ItemsLeft; + } + P.NewLine(); + } +} + +Error DumpOutputStyle::dumpLines() { + printHeader(P, "Lines"); + + if (File.isPdb() && !getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + + uint32_t LastModi = UINT32_MAX; + uint32_t LastNameIndex = UINT32_MAX; + iterateModuleSubsections<DebugLinesSubsectionRef>( + File, PrintScope{P, 4}, + [this, &LastModi, &LastNameIndex](uint32_t Modi, + const SymbolGroup &Strings, + DebugLinesSubsectionRef &Lines) { + uint16_t Segment = Lines.header()->RelocSegment; + uint32_t Begin = Lines.header()->RelocOffset; + uint32_t End = Begin + Lines.header()->CodeSize; + for (const auto &Block : Lines) { + if (LastModi != Modi || LastNameIndex != Block.NameIndex) { + LastModi = Modi; + LastNameIndex = Block.NameIndex; + Strings.formatFromChecksumsOffset(P, Block.NameIndex); + } + + AutoIndent Indent(P, 2); + P.formatLine("{0:X-4}:{1:X-8}-{2:X-8}, ", Segment, Begin, End); + uint32_t Count = Block.LineNumbers.size(); + if (Lines.hasColumnInfo()) + P.format("line/column/addr entries = {0}", Count); + else + P.format("line/addr entries = {0}", Count); + + P.NewLine(); + typesetLinesAndColumns(P, Begin, Block); + } + }); + + return Error::success(); +} + +Error DumpOutputStyle::dumpInlineeLines() { + printHeader(P, "Inlinee Lines"); + + if (File.isPdb() && !getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + + iterateModuleSubsections<DebugInlineeLinesSubsectionRef>( + 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) { + P.formatLine("{0,+8} | {1,+5} | ", Entry.Header->Inlinee, + fmtle(Entry.Header->SourceLineNum)); + Strings.formatFromChecksumsOffset(P, Entry.Header->FileID, true); + for (const auto &ExtraFileID : Entry.ExtraFiles) { + P.formatLine(" "); + Strings.formatFromChecksumsOffset(P, ExtraFileID, true); + } + } + P.NewLine(); + }); + + return Error::success(); +} + +Error DumpOutputStyle::dumpXmi() { + printHeader(P, "Cross Module Imports"); + + if (File.isPdb() && !getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + + iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>( + File, PrintScope{P, 2}, + [this](uint32_t Modi, const SymbolGroup &Strings, + DebugCrossModuleImportsSubsectionRef &Imports) { + P.formatLine("{0,=32} | {1}", "Imported Module", "Type IDs"); + + for (const auto &Xmi : Imports) { + auto ExpectedModule = + Strings.getNameFromStringTable(Xmi.Header->ModuleNameOffset); + StringRef Module; + SmallString<32> ModuleStorage; + if (!ExpectedModule) { + Module = "(unknown module)"; + consumeError(ExpectedModule.takeError()); + } else + Module = *ExpectedModule; + if (Module.size() > 32) { + ModuleStorage = "..."; + ModuleStorage += Module.take_back(32 - 3); + Module = ModuleStorage; + } + std::vector<std::string> TIs; + for (const auto I : Xmi.Imports) + TIs.push_back(std::string(formatv("{0,+10:X+}", fmtle(I)))); + std::string Result = + typesetItemList(TIs, P.getIndentLevel() + 35, 12, " "); + P.formatLine("{0,+32} | {1}", Module, Result); + } + }); + + return Error::success(); +} + +Error DumpOutputStyle::dumpXme() { + printHeader(P, "Cross Module Exports"); + + if (File.isPdb() && !getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + + iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>( + 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) { + P.formatLine("{0,+10:X+} | {1}", TypeIndex(Export.Local), + TypeIndex(Export.Global)); + } + }); + + return Error::success(); +} + +std::string formatFrameType(object::frame_type FT) { + switch (FT) { + case object::frame_type::Fpo: + return "FPO"; + case object::frame_type::NonFpo: + return "Non-FPO"; + case object::frame_type::Trap: + return "Trap"; + case object::frame_type::Tss: + return "TSS"; + } + return "<unknown>"; +} + +Error DumpOutputStyle::dumpOldFpo(PDBFile &File) { + printHeader(P, "Old FPO Data"); + + ExitOnError Err("Error dumping old fpo data:"); + auto &Dbi = Err(File.getPDBDbiStream()); + + if (!Dbi.hasOldFpoRecords()) { + printStreamNotPresent("FPO"); + return Error::success(); + } + + const FixedStreamArray<object::FpoData>& Records = Dbi.getOldFpoRecords(); + + P.printLine(" RVA | Code | Locals | Params | Prolog | Saved Regs | Use " + "BP | Has SEH | Frame Type"); + + for (const object::FpoData &FD : Records) { + P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,6} | {5,10} | {6,6} | " + "{7,7} | {8,9}", + uint32_t(FD.Offset), uint32_t(FD.Size), uint32_t(FD.NumLocals), + uint32_t(FD.NumParams), FD.getPrologSize(), + FD.getNumSavedRegs(), FD.useBP(), FD.hasSEH(), + formatFrameType(FD.getFP())); + } + return Error::success(); +} + +Error DumpOutputStyle::dumpNewFpo(PDBFile &File) { + printHeader(P, "New FPO Data"); + + ExitOnError Err("Error dumping new fpo data:"); + auto &Dbi = Err(File.getPDBDbiStream()); + + if (!Dbi.hasNewFpoRecords()) { + printStreamNotPresent("New FPO"); + return Error::success(); + } + + const DebugFrameDataSubsectionRef& FDS = Dbi.getNewFpoRecords(); + + P.printLine(" RVA | Code | Locals | Params | Stack | Prolog | Saved Regs " + "| Has SEH | Has C++EH | Start | Program"); + for (const FrameData &FD : FDS) { + bool IsFuncStart = FD.Flags & FrameData::IsFunctionStart; + bool HasEH = FD.Flags & FrameData::HasEH; + bool HasSEH = FD.Flags & FrameData::HasSEH; + + auto &StringTable = Err(File.getStringTable()); + + auto Program = Err(StringTable.getStringForID(FD.FrameFunc)); + P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,5} | {5,6} | {6,10} | " + "{7,7} | {8,9} | {9,5} | {10}", + uint32_t(FD.RvaStart), uint32_t(FD.CodeSize), + uint32_t(FD.LocalSize), uint32_t(FD.ParamsSize), + uint32_t(FD.MaxStackSize), uint16_t(FD.PrologSize), + uint16_t(FD.SavedRegsSize), HasSEH, HasEH, IsFuncStart, + Program); + } + return Error::success(); +} + +Error DumpOutputStyle::dumpFpo() { + if (!File.isPdb()) { + printStreamNotValidForObj(); + return Error::success(); + } + + PDBFile &File = getPdb(); + if (!File.hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + + if (auto EC = dumpOldFpo(File)) + return EC; + if (auto EC = dumpNewFpo(File)) + return EC; + return Error::success(); +} + +Error DumpOutputStyle::dumpStringTableFromPdb() { + AutoIndent Indent(P); + auto IS = getPdb().getStringTable(); + if (!IS) { + P.formatLine("Not present in file"); + consumeError(IS.takeError()); + return Error::success(); + } + + if (opts::dump::DumpStringTable) { + if (IS->name_ids().empty()) + P.formatLine("Empty"); + else { + auto MaxID = + std::max_element(IS->name_ids().begin(), IS->name_ids().end()); + uint32_t Digits = NumDigits(*MaxID); + + P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits), + "String"); + + std::vector<uint32_t> SortedIDs(IS->name_ids().begin(), + IS->name_ids().end()); + llvm::sort(SortedIDs); + for (uint32_t I : SortedIDs) { + auto ES = IS->getStringForID(I); + llvm::SmallString<32> Str; + if (!ES) { + consumeError(ES.takeError()); + Str = "Error reading string"; + } else if (!ES->empty()) { + Str.append("'"); + Str.append(*ES); + Str.append("'"); + } + + if (!Str.empty()) + P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits), + Str); + } + } + } + + if (opts::dump::DumpStringTableDetails) { + P.NewLine(); + { + P.printLine("String Table Header:"); + AutoIndent Indent(P); + P.formatLine("Signature: {0}", IS->getSignature()); + P.formatLine("Hash Version: {0}", IS->getHashVersion()); + P.formatLine("Name Buffer Size: {0}", IS->getByteSize()); + P.NewLine(); + } + + BinaryStreamRef NameBuffer = IS->getStringTable().getBuffer(); + ArrayRef<uint8_t> Contents; + cantFail(NameBuffer.readBytes(0, NameBuffer.getLength(), Contents)); + P.formatBinary("Name Buffer", Contents, 0); + P.NewLine(); + { + P.printLine("Hash Table:"); + AutoIndent Indent(P); + P.formatLine("Bucket Count: {0}", IS->name_ids().size()); + for (const auto &Entry : enumerate(IS->name_ids())) + P.formatLine("Bucket[{0}] : {1}", Entry.index(), + uint32_t(Entry.value())); + P.formatLine("Name Count: {0}", IS->getNameCount()); + } + } + return Error::success(); +} + +Error DumpOutputStyle::dumpStringTableFromObj() { + iterateModuleSubsections<DebugStringTableSubsectionRef>( + File, PrintScope{P, 4}, + [&](uint32_t Modi, const SymbolGroup &Strings, + DebugStringTableSubsectionRef &Strings2) { + BinaryStreamRef StringTableBuffer = Strings2.getBuffer(); + BinaryStreamReader Reader(StringTableBuffer); + while (Reader.bytesRemaining() > 0) { + StringRef Str; + uint32_t Offset = Reader.getOffset(); + cantFail(Reader.readCString(Str)); + if (Str.empty()) + continue; + + P.formatLine("{0} | {1}", fmt_align(Offset, AlignStyle::Right, 4), + Str); + } + }); + return Error::success(); +} + +Error DumpOutputStyle::dumpNamedStreams() { + printHeader(P, "Named Streams"); + + if (File.isObj()) { + printStreamNotValidForObj(); + return Error::success(); + } + + AutoIndent Indent(P); + ExitOnError Err("Invalid PDB File: "); + + auto &IS = Err(File.pdb().getPDBInfoStream()); + const NamedStreamMap &NS = IS.getNamedStreams(); + for (const auto &Entry : NS.entries()) { + P.printLine(Entry.getKey()); + AutoIndent Indent2(P, 2); + P.formatLine("Index: {0}", Entry.getValue()); + P.formatLine("Size in bytes: {0}", + File.pdb().getStreamByteSize(Entry.getValue())); + } + + return Error::success(); +} + +Error DumpOutputStyle::dumpStringTable() { + printHeader(P, "String Table"); + + if (File.isPdb()) + return dumpStringTableFromPdb(); + + return dumpStringTableFromObj(); +} + +static void buildDepSet(LazyRandomTypeCollection &Types, + ArrayRef<TypeIndex> Indices, + std::map<TypeIndex, CVType> &DepSet) { + SmallVector<TypeIndex, 4> DepList; + for (const auto &I : Indices) { + TypeIndex TI(I); + if (DepSet.find(TI) != DepSet.end() || TI.isSimple() || TI.isNoneType()) + continue; + + CVType Type = Types.getType(TI); + DepSet[TI] = Type; + codeview::discoverTypeIndices(Type, DepList); + buildDepSet(Types, DepList, DepSet); + } +} + +static void +dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types, + TypeReferenceTracker *RefTracker, uint32_t NumTypeRecords, + uint32_t NumHashBuckets, + FixedStreamArray<support::ulittle32_t> HashValues, + TpiStream *Stream, bool Bytes, bool Extras) { + + Printer.formatLine("Showing {0:N} records", NumTypeRecords); + uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + NumTypeRecords); + + MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker, + NumHashBuckets, HashValues, Stream); + + if (auto EC = codeview::visitTypeStream(Types, V)) { + Printer.formatLine("An error occurred dumping type records: {0}", + toString(std::move(EC))); + } +} + +static void dumpPartialTypeStream(LinePrinter &Printer, + LazyRandomTypeCollection &Types, + TypeReferenceTracker *RefTracker, + TpiStream &Stream, ArrayRef<TypeIndex> TiList, + bool Bytes, bool Extras, bool Deps) { + uint32_t Width = + NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords()); + + MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker, + Stream.getNumHashBuckets(), Stream.getHashValues(), + &Stream); + + if (opts::dump::DumpTypeDependents) { + // If we need to dump all dependents, then iterate each index and find + // all dependents, adding them to a map ordered by TypeIndex. + std::map<TypeIndex, CVType> DepSet; + buildDepSet(Types, TiList, DepSet); + + Printer.formatLine( + "Showing {0:N} records and their dependents ({1:N} records total)", + TiList.size(), DepSet.size()); + + for (auto &Dep : DepSet) { + if (auto EC = codeview::visitTypeRecord(Dep.second, Dep.first, V)) + Printer.formatLine("An error occurred dumping type record {0}: {1}", + Dep.first, toString(std::move(EC))); + } + } else { + Printer.formatLine("Showing {0:N} records.", TiList.size()); + + for (const auto &I : TiList) { + TypeIndex TI(I); + CVType Type = Types.getType(TI); + if (auto EC = codeview::visitTypeRecord(Type, TI, V)) + Printer.formatLine("An error occurred dumping type record {0}: {1}", TI, + toString(std::move(EC))); + } + } +} + +Error DumpOutputStyle::dumpTypesFromObjectFile() { + LazyRandomTypeCollection Types(100); + + for (const auto &S : getObj().sections()) { + Expected<StringRef> NameOrErr = S.getName(); + if (!NameOrErr) + return NameOrErr.takeError(); + StringRef SectionName = *NameOrErr; + + // .debug$T is a standard CodeView type section, while .debug$P is the same + // format but used for MSVC precompiled header object files. + if (SectionName == ".debug$T") + printHeader(P, "Types (.debug$T)"); + else if (SectionName == ".debug$P") + printHeader(P, "Precompiled Types (.debug$P)"); + else + continue; + + Expected<StringRef> ContentsOrErr = S.getContents(); + if (!ContentsOrErr) + return ContentsOrErr.takeError(); + + uint32_t Magic; + BinaryStreamReader Reader(*ContentsOrErr, llvm::support::little); + if (auto EC = Reader.readInteger(Magic)) + return EC; + if (Magic != COFF::DEBUG_SECTION_MAGIC) + return make_error<StringError>("Invalid CodeView debug section.", + inconvertibleErrorCode()); + + Types.reset(Reader, 100); + + if (opts::dump::DumpTypes) { + dumpFullTypeStream(P, Types, RefTracker.get(), 0, 0, {}, nullptr, + opts::dump::DumpTypeData, false); + } else if (opts::dump::DumpTypeExtras) { + auto LocalHashes = LocallyHashedType::hashTypeCollection(Types); + auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types); + assert(LocalHashes.size() == GlobalHashes.size()); + + P.formatLine("Local / Global hashes:"); + TypeIndex TI(TypeIndex::FirstNonSimpleIndex); + for (auto H : zip(LocalHashes, GlobalHashes)) { + AutoIndent Indent2(P); + LocallyHashedType &L = std::get<0>(H); + GloballyHashedType &G = std::get<1>(H); + + P.formatLine("TI: {0}, LocalHash: {1:X}, GlobalHash: {2}", TI, L, G); + + ++TI; + } + P.NewLine(); + } + } + + return Error::success(); +} + +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)"); + } + + assert(!File.isObj()); + + bool Present = false; + bool DumpTypes = false; + bool DumpBytes = false; + bool DumpExtras = false; + std::vector<uint32_t> Indices; + if (StreamIdx == StreamTPI) { + 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) { + Present = getPdb().hasPDBIpiStream(); + DumpTypes = opts::dump::DumpIds; + DumpBytes = opts::dump::DumpIdData; + DumpExtras = opts::dump::DumpIdExtras; + Indices.assign(opts::dump::DumpIdIndex.begin(), + opts::dump::DumpIdIndex.end()); + } + + if (!Present) { + printStreamNotPresent(StreamIdx == StreamTPI ? "TPI" : "IPI"); + return Error::success(); + } + + AutoIndent Indent(P); + ExitOnError Err("Unexpected error processing types: "); + + auto &Stream = Err((StreamIdx == StreamTPI) ? getPdb().getPDBTpiStream() + : getPdb().getPDBIpiStream()); + + auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids(); + + // Only emit notes about referenced/unreferenced for types. + TypeReferenceTracker *MaybeTracker = + (StreamIdx == StreamTPI) ? RefTracker.get() : nullptr; + + // Enable resolving forward decls. + Stream.buildHashMap(); + + if (DumpTypes || !Indices.empty()) { + if (Indices.empty()) + dumpFullTypeStream(P, Types, MaybeTracker, Stream.getNumTypeRecords(), + Stream.getNumHashBuckets(), Stream.getHashValues(), + &Stream, DumpBytes, DumpExtras); + else { + std::vector<TypeIndex> TiList(Indices.begin(), Indices.end()); + dumpPartialTypeStream(P, Types, MaybeTracker, Stream, TiList, DumpBytes, + DumpExtras, opts::dump::DumpTypeDependents); + } + } + + if (DumpExtras) { + P.NewLine(); + + P.formatLine("Header Version: {0}", + static_cast<uint32_t>(Stream.getTpiVersion())); + P.formatLine("Hash Stream Index: {0}", Stream.getTypeHashStreamIndex()); + P.formatLine("Aux Hash Stream Index: {0}", + Stream.getTypeHashStreamAuxIndex()); + P.formatLine("Hash Key Size: {0}", Stream.getHashKeySize()); + P.formatLine("Num Hash Buckets: {0}", Stream.getNumHashBuckets()); + + auto IndexOffsets = Stream.getTypeIndexOffsets(); + P.formatLine("Type Index Offsets:"); + for (const auto &IO : IndexOffsets) { + AutoIndent Indent2(P); + P.formatLine("TI: {0}, Offset: {1}", IO.Type, fmtle(IO.Offset)); + } + + if (getPdb().hasPDBStringTable()) { + P.NewLine(); + P.formatLine("Hash Adjusters:"); + auto &Adjusters = Stream.getHashAdjusters(); + auto &Strings = Err(getPdb().getStringTable()); + for (const auto &A : Adjusters) { + AutoIndent Indent2(P); + auto ExpectedStr = Strings.getStringForID(A.first); + TypeIndex TI(A.second); + if (ExpectedStr) + P.formatLine("`{0}` -> {1}", *ExpectedStr, TI); + else { + P.formatLine("unknown str id ({0}) -> {1}", A.first, TI); + consumeError(ExpectedStr.takeError()); + } + } + } + } + return Error::success(); +} + +Error DumpOutputStyle::dumpModuleSymsForObj() { + printHeader(P, "Symbols"); + + AutoIndent Indent(P); + + ExitOnError Err("Unexpected error processing symbols: "); + + 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) { + Dumper.setSymbolGroup(&Strings); + for (auto Symbol : Symbols) { + if (auto EC = Visitor.visitSymbolRecord(Symbol)) { + SymbolError = std::make_unique<Error>(std::move(EC)); + return; + } + } + }); + + if (SymbolError) + return std::move(*SymbolError); + + return Error::success(); +} + +Error DumpOutputStyle::dumpModuleSymsForPdb() { + printHeader(P, "Symbols"); + + if (File.isPdb() && !getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + + AutoIndent Indent(P); + ExitOnError Err("Unexpected error processing symbols: "); + + auto &Ids = File.ids(); + auto &Types = File.types(); + + 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())); + return; + } + + ModuleDebugStreamRef &ModS = *ExpectedModS; + + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); + MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Strings, + Ids, Types); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Dumper); + CVSymbolVisitor Visitor(Pipeline); + auto SS = ModS.getSymbolsSubstream(); + if (auto EC = + Visitor.visitSymbolStream(ModS.getSymbolArray(), SS.Offset)) { + P.formatLine("Error while processing symbol records. {0}", + toString(std::move(EC))); + return; + } + }); + return Error::success(); +} + +Error DumpOutputStyle::dumpTypeRefStats() { + printHeader(P, "Type Reference Statistics"); + AutoIndent Indent(P); + + // Sum the byte size of all type records, and the size and count of all + // referenced records. + size_t TotalRecs = File.types().size(); + size_t RefRecs = 0; + size_t TotalBytes = 0; + size_t RefBytes = 0; + auto &Types = File.types(); + for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI)) { + CVType Type = File.types().getType(*TI); + TotalBytes += Type.length(); + if (RefTracker->isTypeReferenced(*TI)) { + ++RefRecs; + RefBytes += Type.length(); + } + } + + P.formatLine("Records referenced: {0:N} / {1:N} {2:P}", RefRecs, TotalRecs, + (double)RefRecs / TotalRecs); + P.formatLine("Bytes referenced: {0:N} / {1:N} {2:P}", RefBytes, TotalBytes, + (double)RefBytes / TotalBytes); + + return Error::success(); +} + +Error DumpOutputStyle::dumpGSIRecords() { + printHeader(P, "GSI Records"); + + if (File.isObj()) { + printStreamNotValidForObj(); + return Error::success(); + } + + if (!getPdb().hasPDBSymbolStream()) { + printStreamNotPresent("GSI Common Symbol"); + return Error::success(); + } + + AutoIndent Indent(P); + + auto &Records = cantFail(getPdb().getPDBSymbolStream()); + auto &Types = File.types(); + auto &Ids = File.ids(); + + P.printLine("Records"); + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); + MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Dumper); + CVSymbolVisitor Visitor(Pipeline); + + BinaryStreamRef SymStream = Records.getSymbolArray().getUnderlyingStream(); + if (auto E = Visitor.visitSymbolStream(Records.getSymbolArray(), 0)) + return E; + return Error::success(); +} + +Error DumpOutputStyle::dumpGlobals() { + printHeader(P, "Global Symbols"); + + if (File.isObj()) { + printStreamNotValidForObj(); + return Error::success(); + } + + if (!getPdb().hasPDBGlobalsStream()) { + printStreamNotPresent("Globals"); + return Error::success(); + } + + AutoIndent Indent(P); + ExitOnError Err("Error dumping globals stream: "); + auto &Globals = Err(getPdb().getPDBGlobalsStream()); + + if (opts::dump::DumpGlobalNames.empty()) { + const GSIHashTable &Table = Globals.getGlobalsTable(); + Err(dumpSymbolsFromGSI(Table, opts::dump::DumpGlobalExtras)); + } else { + SymbolStream &SymRecords = cantFail(getPdb().getPDBSymbolStream()); + auto &Types = File.types(); + auto &Ids = File.ids(); + + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); + MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Dumper); + CVSymbolVisitor Visitor(Pipeline); + + using ResultEntryType = std::pair<uint32_t, CVSymbol>; + for (StringRef Name : opts::dump::DumpGlobalNames) { + AutoIndent Indent(P); + P.formatLine("Global Name `{0}`", Name); + std::vector<ResultEntryType> Results = + Globals.findRecordsByName(Name, SymRecords); + if (Results.empty()) { + AutoIndent Indent(P); + P.printLine("(no matching records found)"); + continue; + } + + for (ResultEntryType Result : Results) { + if (auto E = Visitor.visitSymbolRecord(Result.second, Result.first)) + return E; + } + } + } + return Error::success(); +} + +Error DumpOutputStyle::dumpPublics() { + printHeader(P, "Public Symbols"); + + if (File.isObj()) { + printStreamNotValidForObj(); + return Error::success(); + } + + if (!getPdb().hasPDBPublicsStream()) { + printStreamNotPresent("Publics"); + return Error::success(); + } + + AutoIndent Indent(P); + ExitOnError Err("Error dumping publics stream: "); + auto &Publics = Err(getPdb().getPDBPublicsStream()); + + const GSIHashTable &PublicsTable = Publics.getPublicsTable(); + if (opts::dump::DumpPublicExtras) { + P.printLine("Publics Header"); + AutoIndent Indent(P); + P.formatLine("sym hash = {0}, thunk table addr = {1}", Publics.getSymHash(), + formatSegmentOffset(Publics.getThunkTableSection(), + Publics.getThunkTableOffset())); + } + Err(dumpSymbolsFromGSI(PublicsTable, opts::dump::DumpPublicExtras)); + + // Skip the rest if we aren't dumping extras. + if (!opts::dump::DumpPublicExtras) + return Error::success(); + + P.formatLine("Address Map"); + { + // These are offsets into the publics stream sorted by secidx:secrel. + AutoIndent Indent2(P); + for (uint32_t Addr : Publics.getAddressMap()) + P.formatLine("off = {0}", Addr); + } + + // The thunk map is optional debug info used for ILT thunks. + if (!Publics.getThunkMap().empty()) { + P.formatLine("Thunk Map"); + AutoIndent Indent2(P); + for (uint32_t Addr : Publics.getThunkMap()) + P.formatLine("{0:x8}", Addr); + } + + // The section offsets table appears to be empty when incremental linking + // isn't in use. + if (!Publics.getSectionOffsets().empty()) { + P.formatLine("Section Offsets"); + AutoIndent Indent2(P); + for (const SectionOffset &SO : Publics.getSectionOffsets()) + P.formatLine("{0:x4}:{1:x8}", uint16_t(SO.Isect), uint32_t(SO.Off)); + } + + return Error::success(); +} + +Error DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable &Table, + bool HashExtras) { + auto ExpectedSyms = getPdb().getPDBSymbolStream(); + if (!ExpectedSyms) + return ExpectedSyms.takeError(); + auto &Types = File.types(); + auto &Ids = File.ids(); + + if (HashExtras) { + P.printLine("GSI Header"); + AutoIndent Indent(P); + P.formatLine("sig = {0:X}, hdr = {1:X}, hr size = {2}, num buckets = {3}", + Table.getVerSignature(), Table.getVerHeader(), + Table.getHashRecordSize(), Table.getNumBuckets()); + } + + { + P.printLine("Records"); + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); + MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Dumper); + CVSymbolVisitor Visitor(Pipeline); + + + BinaryStreamRef SymStream = + ExpectedSyms->getSymbolArray().getUnderlyingStream(); + for (uint32_t PubSymOff : Table) { + Expected<CVSymbol> Sym = readSymbolFromStream(SymStream, PubSymOff); + if (!Sym) + return Sym.takeError(); + if (auto E = Visitor.visitSymbolRecord(*Sym, PubSymOff)) + return E; + } + } + + // Return early if we aren't dumping public hash table and address map info. + if (HashExtras) { + P.formatLine("Hash Entries"); + { + AutoIndent Indent2(P); + for (const PSHashRecord &HR : Table.HashRecords) + P.formatLine("off = {0}, refcnt = {1}", uint32_t(HR.Off), + uint32_t(HR.CRef)); + } + + P.formatLine("Hash Buckets"); + { + AutoIndent Indent2(P); + for (uint32_t Hash : Table.HashBuckets) + P.formatLine("{0:x8}", Hash); + } + } + + return Error::success(); +} + +static std::string formatSegMapDescriptorFlag(uint32_t IndentLevel, + OMFSegDescFlags Flags) { + std::vector<std::string> Opts; + if (Flags == OMFSegDescFlags::None) + return "none"; + + PUSH_FLAG(OMFSegDescFlags, Read, Flags, "read"); + PUSH_FLAG(OMFSegDescFlags, Write, Flags, "write"); + PUSH_FLAG(OMFSegDescFlags, Execute, Flags, "execute"); + PUSH_FLAG(OMFSegDescFlags, AddressIs32Bit, Flags, "32 bit addr"); + PUSH_FLAG(OMFSegDescFlags, IsSelector, Flags, "selector"); + PUSH_FLAG(OMFSegDescFlags, IsAbsoluteAddress, Flags, "absolute addr"); + PUSH_FLAG(OMFSegDescFlags, IsGroup, Flags, "group"); + return typesetItemList(Opts, IndentLevel, 4, " | "); +} + +Error DumpOutputStyle::dumpSectionHeaders() { + dumpSectionHeaders("Section Headers", DbgHeaderType::SectionHdr); + dumpSectionHeaders("Original Section Headers", DbgHeaderType::SectionHdrOrig); + return Error::success(); +} + +void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) { + printHeader(P, Label); + + if (File.isObj()) { + printStreamNotValidForObj(); + return; + } + + if (!getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return; + } + + AutoIndent Indent(P); + ExitOnError Err("Error dumping section headers: "); + std::unique_ptr<MappedBlockStream> Stream; + ArrayRef<object::coff_section> Headers; + auto ExpectedHeaders = loadSectionHeaders(getPdb(), Type); + if (!ExpectedHeaders) { + P.printLine(toString(ExpectedHeaders.takeError())); + return; + } + std::tie(Stream, Headers) = std::move(*ExpectedHeaders); + + uint32_t I = 1; + for (const auto &Header : Headers) { + P.NewLine(); + P.formatLine("SECTION HEADER #{0}", I); + P.formatLine("{0,8} name", Header.Name); + P.formatLine("{0,8:X-} virtual size", uint32_t(Header.VirtualSize)); + P.formatLine("{0,8:X-} virtual address", uint32_t(Header.VirtualAddress)); + P.formatLine("{0,8:X-} size of raw data", uint32_t(Header.SizeOfRawData)); + P.formatLine("{0,8:X-} file pointer to raw data", + uint32_t(Header.PointerToRawData)); + P.formatLine("{0,8:X-} file pointer to relocation table", + uint32_t(Header.PointerToRelocations)); + P.formatLine("{0,8:X-} file pointer to line numbers", + uint32_t(Header.PointerToLinenumbers)); + P.formatLine("{0,8:X-} number of relocations", + uint32_t(Header.NumberOfRelocations)); + P.formatLine("{0,8:X-} number of line numbers", + uint32_t(Header.NumberOfLinenumbers)); + P.formatLine("{0,8:X-} flags", uint32_t(Header.Characteristics)); + AutoIndent IndentMore(P, 9); + P.formatLine("{0}", formatSectionCharacteristics( + P.getIndentLevel(), Header.Characteristics, 1, "")); + ++I; + } +} + +Error DumpOutputStyle::dumpSectionContribs() { + printHeader(P, "Section Contributions"); + + if (File.isObj()) { + printStreamNotValidForObj(); + return Error::success(); + } + + if (!getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + + AutoIndent Indent(P); + ExitOnError Err("Error dumping section contributions: "); + + auto &Dbi = Err(getPdb().getPDBDbiStream()); + + class Visitor : public ISectionContribVisitor { + public: + Visitor(LinePrinter &P, ArrayRef<std::string> Names) : P(P), Names(Names) { + auto Max = std::max_element( + Names.begin(), Names.end(), + [](StringRef S1, StringRef S2) { return S1.size() < S2.size(); }); + MaxNameLen = (Max == Names.end() ? 0 : Max->size()); + } + void visit(const SectionContrib &SC) override { + dumpSectionContrib(P, SC, Names, MaxNameLen); + } + void visit(const SectionContrib2 &SC) override { + dumpSectionContrib(P, SC, Names, MaxNameLen); + } + + private: + LinePrinter &P; + uint32_t MaxNameLen; + ArrayRef<std::string> Names; + }; + + 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"); + + if (File.isObj()) { + printStreamNotValidForObj(); + return Error::success(); + } + + if (!getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + + AutoIndent Indent(P); + ExitOnError Err("Error dumping section map: "); + + auto &Dbi = Err(getPdb().getPDBDbiStream()); + + uint32_t I = 0; + for (auto &M : Dbi.getSectionMap()) { + P.formatLine( + "Section {0:4} | ovl = {1}, group = {2}, frame = {3}, name = {4}", I, + fmtle(M.Ovl), fmtle(M.Group), fmtle(M.Frame), fmtle(M.SecName)); + P.formatLine(" class = {0}, offset = {1}, size = {2}", + fmtle(M.ClassName), fmtle(M.Offset), fmtle(M.SecByteLength)); + P.formatLine(" flags = {0}", + formatSegMapDescriptorFlag( + P.getIndentLevel() + 13, + static_cast<OMFSegDescFlags>(uint16_t(M.Flags)))); + ++I; + } + return Error::success(); +} |