Path: blob/main/contrib/llvm-project/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp
35259 views
//===- DumpOutputStyle.cpp ------------------------------------ *- C++ --*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "DumpOutputStyle.h"910#include "MinimalSymbolDumper.h"11#include "MinimalTypeDumper.h"12#include "StreamUtil.h"13#include "TypeReferenceTracker.h"14#include "llvm-pdbutil.h"1516#include "llvm/ADT/STLExtras.h"17#include "llvm/ADT/StringExtras.h"18#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h"19#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"20#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h"21#include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h"22#include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h"23#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h"24#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h"25#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h"26#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h"27#include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h"28#include "llvm/DebugInfo/CodeView/Formatters.h"29#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"30#include "llvm/DebugInfo/CodeView/Line.h"31#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"32#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h"33#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h"34#include "llvm/DebugInfo/CodeView/TypeHashing.h"35#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"36#include "llvm/DebugInfo/MSF/MappedBlockStream.h"37#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"38#include "llvm/DebugInfo/PDB/Native/DbiStream.h"39#include "llvm/DebugInfo/PDB/Native/FormatUtil.h"40#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"41#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h"42#include "llvm/DebugInfo/PDB/Native/InfoStream.h"43#include "llvm/DebugInfo/PDB/Native/InputFile.h"44#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"45#include "llvm/DebugInfo/PDB/Native/NativeSession.h"46#include "llvm/DebugInfo/PDB/Native/PDBFile.h"47#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"48#include "llvm/DebugInfo/PDB/Native/RawError.h"49#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"50#include "llvm/DebugInfo/PDB/Native/TpiHashing.h"51#include "llvm/DebugInfo/PDB/Native/TpiStream.h"52#include "llvm/Object/COFF.h"53#include "llvm/Support/BinaryStreamReader.h"54#include "llvm/Support/FormatAdapters.h"55#include "llvm/Support/FormatVariadic.h"5657#include <cctype>5859using namespace llvm;60using namespace llvm::codeview;61using namespace llvm::msf;62using namespace llvm::pdb;6364DumpOutputStyle::DumpOutputStyle(InputFile &File)65: File(File), P(2, false, outs(), opts::Filters) {66if (opts::dump::DumpTypeRefStats)67RefTracker.reset(new TypeReferenceTracker(File));68}6970DumpOutputStyle::~DumpOutputStyle() {}7172PDBFile &DumpOutputStyle::getPdb() { return File.pdb(); }73object::COFFObjectFile &DumpOutputStyle::getObj() { return File.obj(); }7475void DumpOutputStyle::printStreamNotValidForObj() {76AutoIndent Indent(P, 4);77P.formatLine("Dumping this stream is not valid for object files");78}7980void DumpOutputStyle::printStreamNotPresent(StringRef StreamName) {81AutoIndent Indent(P, 4);82P.formatLine("{0} stream not present", StreamName);83}8485Error DumpOutputStyle::dump() {86// Walk symbols & globals if we are supposed to mark types referenced.87if (opts::dump::DumpTypeRefStats)88RefTracker->mark();8990if (opts::dump::DumpSummary) {91if (auto EC = dumpFileSummary())92return EC;93P.NewLine();94}9596if (opts::dump::DumpStreams) {97if (auto EC = dumpStreamSummary())98return EC;99P.NewLine();100}101102if (opts::dump::DumpSymbolStats) {103ExitOnError Err("Unexpected error processing module stats: ");104Err(dumpSymbolStats());105P.NewLine();106}107108if (opts::dump::DumpUdtStats) {109if (auto EC = dumpUdtStats())110return EC;111P.NewLine();112}113114if (opts::dump::DumpTypeStats || opts::dump::DumpIDStats) {115if (auto EC = dumpTypeStats())116return EC;117P.NewLine();118}119120if (opts::dump::DumpNamedStreams) {121if (auto EC = dumpNamedStreams())122return EC;123P.NewLine();124}125126if (opts::dump::DumpStringTable || opts::dump::DumpStringTableDetails) {127if (auto EC = dumpStringTable())128return EC;129P.NewLine();130}131132if (opts::dump::DumpModules) {133ExitOnError Err("Unexpected error processing modules: ");134Err(dumpModules());135}136137if (opts::dump::DumpModuleFiles) {138ExitOnError Err("Unexpected error processing files: ");139Err(dumpModuleFiles());140}141142if (opts::dump::DumpLines) {143ExitOnError Err("Unexpected error processing lines: ");144Err(dumpLines());145}146147if (opts::dump::DumpInlineeLines) {148ExitOnError Err("Unexpected error processing inlinee lines: ");149Err(dumpInlineeLines());150}151152if (opts::dump::DumpXmi) {153ExitOnError Err("Unexpected error processing cross module imports: ");154Err(dumpXmi());155}156157if (opts::dump::DumpXme) {158ExitOnError Err("Unexpected error processing cross module exports: ");159Err(dumpXme());160}161162if (opts::dump::DumpFpo) {163if (auto EC = dumpFpo())164return EC;165}166167if (File.isObj()) {168if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() ||169opts::dump::DumpTypeExtras)170if (auto EC = dumpTypesFromObjectFile())171return EC;172} else {173if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() ||174opts::dump::DumpTypeExtras) {175if (auto EC = dumpTpiStream(StreamTPI))176return EC;177}178179if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() ||180opts::dump::DumpIdExtras) {181if (auto EC = dumpTpiStream(StreamIPI))182return EC;183}184}185186if (opts::dump::DumpGSIRecords) {187if (auto EC = dumpGSIRecords())188return EC;189}190191if (opts::dump::DumpGlobals) {192if (auto EC = dumpGlobals())193return EC;194}195196if (opts::dump::DumpPublics) {197if (auto EC = dumpPublics())198return EC;199}200201if (opts::dump::DumpSymbols) {202ExitOnError Err("Unexpected error processing symbols: ");203Err(File.isPdb() ? dumpModuleSymsForPdb() : dumpModuleSymsForObj());204}205206if (opts::dump::DumpTypeRefStats) {207if (auto EC = dumpTypeRefStats())208return EC;209}210211if (opts::dump::DumpSectionHeaders) {212if (auto EC = dumpSectionHeaders())213return EC;214}215216if (opts::dump::DumpSectionContribs) {217if (auto EC = dumpSectionContribs())218return EC;219}220221if (opts::dump::DumpSectionMap) {222if (auto EC = dumpSectionMap())223return EC;224}225226P.NewLine();227228return Error::success();229}230231static void printHeader(LinePrinter &P, const Twine &S) {232P.NewLine();233P.formatLine("{0,=60}", S);234P.formatLine("{0}", fmt_repeat('=', 60));235}236237Error DumpOutputStyle::dumpFileSummary() {238printHeader(P, "Summary");239240if (File.isObj()) {241printStreamNotValidForObj();242return Error::success();243}244245AutoIndent Indent(P);246ExitOnError Err("Invalid PDB Format: ");247248P.formatLine("Block Size: {0}", getPdb().getBlockSize());249P.formatLine("Number of blocks: {0}", getPdb().getBlockCount());250P.formatLine("Number of streams: {0}", getPdb().getNumStreams());251252auto &PS = Err(getPdb().getPDBInfoStream());253P.formatLine("Signature: {0}", PS.getSignature());254P.formatLine("Age: {0}", PS.getAge());255P.formatLine("GUID: {0}", fmt_guid(PS.getGuid().Guid));256P.formatLine("Features: {0:x+}", static_cast<uint32_t>(PS.getFeatures()));257P.formatLine("Has Debug Info: {0}", getPdb().hasPDBDbiStream());258P.formatLine("Has Types: {0}", getPdb().hasPDBTpiStream());259P.formatLine("Has IDs: {0}", getPdb().hasPDBIpiStream());260P.formatLine("Has Globals: {0}", getPdb().hasPDBGlobalsStream());261P.formatLine("Has Publics: {0}", getPdb().hasPDBPublicsStream());262if (getPdb().hasPDBDbiStream()) {263DbiStream &DBI = Err(getPdb().getPDBDbiStream());264P.formatLine("Is incrementally linked: {0}", DBI.isIncrementallyLinked());265P.formatLine("Has conflicting types: {0}", DBI.hasCTypes());266P.formatLine("Is stripped: {0}", DBI.isStripped());267}268269return Error::success();270}271272static StatCollection getSymbolStats(const SymbolGroup &SG,273StatCollection &CumulativeStats) {274StatCollection Stats;275if (SG.getFile().isPdb() && SG.hasDebugStream()) {276// For PDB files, all symbols are packed into one stream.277for (const auto &S : SG.getPdbModuleStream().symbols(nullptr)) {278Stats.update(S.kind(), S.length());279CumulativeStats.update(S.kind(), S.length());280}281return Stats;282}283284for (const auto &SS : SG.getDebugSubsections()) {285// For object files, all symbols are spread across multiple Symbol286// subsections of a given .debug$S section.287if (SS.kind() != DebugSubsectionKind::Symbols)288continue;289DebugSymbolsSubsectionRef Symbols;290BinaryStreamReader Reader(SS.getRecordData());291cantFail(Symbols.initialize(Reader));292for (const auto &S : Symbols) {293Stats.update(S.kind(), S.length());294CumulativeStats.update(S.kind(), S.length());295}296}297return Stats;298}299300static StatCollection getChunkStats(const SymbolGroup &SG,301StatCollection &CumulativeStats) {302StatCollection Stats;303for (const auto &Chunk : SG.getDebugSubsections()) {304Stats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength());305CumulativeStats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength());306}307return Stats;308}309310static inline std::string formatModuleDetailKind(DebugSubsectionKind K) {311return formatChunkKind(K, false);312}313314static inline std::string formatModuleDetailKind(SymbolKind K) {315return formatSymbolKind(K);316}317318// Get the stats sorted by size, descending.319std::vector<StatCollection::KindAndStat>320StatCollection::getStatsSortedBySize() const {321std::vector<KindAndStat> SortedStats(Individual.begin(), Individual.end());322llvm::stable_sort(SortedStats,323[](const KindAndStat &LHS, const KindAndStat &RHS) {324return LHS.second.Size > RHS.second.Size;325});326return SortedStats;327}328329template <typename Kind>330static void printModuleDetailStats(LinePrinter &P, StringRef Label,331const StatCollection &Stats) {332P.NewLine();333P.formatLine(" {0}", Label);334AutoIndent Indent(P);335P.formatLine("{0,40}: {1,7} entries ({2,12:N} bytes)", "Total",336Stats.Totals.Count, Stats.Totals.Size);337P.formatLine("{0}", fmt_repeat('-', 74));338339for (const auto &K : Stats.getStatsSortedBySize()) {340std::string KindName = formatModuleDetailKind(Kind(K.first));341P.formatLine("{0,40}: {1,7} entries ({2,12:N} bytes)", KindName,342K.second.Count, K.second.Size);343}344}345346Error DumpOutputStyle::dumpStreamSummary() {347printHeader(P, "Streams");348349if (File.isObj()) {350printStreamNotValidForObj();351return Error::success();352}353354AutoIndent Indent(P);355356if (StreamPurposes.empty())357discoverStreamPurposes(getPdb(), StreamPurposes);358359uint32_t StreamCount = getPdb().getNumStreams();360uint32_t MaxStreamSize = getPdb().getMaxStreamSize();361362for (uint32_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {363P.formatLine(364"Stream {0} ({1} bytes): [{2}]",365fmt_align(StreamIdx, AlignStyle::Right, NumDigits(StreamCount)),366fmt_align(getPdb().getStreamByteSize(StreamIdx), AlignStyle::Right,367NumDigits(MaxStreamSize)),368StreamPurposes[StreamIdx].getLongName());369370if (opts::dump::DumpStreamBlocks) {371auto Blocks = getPdb().getStreamBlockList(StreamIdx);372std::vector<uint32_t> BV(Blocks.begin(), Blocks.end());373P.formatLine(" {0} Blocks: [{1}]",374fmt_repeat(' ', NumDigits(StreamCount)),375make_range(BV.begin(), BV.end()));376}377}378379return Error::success();380}381382static Expected<std::pair<std::unique_ptr<MappedBlockStream>,383ArrayRef<llvm::object::coff_section>>>384loadSectionHeaders(PDBFile &File, DbgHeaderType Type) {385if (!File.hasPDBDbiStream())386return make_error<StringError>(387"Section headers require a DBI Stream, which could not be loaded",388inconvertibleErrorCode());389390DbiStream &Dbi = cantFail(File.getPDBDbiStream());391uint32_t SI = Dbi.getDebugStreamIndex(Type);392393if (SI == kInvalidStreamIndex)394return make_error<StringError>(395"PDB does not contain the requested image section header type",396inconvertibleErrorCode());397398auto Stream = File.createIndexedStream(SI);399if (!Stream)400return make_error<StringError>("Could not load the required stream data",401inconvertibleErrorCode());402403ArrayRef<object::coff_section> Headers;404if (Stream->getLength() % sizeof(object::coff_section) != 0)405return make_error<StringError>(406"Section header array size is not a multiple of section header size",407inconvertibleErrorCode());408409uint32_t NumHeaders = Stream->getLength() / sizeof(object::coff_section);410BinaryStreamReader Reader(*Stream);411cantFail(Reader.readArray(Headers, NumHeaders));412return std::make_pair(std::move(Stream), Headers);413}414415static Expected<std::vector<std::string>> getSectionNames(PDBFile &File) {416auto ExpectedHeaders = loadSectionHeaders(File, DbgHeaderType::SectionHdr);417if (!ExpectedHeaders)418return ExpectedHeaders.takeError();419420std::unique_ptr<MappedBlockStream> Stream;421ArrayRef<object::coff_section> Headers;422std::tie(Stream, Headers) = std::move(*ExpectedHeaders);423std::vector<std::string> Names;424for (const auto &H : Headers)425Names.push_back(H.Name);426return Names;427}428429static void dumpSectionContrib(LinePrinter &P, const SectionContrib &SC,430ArrayRef<std::string> SectionNames,431uint32_t FieldWidth) {432std::string NameInsert;433if (SC.ISect > 0 && SC.ISect <= SectionNames.size()) {434StringRef SectionName = SectionNames[SC.ISect - 1];435NameInsert = formatv("[{0}]", SectionName).str();436} else437NameInsert = "[???]";438P.formatLine("SC{5} | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "439"crc = {4}",440formatSegmentOffset(SC.ISect, SC.Off), fmtle(SC.Size),441fmtle(SC.Imod), fmtle(SC.DataCrc), fmtle(SC.RelocCrc),442fmt_align(NameInsert, AlignStyle::Left, FieldWidth + 2));443AutoIndent Indent(P, FieldWidth + 2);444P.formatLine(" {0}",445formatSectionCharacteristics(P.getIndentLevel() + 6,446SC.Characteristics, 3, " | "));447}448449static void dumpSectionContrib(LinePrinter &P, const SectionContrib2 &SC,450ArrayRef<std::string> SectionNames,451uint32_t FieldWidth) {452P.formatLine("SC2[{6}] | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "453"crc = {4}, coff section = {5}",454formatSegmentOffset(SC.Base.ISect, SC.Base.Off),455fmtle(SC.Base.Size), fmtle(SC.Base.Imod), fmtle(SC.Base.DataCrc),456fmtle(SC.Base.RelocCrc), fmtle(SC.ISectCoff));457P.formatLine(" {0}",458formatSectionCharacteristics(P.getIndentLevel() + 6,459SC.Base.Characteristics, 3, " | "));460}461462Error DumpOutputStyle::dumpModules() {463printHeader(P, "Modules");464465if (File.isObj()) {466printStreamNotValidForObj();467return Error::success();468}469470if (!getPdb().hasPDBDbiStream()) {471printStreamNotPresent("DBI");472return Error::success();473}474475AutoIndent Indent(P);476477Expected<DbiStream &> StreamOrErr = getPdb().getPDBDbiStream();478if (!StreamOrErr)479return StreamOrErr.takeError();480DbiStream &Stream = *StreamOrErr;481482const DbiModuleList &Modules = Stream.modules();483return iterateSymbolGroups(484File, PrintScope{P, 11},485[&](uint32_t Modi, const SymbolGroup &Strings) -> Error {486auto Desc = Modules.getModuleDescriptor(Modi);487if (opts::dump::DumpSectionContribs) {488auto SectionsOrErr = getSectionNames(getPdb());489if (!SectionsOrErr)490return SectionsOrErr.takeError();491ArrayRef<std::string> Sections = *SectionsOrErr;492dumpSectionContrib(P, Desc.getSectionContrib(), Sections, 0);493}494P.formatLine("Obj: `{0}`: ", Desc.getObjFileName());495P.formatLine("debug stream: {0}, # files: {1}, has ec info: {2}",496Desc.getModuleStreamIndex(), Desc.getNumberOfFiles(),497Desc.hasECInfo());498499auto PdbPathOrErr = Stream.getECName(Desc.getPdbFilePathNameIndex());500if (!PdbPathOrErr)501return PdbPathOrErr.takeError();502StringRef PdbFilePath = *PdbPathOrErr;503504auto SrcPathOrErr = Stream.getECName(Desc.getSourceFileNameIndex());505if (!SrcPathOrErr)506return SrcPathOrErr.takeError();507StringRef SrcFilePath = *SrcPathOrErr;508509P.formatLine("pdb file ni: {0} `{1}`, src file ni: {2} `{3}`",510Desc.getPdbFilePathNameIndex(), PdbFilePath,511Desc.getSourceFileNameIndex(), SrcFilePath);512return Error::success();513});514}515516Error DumpOutputStyle::dumpModuleFiles() {517printHeader(P, "Files");518519if (File.isObj()) {520printStreamNotValidForObj();521return Error::success();522}523524if (!getPdb().hasPDBDbiStream()) {525printStreamNotPresent("DBI");526return Error::success();527}528529return iterateSymbolGroups(530File, PrintScope{P, 11},531[this](uint32_t Modi, const SymbolGroup &Strings) -> Error {532Expected<DbiStream &> StreamOrErr = getPdb().getPDBDbiStream();533if (!StreamOrErr)534return StreamOrErr.takeError();535DbiStream &Stream = *StreamOrErr;536537const DbiModuleList &Modules = Stream.modules();538for (const auto &F : Modules.source_files(Modi)) {539Strings.formatFromFileName(P, F);540}541return Error::success();542});543}544545Error DumpOutputStyle::dumpSymbolStats() {546printHeader(P, "Module Stats");547548if (File.isPdb() && !getPdb().hasPDBDbiStream()) {549printStreamNotPresent("DBI");550return Error::success();551}552553StatCollection SymStats;554StatCollection ChunkStats;555PrintScope Scope(P, 2);556557if (Error Err = iterateSymbolGroups(558File, Scope, [&](uint32_t Modi, const SymbolGroup &SG) -> Error {559StatCollection SS = getSymbolStats(SG, SymStats);560StatCollection CS = getChunkStats(SG, ChunkStats);561562if (!SG.getFile().isPdb())563return Error::success();564565AutoIndent Indent(P);566auto Modules = cantFail(File.pdb().getPDBDbiStream()).modules();567uint32_t ModCount = Modules.getModuleCount();568DbiModuleDescriptor Desc = Modules.getModuleDescriptor(Modi);569uint32_t StreamIdx = Desc.getModuleStreamIndex();570571if (StreamIdx == kInvalidStreamIndex) {572P.formatLine(573"Mod {0} (debug info not present): [{1}]",574fmt_align(Modi, AlignStyle::Right, NumDigits(ModCount)),575Desc.getModuleName());576return Error::success();577}578P.formatLine("Stream {0}, {1} bytes", StreamIdx,579getPdb().getStreamByteSize(StreamIdx));580581printModuleDetailStats<SymbolKind>(P, "Symbols", SS);582printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", CS);583584return Error::success();585}))586return Err;587588if (SymStats.Totals.Count > 0) {589P.printLine(" Summary |");590AutoIndent Indent(P, 4);591printModuleDetailStats<SymbolKind>(P, "Symbols", SymStats);592printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", ChunkStats);593}594595return Error::success();596}597598Error DumpOutputStyle::dumpTypeStats() {599printHeader(P, "Type Record Stats");600601// Iterate the types, categorize by kind, accumulate size stats.602StatCollection TypeStats;603LazyRandomTypeCollection &Types =604opts::dump::DumpTypeStats ? File.types() : File.ids();605for (std::optional<TypeIndex> TI = Types.getFirst(); TI;606TI = Types.getNext(*TI)) {607CVType Type = Types.getType(*TI);608TypeStats.update(uint32_t(Type.kind()), Type.length());609}610611P.NewLine();612P.formatLine(" Types");613AutoIndent Indent(P);614P.formatLine("{0,16}: {1,7} entries ({2,12:N} bytes, {3,7} avg)", "Total",615TypeStats.Totals.Count, TypeStats.Totals.Size,616(double)TypeStats.Totals.Size / TypeStats.Totals.Count);617P.formatLine("{0}", fmt_repeat('-', 74));618619for (const auto &K : TypeStats.getStatsSortedBySize()) {620P.formatLine("{0,16}: {1,7} entries ({2,12:N} bytes, {3,7} avg)",621formatTypeLeafKind(TypeLeafKind(K.first)), K.second.Count,622K.second.Size, (double)K.second.Size / K.second.Count);623}624return Error::success();625}626627static bool isValidNamespaceIdentifier(StringRef S) {628if (S.empty())629return false;630631if (std::isdigit(S[0]))632return false;633634return llvm::all_of(S, [](char C) { return std::isalnum(C); });635}636637namespace {638constexpr uint32_t kNoneUdtKind = 0;639constexpr uint32_t kSimpleUdtKind = 1;640constexpr uint32_t kUnknownUdtKind = 2;641} // namespace642643static std::string getUdtStatLabel(uint32_t Kind) {644if (Kind == kNoneUdtKind)645return "<none type>";646647if (Kind == kSimpleUdtKind)648return "<simple type>";649650if (Kind == kUnknownUdtKind)651return "<unknown type>";652653return formatTypeLeafKind(static_cast<TypeLeafKind>(Kind));654}655656static uint32_t getLongestTypeLeafName(const StatCollection &Stats) {657size_t L = 0;658for (const auto &Stat : Stats.Individual) {659std::string Label = getUdtStatLabel(Stat.first);660L = std::max(L, Label.size());661}662return static_cast<uint32_t>(L);663}664665Error DumpOutputStyle::dumpUdtStats() {666printHeader(P, "S_UDT Record Stats");667668if (File.isPdb() && !getPdb().hasPDBGlobalsStream()) {669printStreamNotPresent("Globals");670return Error::success();671}672673StatCollection UdtStats;674StatCollection UdtTargetStats;675AutoIndent Indent(P, 4);676677auto &TpiTypes = File.types();678679StringMap<StatCollection::Stat> NamespacedStats;680681size_t LongestNamespace = 0;682auto HandleOneSymbol = [&](const CVSymbol &Sym) {683if (Sym.kind() != SymbolKind::S_UDT)684return;685UdtStats.update(SymbolKind::S_UDT, Sym.length());686687UDTSym UDT = cantFail(SymbolDeserializer::deserializeAs<UDTSym>(Sym));688689uint32_t Kind = 0;690uint32_t RecordSize = 0;691692if (UDT.Type.isNoneType())693Kind = kNoneUdtKind;694else if (UDT.Type.isSimple())695Kind = kSimpleUdtKind;696else if (std::optional<CVType> T = TpiTypes.tryGetType(UDT.Type)) {697Kind = T->kind();698RecordSize = T->length();699} else700Kind = kUnknownUdtKind;701702UdtTargetStats.update(Kind, RecordSize);703704size_t Pos = UDT.Name.find("::");705if (Pos == StringRef::npos)706return;707708StringRef Scope = UDT.Name.take_front(Pos);709if (Scope.empty() || !isValidNamespaceIdentifier(Scope))710return;711712LongestNamespace = std::max(LongestNamespace, Scope.size());713NamespacedStats[Scope].update(RecordSize);714};715716P.NewLine();717718if (File.isPdb()) {719auto &SymbolRecords = cantFail(getPdb().getPDBSymbolStream());720auto ExpGlobals = getPdb().getPDBGlobalsStream();721if (!ExpGlobals)722return ExpGlobals.takeError();723724for (uint32_t PubSymOff : ExpGlobals->getGlobalsTable()) {725CVSymbol Sym = SymbolRecords.readRecord(PubSymOff);726HandleOneSymbol(Sym);727}728} else {729for (const auto &Sec : File.symbol_groups()) {730for (const auto &SS : Sec.getDebugSubsections()) {731if (SS.kind() != DebugSubsectionKind::Symbols)732continue;733734DebugSymbolsSubsectionRef Symbols;735BinaryStreamReader Reader(SS.getRecordData());736cantFail(Symbols.initialize(Reader));737for (const auto &S : Symbols)738HandleOneSymbol(S);739}740}741}742743LongestNamespace += StringRef(" namespace ''").size();744size_t LongestTypeLeafKind = getLongestTypeLeafName(UdtTargetStats);745size_t FieldWidth = std::max(LongestNamespace, LongestTypeLeafKind);746747// Compute the max number of digits for count and size fields, including comma748// separators.749StringRef CountHeader("Count");750StringRef SizeHeader("Size");751size_t CD = NumDigits(UdtStats.Totals.Count);752CD += (CD - 1) / 3;753CD = std::max(CD, CountHeader.size());754755size_t SD = NumDigits(UdtStats.Totals.Size);756SD += (SD - 1) / 3;757SD = std::max(SD, SizeHeader.size());758759uint32_t TableWidth = FieldWidth + 3 + CD + 2 + SD + 1;760761P.formatLine("{0} | {1} {2}",762fmt_align("Record Kind", AlignStyle::Right, FieldWidth),763fmt_align(CountHeader, AlignStyle::Right, CD),764fmt_align(SizeHeader, AlignStyle::Right, SD));765766P.formatLine("{0}", fmt_repeat('-', TableWidth));767for (const auto &Stat : UdtTargetStats.getStatsSortedBySize()) {768std::string Label = getUdtStatLabel(Stat.first);769P.formatLine("{0} | {1:N} {2:N}",770fmt_align(Label, AlignStyle::Right, FieldWidth),771fmt_align(Stat.second.Count, AlignStyle::Right, CD),772fmt_align(Stat.second.Size, AlignStyle::Right, SD));773}774P.formatLine("{0}", fmt_repeat('-', TableWidth));775P.formatLine("{0} | {1:N} {2:N}",776fmt_align("Total (S_UDT)", AlignStyle::Right, FieldWidth),777fmt_align(UdtStats.Totals.Count, AlignStyle::Right, CD),778fmt_align(UdtStats.Totals.Size, AlignStyle::Right, SD));779P.formatLine("{0}", fmt_repeat('-', TableWidth));780struct StrAndStat {781StringRef Key;782StatCollection::Stat Stat;783};784785// Print namespace stats in descending order of size.786std::vector<StrAndStat> NamespacedStatsSorted;787for (const auto &Stat : NamespacedStats)788NamespacedStatsSorted.push_back({Stat.getKey(), Stat.second});789llvm::stable_sort(NamespacedStatsSorted,790[](const StrAndStat &L, const StrAndStat &R) {791return L.Stat.Size > R.Stat.Size;792});793for (const auto &Stat : NamespacedStatsSorted) {794std::string Label = std::string(formatv("namespace '{0}'", Stat.Key));795P.formatLine("{0} | {1:N} {2:N}",796fmt_align(Label, AlignStyle::Right, FieldWidth),797fmt_align(Stat.Stat.Count, AlignStyle::Right, CD),798fmt_align(Stat.Stat.Size, AlignStyle::Right, SD));799}800return Error::success();801}802803static void typesetLinesAndColumns(LinePrinter &P, uint32_t Start,804const LineColumnEntry &E) {805const uint32_t kMaxCharsPerLineNumber = 4; // 4 digit line number806uint32_t MinColumnWidth = kMaxCharsPerLineNumber + 5;807808// Let's try to keep it under 100 characters809constexpr uint32_t kMaxRowLength = 100;810// At least 3 spaces between columns.811uint32_t ColumnsPerRow = kMaxRowLength / (MinColumnWidth + 3);812uint32_t ItemsLeft = E.LineNumbers.size();813auto LineIter = E.LineNumbers.begin();814while (ItemsLeft != 0) {815uint32_t RowColumns = std::min(ItemsLeft, ColumnsPerRow);816for (uint32_t I = 0; I < RowColumns; ++I) {817LineInfo Line(LineIter->Flags);818std::string LineStr;819if (Line.isAlwaysStepInto())820LineStr = "ASI";821else if (Line.isNeverStepInto())822LineStr = "NSI";823else824LineStr = utostr(Line.getStartLine());825char Statement = Line.isStatement() ? ' ' : '!';826P.format("{0} {1:X-} {2} ",827fmt_align(LineStr, AlignStyle::Right, kMaxCharsPerLineNumber),828fmt_align(Start + LineIter->Offset, AlignStyle::Right, 8, '0'),829Statement);830++LineIter;831--ItemsLeft;832}833P.NewLine();834}835}836837Error DumpOutputStyle::dumpLines() {838printHeader(P, "Lines");839840if (File.isPdb() && !getPdb().hasPDBDbiStream()) {841printStreamNotPresent("DBI");842return Error::success();843}844845uint32_t LastModi = UINT32_MAX;846uint32_t LastNameIndex = UINT32_MAX;847return iterateModuleSubsections<DebugLinesSubsectionRef>(848File, PrintScope{P, 4},849[this, &LastModi,850&LastNameIndex](uint32_t Modi, const SymbolGroup &Strings,851DebugLinesSubsectionRef &Lines) -> Error {852uint16_t Segment = Lines.header()->RelocSegment;853uint32_t Begin = Lines.header()->RelocOffset;854uint32_t End = Begin + Lines.header()->CodeSize;855for (const auto &Block : Lines) {856if (LastModi != Modi || LastNameIndex != Block.NameIndex) {857LastModi = Modi;858LastNameIndex = Block.NameIndex;859Strings.formatFromChecksumsOffset(P, Block.NameIndex);860}861862AutoIndent Indent(P, 2);863P.formatLine("{0:X-4}:{1:X-8}-{2:X-8}, ", Segment, Begin, End);864uint32_t Count = Block.LineNumbers.size();865if (Lines.hasColumnInfo())866P.format("line/column/addr entries = {0}", Count);867else868P.format("line/addr entries = {0}", Count);869870P.NewLine();871typesetLinesAndColumns(P, Begin, Block);872}873return Error::success();874});875}876877Error DumpOutputStyle::dumpInlineeLines() {878printHeader(P, "Inlinee Lines");879880if (File.isPdb() && !getPdb().hasPDBDbiStream()) {881printStreamNotPresent("DBI");882return Error::success();883}884885return iterateModuleSubsections<DebugInlineeLinesSubsectionRef>(886File, PrintScope{P, 2},887[this](uint32_t Modi, const SymbolGroup &Strings,888DebugInlineeLinesSubsectionRef &Lines) -> Error {889P.formatLine("{0,+8} | {1,+5} | {2}", "Inlinee", "Line", "Source File");890for (const auto &Entry : Lines) {891P.formatLine("{0,+8} | {1,+5} | ", Entry.Header->Inlinee,892fmtle(Entry.Header->SourceLineNum));893Strings.formatFromChecksumsOffset(P, Entry.Header->FileID, true);894for (const auto &ExtraFileID : Entry.ExtraFiles) {895P.formatLine(" ");896Strings.formatFromChecksumsOffset(P, ExtraFileID, true);897}898}899P.NewLine();900return Error::success();901});902}903904Error DumpOutputStyle::dumpXmi() {905printHeader(P, "Cross Module Imports");906907if (File.isPdb() && !getPdb().hasPDBDbiStream()) {908printStreamNotPresent("DBI");909return Error::success();910}911912return iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>(913File, PrintScope{P, 2},914[this](uint32_t Modi, const SymbolGroup &Strings,915DebugCrossModuleImportsSubsectionRef &Imports) -> Error {916P.formatLine("{0,=32} | {1}", "Imported Module", "Type IDs");917918for (const auto &Xmi : Imports) {919auto ExpectedModule =920Strings.getNameFromStringTable(Xmi.Header->ModuleNameOffset);921StringRef Module;922SmallString<32> ModuleStorage;923if (!ExpectedModule) {924Module = "(unknown module)";925consumeError(ExpectedModule.takeError());926} else927Module = *ExpectedModule;928if (Module.size() > 32) {929ModuleStorage = "...";930ModuleStorage += Module.take_back(32 - 3);931Module = ModuleStorage;932}933std::vector<std::string> TIs;934for (const auto I : Xmi.Imports)935TIs.push_back(std::string(formatv("{0,+10:X+}", fmtle(I))));936std::string Result =937typesetItemList(TIs, P.getIndentLevel() + 35, 12, " ");938P.formatLine("{0,+32} | {1}", Module, Result);939}940return Error::success();941});942}943944Error DumpOutputStyle::dumpXme() {945printHeader(P, "Cross Module Exports");946947if (File.isPdb() && !getPdb().hasPDBDbiStream()) {948printStreamNotPresent("DBI");949return Error::success();950}951952return iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>(953File, PrintScope{P, 2},954[this](uint32_t Modi, const SymbolGroup &Strings,955DebugCrossModuleExportsSubsectionRef &Exports) -> Error {956P.formatLine("{0,-10} | {1}", "Local ID", "Global ID");957for (const auto &Export : Exports) {958P.formatLine("{0,+10:X+} | {1}", TypeIndex(Export.Local),959TypeIndex(Export.Global));960}961return Error::success();962});963}964965std::string formatFrameType(object::frame_type FT) {966switch (FT) {967case object::frame_type::Fpo:968return "FPO";969case object::frame_type::NonFpo:970return "Non-FPO";971case object::frame_type::Trap:972return "Trap";973case object::frame_type::Tss:974return "TSS";975}976return "<unknown>";977}978979Error DumpOutputStyle::dumpOldFpo(PDBFile &File) {980printHeader(P, "Old FPO Data");981982ExitOnError Err("Error dumping old fpo data:");983DbiStream &Dbi = Err(File.getPDBDbiStream());984985if (!Dbi.hasOldFpoRecords()) {986printStreamNotPresent("FPO");987return Error::success();988}989990const FixedStreamArray<object::FpoData>& Records = Dbi.getOldFpoRecords();991992P.printLine(" RVA | Code | Locals | Params | Prolog | Saved Regs | Use "993"BP | Has SEH | Frame Type");994995for (const object::FpoData &FD : Records) {996P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,6} | {5,10} | {6,6} | "997"{7,7} | {8,9}",998uint32_t(FD.Offset), uint32_t(FD.Size), uint32_t(FD.NumLocals),999uint32_t(FD.NumParams), FD.getPrologSize(),1000FD.getNumSavedRegs(), FD.useBP(), FD.hasSEH(),1001formatFrameType(FD.getFP()));1002}1003return Error::success();1004}10051006Error DumpOutputStyle::dumpNewFpo(PDBFile &File) {1007printHeader(P, "New FPO Data");10081009ExitOnError Err("Error dumping new fpo data:");1010DbiStream &Dbi = Err(File.getPDBDbiStream());10111012if (!Dbi.hasNewFpoRecords()) {1013printStreamNotPresent("New FPO");1014return Error::success();1015}10161017const DebugFrameDataSubsectionRef& FDS = Dbi.getNewFpoRecords();10181019P.printLine(" RVA | Code | Locals | Params | Stack | Prolog | Saved Regs "1020"| Has SEH | Has C++EH | Start | Program");1021for (const FrameData &FD : FDS) {1022bool IsFuncStart = FD.Flags & FrameData::IsFunctionStart;1023bool HasEH = FD.Flags & FrameData::HasEH;1024bool HasSEH = FD.Flags & FrameData::HasSEH;10251026auto &StringTable = Err(File.getStringTable());10271028auto Program = Err(StringTable.getStringForID(FD.FrameFunc));1029P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,5} | {5,6} | {6,10} | "1030"{7,7} | {8,9} | {9,5} | {10}",1031uint32_t(FD.RvaStart), uint32_t(FD.CodeSize),1032uint32_t(FD.LocalSize), uint32_t(FD.ParamsSize),1033uint32_t(FD.MaxStackSize), uint16_t(FD.PrologSize),1034uint16_t(FD.SavedRegsSize), HasSEH, HasEH, IsFuncStart,1035Program);1036}1037return Error::success();1038}10391040Error DumpOutputStyle::dumpFpo() {1041if (!File.isPdb()) {1042printStreamNotValidForObj();1043return Error::success();1044}10451046PDBFile &File = getPdb();1047if (!File.hasPDBDbiStream()) {1048printStreamNotPresent("DBI");1049return Error::success();1050}10511052if (auto EC = dumpOldFpo(File))1053return EC;1054if (auto EC = dumpNewFpo(File))1055return EC;1056return Error::success();1057}10581059Error DumpOutputStyle::dumpStringTableFromPdb() {1060AutoIndent Indent(P);1061auto IS = getPdb().getStringTable();1062if (!IS) {1063P.formatLine("Not present in file");1064consumeError(IS.takeError());1065return Error::success();1066}10671068if (opts::dump::DumpStringTable) {1069if (IS->name_ids().empty())1070P.formatLine("Empty");1071else {1072auto MaxID = llvm::max_element(IS->name_ids());1073uint32_t Digits = NumDigits(*MaxID);10741075P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits),1076"String");10771078std::vector<uint32_t> SortedIDs(IS->name_ids().begin(),1079IS->name_ids().end());1080llvm::sort(SortedIDs);1081for (uint32_t I : SortedIDs) {1082auto ES = IS->getStringForID(I);1083llvm::SmallString<32> Str;1084if (!ES) {1085consumeError(ES.takeError());1086Str = "Error reading string";1087} else if (!ES->empty()) {1088Str.append("'");1089Str.append(*ES);1090Str.append("'");1091}10921093if (!Str.empty())1094P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits),1095Str);1096}1097}1098}10991100if (opts::dump::DumpStringTableDetails) {1101P.NewLine();1102{1103P.printLine("String Table Header:");1104AutoIndent Indent(P);1105P.formatLine("Signature: {0}", IS->getSignature());1106P.formatLine("Hash Version: {0}", IS->getHashVersion());1107P.formatLine("Name Buffer Size: {0}", IS->getByteSize());1108P.NewLine();1109}11101111BinaryStreamRef NameBuffer = IS->getStringTable().getBuffer();1112ArrayRef<uint8_t> Contents;1113cantFail(NameBuffer.readBytes(0, NameBuffer.getLength(), Contents));1114P.formatBinary("Name Buffer", Contents, 0);1115P.NewLine();1116{1117P.printLine("Hash Table:");1118AutoIndent Indent(P);1119P.formatLine("Bucket Count: {0}", IS->name_ids().size());1120for (const auto &Entry : enumerate(IS->name_ids()))1121P.formatLine("Bucket[{0}] : {1}", Entry.index(),1122uint32_t(Entry.value()));1123P.formatLine("Name Count: {0}", IS->getNameCount());1124}1125}1126return Error::success();1127}11281129Error DumpOutputStyle::dumpStringTableFromObj() {1130return iterateModuleSubsections<DebugStringTableSubsectionRef>(1131File, PrintScope{P, 4},1132[&](uint32_t Modi, const SymbolGroup &Strings,1133DebugStringTableSubsectionRef &Strings2) -> Error {1134BinaryStreamRef StringTableBuffer = Strings2.getBuffer();1135BinaryStreamReader Reader(StringTableBuffer);1136while (Reader.bytesRemaining() > 0) {1137StringRef Str;1138uint32_t Offset = Reader.getOffset();1139cantFail(Reader.readCString(Str));1140if (Str.empty())1141continue;11421143P.formatLine("{0} | {1}", fmt_align(Offset, AlignStyle::Right, 4),1144Str);1145}1146return Error::success();1147});1148}11491150Error DumpOutputStyle::dumpNamedStreams() {1151printHeader(P, "Named Streams");11521153if (File.isObj()) {1154printStreamNotValidForObj();1155return Error::success();1156}11571158AutoIndent Indent(P);1159ExitOnError Err("Invalid PDB File: ");11601161auto &IS = Err(File.pdb().getPDBInfoStream());1162const NamedStreamMap &NS = IS.getNamedStreams();1163for (const auto &Entry : NS.entries()) {1164P.printLine(Entry.getKey());1165AutoIndent Indent2(P, 2);1166P.formatLine("Index: {0}", Entry.getValue());1167P.formatLine("Size in bytes: {0}",1168File.pdb().getStreamByteSize(Entry.getValue()));1169}11701171return Error::success();1172}11731174Error DumpOutputStyle::dumpStringTable() {1175printHeader(P, "String Table");11761177if (File.isPdb())1178return dumpStringTableFromPdb();11791180return dumpStringTableFromObj();1181}11821183static void buildDepSet(LazyRandomTypeCollection &Types,1184ArrayRef<TypeIndex> Indices,1185std::map<TypeIndex, CVType> &DepSet) {1186SmallVector<TypeIndex, 4> DepList;1187for (const auto &I : Indices) {1188TypeIndex TI(I);1189if (DepSet.find(TI) != DepSet.end() || TI.isSimple() || TI.isNoneType())1190continue;11911192CVType Type = Types.getType(TI);1193DepSet[TI] = Type;1194codeview::discoverTypeIndices(Type, DepList);1195buildDepSet(Types, DepList, DepSet);1196}1197}11981199static void1200dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types,1201TypeReferenceTracker *RefTracker, uint32_t NumTypeRecords,1202uint32_t NumHashBuckets,1203FixedStreamArray<support::ulittle32_t> HashValues,1204TpiStream *Stream, bool Bytes, bool Extras) {12051206Printer.formatLine("Showing {0:N} records", NumTypeRecords);1207uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + NumTypeRecords);12081209MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker,1210NumHashBuckets, HashValues, Stream);12111212if (auto EC = codeview::visitTypeStream(Types, V)) {1213Printer.formatLine("An error occurred dumping type records: {0}",1214toString(std::move(EC)));1215}1216}12171218static void dumpPartialTypeStream(LinePrinter &Printer,1219LazyRandomTypeCollection &Types,1220TypeReferenceTracker *RefTracker,1221TpiStream &Stream, ArrayRef<TypeIndex> TiList,1222bool Bytes, bool Extras, bool Deps) {1223uint32_t Width =1224NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords());12251226MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker,1227Stream.getNumHashBuckets(), Stream.getHashValues(),1228&Stream);12291230if (opts::dump::DumpTypeDependents) {1231// If we need to dump all dependents, then iterate each index and find1232// all dependents, adding them to a map ordered by TypeIndex.1233std::map<TypeIndex, CVType> DepSet;1234buildDepSet(Types, TiList, DepSet);12351236Printer.formatLine(1237"Showing {0:N} records and their dependents ({1:N} records total)",1238TiList.size(), DepSet.size());12391240for (auto &Dep : DepSet) {1241if (auto EC = codeview::visitTypeRecord(Dep.second, Dep.first, V))1242Printer.formatLine("An error occurred dumping type record {0}: {1}",1243Dep.first, toString(std::move(EC)));1244}1245} else {1246Printer.formatLine("Showing {0:N} records.", TiList.size());12471248for (const auto &I : TiList) {1249TypeIndex TI(I);1250if (TI.isSimple()) {1251Printer.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Width),1252Types.getTypeName(TI));1253} else if (std::optional<CVType> Type = Types.tryGetType(TI)) {1254if (auto EC = codeview::visitTypeRecord(*Type, TI, V))1255Printer.formatLine("An error occurred dumping type record {0}: {1}",1256TI, toString(std::move(EC)));1257} else {1258Printer.formatLine("Type {0} doesn't exist in TPI stream", TI);1259}1260}1261}1262}12631264Error DumpOutputStyle::dumpTypesFromObjectFile() {1265LazyRandomTypeCollection Types(100);12661267for (const auto &S : getObj().sections()) {1268Expected<StringRef> NameOrErr = S.getName();1269if (!NameOrErr)1270return NameOrErr.takeError();1271StringRef SectionName = *NameOrErr;12721273// .debug$T is a standard CodeView type section, while .debug$P is the same1274// format but used for MSVC precompiled header object files.1275if (SectionName == ".debug$T")1276printHeader(P, "Types (.debug$T)");1277else if (SectionName == ".debug$P")1278printHeader(P, "Precompiled Types (.debug$P)");1279else1280continue;12811282Expected<StringRef> ContentsOrErr = S.getContents();1283if (!ContentsOrErr)1284return ContentsOrErr.takeError();12851286uint32_t Magic;1287BinaryStreamReader Reader(*ContentsOrErr, llvm::endianness::little);1288if (auto EC = Reader.readInteger(Magic))1289return EC;1290if (Magic != COFF::DEBUG_SECTION_MAGIC)1291return make_error<StringError>("Invalid CodeView debug section.",1292inconvertibleErrorCode());12931294Types.reset(Reader, 100);12951296if (opts::dump::DumpTypes) {1297dumpFullTypeStream(P, Types, RefTracker.get(), 0, 0, {}, nullptr,1298opts::dump::DumpTypeData, false);1299} else if (opts::dump::DumpTypeExtras) {1300auto LocalHashes = LocallyHashedType::hashTypeCollection(Types);1301auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types);1302assert(LocalHashes.size() == GlobalHashes.size());13031304P.formatLine("Local / Global hashes:");1305TypeIndex TI(TypeIndex::FirstNonSimpleIndex);1306for (auto H : zip(LocalHashes, GlobalHashes)) {1307AutoIndent Indent2(P);1308LocallyHashedType &L = std::get<0>(H);1309GloballyHashedType &G = std::get<1>(H);13101311P.formatLine("TI: {0}, LocalHash: {1:X}, GlobalHash: {2}", TI, L, G);13121313++TI;1314}1315P.NewLine();1316}1317}13181319return Error::success();1320}13211322Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {1323assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);13241325if (StreamIdx == StreamTPI) {1326printHeader(P, "Types (TPI Stream)");1327} else if (StreamIdx == StreamIPI) {1328printHeader(P, "Types (IPI Stream)");1329}13301331assert(!File.isObj());13321333bool Present = false;1334bool DumpTypes = false;1335bool DumpBytes = false;1336bool DumpExtras = false;1337std::vector<uint32_t> Indices;1338if (StreamIdx == StreamTPI) {1339Present = getPdb().hasPDBTpiStream();1340DumpTypes = opts::dump::DumpTypes;1341DumpBytes = opts::dump::DumpTypeData;1342DumpExtras = opts::dump::DumpTypeExtras;1343Indices.assign(opts::dump::DumpTypeIndex.begin(),1344opts::dump::DumpTypeIndex.end());1345} else if (StreamIdx == StreamIPI) {1346Present = getPdb().hasPDBIpiStream();1347DumpTypes = opts::dump::DumpIds;1348DumpBytes = opts::dump::DumpIdData;1349DumpExtras = opts::dump::DumpIdExtras;1350Indices.assign(opts::dump::DumpIdIndex.begin(),1351opts::dump::DumpIdIndex.end());1352}13531354if (!Present) {1355printStreamNotPresent(StreamIdx == StreamTPI ? "TPI" : "IPI");1356return Error::success();1357}13581359AutoIndent Indent(P);1360ExitOnError Err("Unexpected error processing types: ");13611362auto &Stream = Err((StreamIdx == StreamTPI) ? getPdb().getPDBTpiStream()1363: getPdb().getPDBIpiStream());13641365auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids();13661367// Only emit notes about referenced/unreferenced for types.1368TypeReferenceTracker *MaybeTracker =1369(StreamIdx == StreamTPI) ? RefTracker.get() : nullptr;13701371// Enable resolving forward decls.1372Stream.buildHashMap();13731374if (DumpTypes || !Indices.empty()) {1375if (Indices.empty())1376dumpFullTypeStream(P, Types, MaybeTracker, Stream.getNumTypeRecords(),1377Stream.getNumHashBuckets(), Stream.getHashValues(),1378&Stream, DumpBytes, DumpExtras);1379else {1380std::vector<TypeIndex> TiList(Indices.begin(), Indices.end());1381dumpPartialTypeStream(P, Types, MaybeTracker, Stream, TiList, DumpBytes,1382DumpExtras, opts::dump::DumpTypeDependents);1383}1384}13851386if (DumpExtras) {1387P.NewLine();13881389P.formatLine("Header Version: {0}",1390static_cast<uint32_t>(Stream.getTpiVersion()));1391P.formatLine("Hash Stream Index: {0}", Stream.getTypeHashStreamIndex());1392P.formatLine("Aux Hash Stream Index: {0}",1393Stream.getTypeHashStreamAuxIndex());1394P.formatLine("Hash Key Size: {0}", Stream.getHashKeySize());1395P.formatLine("Num Hash Buckets: {0}", Stream.getNumHashBuckets());13961397auto IndexOffsets = Stream.getTypeIndexOffsets();1398P.formatLine("Type Index Offsets:");1399for (const auto &IO : IndexOffsets) {1400AutoIndent Indent2(P);1401P.formatLine("TI: {0}, Offset: {1}", IO.Type, fmtle(IO.Offset));1402}14031404if (getPdb().hasPDBStringTable()) {1405P.NewLine();1406P.formatLine("Hash Adjusters:");1407auto &Adjusters = Stream.getHashAdjusters();1408auto &Strings = Err(getPdb().getStringTable());1409for (const auto &A : Adjusters) {1410AutoIndent Indent2(P);1411auto ExpectedStr = Strings.getStringForID(A.first);1412TypeIndex TI(A.second);1413if (ExpectedStr)1414P.formatLine("`{0}` -> {1}", *ExpectedStr, TI);1415else {1416P.formatLine("unknown str id ({0}) -> {1}", A.first, TI);1417consumeError(ExpectedStr.takeError());1418}1419}1420}1421}1422return Error::success();1423}14241425Error DumpOutputStyle::dumpModuleSymsForObj() {1426printHeader(P, "Symbols");14271428AutoIndent Indent(P);14291430auto &Types = File.types();14311432SymbolVisitorCallbackPipeline Pipeline;1433SymbolDeserializer Deserializer(nullptr, CodeViewContainer::ObjectFile);1434MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types, Types);14351436Pipeline.addCallbackToPipeline(Deserializer);1437Pipeline.addCallbackToPipeline(Dumper);1438CVSymbolVisitor Visitor(Pipeline);14391440return iterateModuleSubsections<DebugSymbolsSubsectionRef>(1441File, PrintScope{P, 2},1442[&](uint32_t Modi, const SymbolGroup &Strings,1443DebugSymbolsSubsectionRef &Symbols) -> Error {1444Dumper.setSymbolGroup(&Strings);1445for (auto Symbol : Symbols) {1446if (auto EC = Visitor.visitSymbolRecord(Symbol)) {1447return EC;1448}1449}1450return Error::success();1451});1452}14531454Error DumpOutputStyle::dumpModuleSymsForPdb() {1455printHeader(P, "Symbols");14561457if (File.isPdb() && !getPdb().hasPDBDbiStream()) {1458printStreamNotPresent("DBI");1459return Error::success();1460}14611462AutoIndent Indent(P);14631464auto &Ids = File.ids();1465auto &Types = File.types();14661467return iterateSymbolGroups(1468File, PrintScope{P, 2},1469[&](uint32_t I, const SymbolGroup &Strings) -> Error {1470auto ExpectedModS = getModuleDebugStream(File.pdb(), I);1471if (!ExpectedModS) {1472P.formatLine("Error loading module stream {0}. {1}", I,1473toString(ExpectedModS.takeError()));1474return Error::success();1475}14761477ModuleDebugStreamRef &ModS = *ExpectedModS;14781479SymbolVisitorCallbackPipeline Pipeline;1480SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);1481MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Strings,1482Ids, Types);14831484Pipeline.addCallbackToPipeline(Deserializer);1485Pipeline.addCallbackToPipeline(Dumper);1486CVSymbolVisitor Visitor(Pipeline);1487auto SS = ModS.getSymbolsSubstream();1488if (opts::Filters.SymbolOffset) {1489CVSymbolVisitor::FilterOptions Filter;1490Filter.SymbolOffset = opts::Filters.SymbolOffset;1491Filter.ParentRecursiveDepth = opts::Filters.ParentRecurseDepth;1492Filter.ChildRecursiveDepth = opts::Filters.ChildrenRecurseDepth;1493if (auto EC = Visitor.visitSymbolStreamFiltered(ModS.getSymbolArray(),1494Filter)) {1495P.formatLine("Error while processing symbol records. {0}",1496toStringWithoutConsuming(EC));1497return EC;1498}1499} else if (auto EC = Visitor.visitSymbolStream(ModS.getSymbolArray(),1500SS.Offset)) {1501P.formatLine("Error while processing symbol records. {0}",1502toStringWithoutConsuming(EC));1503return EC;1504}1505return Error::success();1506});1507}15081509Error DumpOutputStyle::dumpTypeRefStats() {1510printHeader(P, "Type Reference Statistics");1511AutoIndent Indent(P);15121513// Sum the byte size of all type records, and the size and count of all1514// referenced records.1515size_t TotalRecs = File.types().size();1516size_t RefRecs = 0;1517size_t TotalBytes = 0;1518size_t RefBytes = 0;1519auto &Types = File.types();1520for (std::optional<TypeIndex> TI = Types.getFirst(); TI;1521TI = Types.getNext(*TI)) {1522CVType Type = File.types().getType(*TI);1523TotalBytes += Type.length();1524if (RefTracker->isTypeReferenced(*TI)) {1525++RefRecs;1526RefBytes += Type.length();1527}1528}15291530P.formatLine("Records referenced: {0:N} / {1:N} {2:P}", RefRecs, TotalRecs,1531(double)RefRecs / TotalRecs);1532P.formatLine("Bytes referenced: {0:N} / {1:N} {2:P}", RefBytes, TotalBytes,1533(double)RefBytes / TotalBytes);15341535return Error::success();1536}15371538Error DumpOutputStyle::dumpGSIRecords() {1539printHeader(P, "GSI Records");15401541if (File.isObj()) {1542printStreamNotValidForObj();1543return Error::success();1544}15451546if (!getPdb().hasPDBSymbolStream()) {1547printStreamNotPresent("GSI Common Symbol");1548return Error::success();1549}15501551AutoIndent Indent(P);15521553auto &Records = cantFail(getPdb().getPDBSymbolStream());1554auto &Types = File.types();1555auto &Ids = File.ids();15561557P.printLine("Records");1558SymbolVisitorCallbackPipeline Pipeline;1559SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);1560MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);15611562Pipeline.addCallbackToPipeline(Deserializer);1563Pipeline.addCallbackToPipeline(Dumper);1564CVSymbolVisitor Visitor(Pipeline);15651566BinaryStreamRef SymStream = Records.getSymbolArray().getUnderlyingStream();1567if (auto E = Visitor.visitSymbolStream(Records.getSymbolArray(), 0))1568return E;1569return Error::success();1570}15711572Error DumpOutputStyle::dumpGlobals() {1573printHeader(P, "Global Symbols");15741575if (File.isObj()) {1576printStreamNotValidForObj();1577return Error::success();1578}15791580if (!getPdb().hasPDBGlobalsStream()) {1581printStreamNotPresent("Globals");1582return Error::success();1583}15841585AutoIndent Indent(P);1586ExitOnError Err("Error dumping globals stream: ");1587auto &Globals = Err(getPdb().getPDBGlobalsStream());15881589if (opts::dump::DumpGlobalNames.empty()) {1590const GSIHashTable &Table = Globals.getGlobalsTable();1591Err(dumpSymbolsFromGSI(Table, opts::dump::DumpGlobalExtras));1592} else {1593SymbolStream &SymRecords = cantFail(getPdb().getPDBSymbolStream());1594auto &Types = File.types();1595auto &Ids = File.ids();15961597SymbolVisitorCallbackPipeline Pipeline;1598SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);1599MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);16001601Pipeline.addCallbackToPipeline(Deserializer);1602Pipeline.addCallbackToPipeline(Dumper);1603CVSymbolVisitor Visitor(Pipeline);16041605using ResultEntryType = std::pair<uint32_t, CVSymbol>;1606for (StringRef Name : opts::dump::DumpGlobalNames) {1607AutoIndent Indent(P);1608P.formatLine("Global Name `{0}`", Name);1609std::vector<ResultEntryType> Results =1610Globals.findRecordsByName(Name, SymRecords);1611if (Results.empty()) {1612AutoIndent Indent(P);1613P.printLine("(no matching records found)");1614continue;1615}16161617for (ResultEntryType Result : Results) {1618if (auto E = Visitor.visitSymbolRecord(Result.second, Result.first))1619return E;1620}1621}1622}1623return Error::success();1624}16251626Error DumpOutputStyle::dumpPublics() {1627printHeader(P, "Public Symbols");16281629if (File.isObj()) {1630printStreamNotValidForObj();1631return Error::success();1632}16331634if (!getPdb().hasPDBPublicsStream()) {1635printStreamNotPresent("Publics");1636return Error::success();1637}16381639AutoIndent Indent(P);1640ExitOnError Err("Error dumping publics stream: ");1641auto &Publics = Err(getPdb().getPDBPublicsStream());16421643const GSIHashTable &PublicsTable = Publics.getPublicsTable();1644if (opts::dump::DumpPublicExtras) {1645P.printLine("Publics Header");1646AutoIndent Indent(P);1647P.formatLine("sym hash = {0}, thunk table addr = {1}", Publics.getSymHash(),1648formatSegmentOffset(Publics.getThunkTableSection(),1649Publics.getThunkTableOffset()));1650}1651Err(dumpSymbolsFromGSI(PublicsTable, opts::dump::DumpPublicExtras));16521653// Skip the rest if we aren't dumping extras.1654if (!opts::dump::DumpPublicExtras)1655return Error::success();16561657P.formatLine("Address Map");1658{1659// These are offsets into the publics stream sorted by secidx:secrel.1660AutoIndent Indent2(P);1661for (uint32_t Addr : Publics.getAddressMap())1662P.formatLine("off = {0}", Addr);1663}16641665// The thunk map is optional debug info used for ILT thunks.1666if (!Publics.getThunkMap().empty()) {1667P.formatLine("Thunk Map");1668AutoIndent Indent2(P);1669for (uint32_t Addr : Publics.getThunkMap())1670P.formatLine("{0:x8}", Addr);1671}16721673// The section offsets table appears to be empty when incremental linking1674// isn't in use.1675if (!Publics.getSectionOffsets().empty()) {1676P.formatLine("Section Offsets");1677AutoIndent Indent2(P);1678for (const SectionOffset &SO : Publics.getSectionOffsets())1679P.formatLine("{0:x4}:{1:x8}", uint16_t(SO.Isect), uint32_t(SO.Off));1680}16811682return Error::success();1683}16841685Error DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable &Table,1686bool HashExtras) {1687auto ExpectedSyms = getPdb().getPDBSymbolStream();1688if (!ExpectedSyms)1689return ExpectedSyms.takeError();1690auto &Types = File.types();1691auto &Ids = File.ids();16921693if (HashExtras) {1694P.printLine("GSI Header");1695AutoIndent Indent(P);1696P.formatLine("sig = {0:X}, hdr = {1:X}, hr size = {2}, num buckets = {3}",1697Table.getVerSignature(), Table.getVerHeader(),1698Table.getHashRecordSize(), Table.getNumBuckets());1699}17001701{1702P.printLine("Records");1703SymbolVisitorCallbackPipeline Pipeline;1704SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);1705MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);17061707Pipeline.addCallbackToPipeline(Deserializer);1708Pipeline.addCallbackToPipeline(Dumper);1709CVSymbolVisitor Visitor(Pipeline);171017111712BinaryStreamRef SymStream =1713ExpectedSyms->getSymbolArray().getUnderlyingStream();1714for (uint32_t PubSymOff : Table) {1715Expected<CVSymbol> Sym = readSymbolFromStream(SymStream, PubSymOff);1716if (!Sym)1717return Sym.takeError();1718if (auto E = Visitor.visitSymbolRecord(*Sym, PubSymOff))1719return E;1720}1721}17221723// Return early if we aren't dumping public hash table and address map info.1724if (HashExtras) {1725P.formatLine("Hash Entries");1726{1727AutoIndent Indent2(P);1728for (const PSHashRecord &HR : Table.HashRecords)1729P.formatLine("off = {0}, refcnt = {1}", uint32_t(HR.Off),1730uint32_t(HR.CRef));1731}17321733P.formatLine("Hash Buckets");1734{1735AutoIndent Indent2(P);1736for (uint32_t Hash : Table.HashBuckets)1737P.formatLine("{0:x8}", Hash);1738}1739}17401741return Error::success();1742}17431744static std::string formatSegMapDescriptorFlag(uint32_t IndentLevel,1745OMFSegDescFlags Flags) {1746std::vector<std::string> Opts;1747if (Flags == OMFSegDescFlags::None)1748return "none";17491750PUSH_FLAG(OMFSegDescFlags, Read, Flags, "read");1751PUSH_FLAG(OMFSegDescFlags, Write, Flags, "write");1752PUSH_FLAG(OMFSegDescFlags, Execute, Flags, "execute");1753PUSH_FLAG(OMFSegDescFlags, AddressIs32Bit, Flags, "32 bit addr");1754PUSH_FLAG(OMFSegDescFlags, IsSelector, Flags, "selector");1755PUSH_FLAG(OMFSegDescFlags, IsAbsoluteAddress, Flags, "absolute addr");1756PUSH_FLAG(OMFSegDescFlags, IsGroup, Flags, "group");1757return typesetItemList(Opts, IndentLevel, 4, " | ");1758}17591760Error DumpOutputStyle::dumpSectionHeaders() {1761dumpSectionHeaders("Section Headers", DbgHeaderType::SectionHdr);1762dumpSectionHeaders("Original Section Headers", DbgHeaderType::SectionHdrOrig);1763return Error::success();1764}17651766void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) {1767printHeader(P, Label);17681769if (File.isObj()) {1770printStreamNotValidForObj();1771return;1772}17731774if (!getPdb().hasPDBDbiStream()) {1775printStreamNotPresent("DBI");1776return;1777}17781779AutoIndent Indent(P);1780ExitOnError Err("Error dumping section headers: ");1781std::unique_ptr<MappedBlockStream> Stream;1782ArrayRef<object::coff_section> Headers;1783auto ExpectedHeaders = loadSectionHeaders(getPdb(), Type);1784if (!ExpectedHeaders) {1785P.printLine(toString(ExpectedHeaders.takeError()));1786return;1787}1788std::tie(Stream, Headers) = std::move(*ExpectedHeaders);17891790uint32_t I = 1;1791for (const auto &Header : Headers) {1792P.NewLine();1793P.formatLine("SECTION HEADER #{0}", I);1794P.formatLine("{0,8} name", Header.Name);1795P.formatLine("{0,8:X-} virtual size", uint32_t(Header.VirtualSize));1796P.formatLine("{0,8:X-} virtual address", uint32_t(Header.VirtualAddress));1797P.formatLine("{0,8:X-} size of raw data", uint32_t(Header.SizeOfRawData));1798P.formatLine("{0,8:X-} file pointer to raw data",1799uint32_t(Header.PointerToRawData));1800P.formatLine("{0,8:X-} file pointer to relocation table",1801uint32_t(Header.PointerToRelocations));1802P.formatLine("{0,8:X-} file pointer to line numbers",1803uint32_t(Header.PointerToLinenumbers));1804P.formatLine("{0,8:X-} number of relocations",1805uint32_t(Header.NumberOfRelocations));1806P.formatLine("{0,8:X-} number of line numbers",1807uint32_t(Header.NumberOfLinenumbers));1808P.formatLine("{0,8:X-} flags", uint32_t(Header.Characteristics));1809AutoIndent IndentMore(P, 9);1810P.formatLine("{0}", formatSectionCharacteristics(1811P.getIndentLevel(), Header.Characteristics, 1, ""));1812++I;1813}1814}18151816Error DumpOutputStyle::dumpSectionContribs() {1817printHeader(P, "Section Contributions");18181819if (File.isObj()) {1820printStreamNotValidForObj();1821return Error::success();1822}18231824if (!getPdb().hasPDBDbiStream()) {1825printStreamNotPresent("DBI");1826return Error::success();1827}18281829AutoIndent Indent(P);1830ExitOnError Err("Error dumping section contributions: ");18311832DbiStream &Dbi = Err(getPdb().getPDBDbiStream());18331834class Visitor : public ISectionContribVisitor {1835public:1836Visitor(LinePrinter &P, ArrayRef<std::string> Names) : P(P), Names(Names) {1837auto Max = llvm::max_element(Names, [](StringRef S1, StringRef S2) {1838return S1.size() < S2.size();1839});1840MaxNameLen = (Max == Names.end() ? 0 : Max->size());1841}1842void visit(const SectionContrib &SC) override {1843dumpSectionContrib(P, SC, Names, MaxNameLen);1844}1845void visit(const SectionContrib2 &SC) override {1846dumpSectionContrib(P, SC, Names, MaxNameLen);1847}18481849private:1850LinePrinter &P;1851uint32_t MaxNameLen;1852ArrayRef<std::string> Names;1853};18541855auto NamesOrErr = getSectionNames(getPdb());1856if (!NamesOrErr)1857return NamesOrErr.takeError();1858ArrayRef<std::string> Names = *NamesOrErr;1859Visitor V(P, Names);1860Dbi.visitSectionContributions(V);1861return Error::success();1862}18631864Error DumpOutputStyle::dumpSectionMap() {1865printHeader(P, "Section Map");18661867if (File.isObj()) {1868printStreamNotValidForObj();1869return Error::success();1870}18711872if (!getPdb().hasPDBDbiStream()) {1873printStreamNotPresent("DBI");1874return Error::success();1875}18761877AutoIndent Indent(P);1878ExitOnError Err("Error dumping section map: ");18791880DbiStream &Dbi = Err(getPdb().getPDBDbiStream());18811882uint32_t I = 0;1883for (auto &M : Dbi.getSectionMap()) {1884P.formatLine(1885"Section {0:4} | ovl = {1}, group = {2}, frame = {3}, name = {4}", I,1886fmtle(M.Ovl), fmtle(M.Group), fmtle(M.Frame), fmtle(M.SecName));1887P.formatLine(" class = {0}, offset = {1}, size = {2}",1888fmtle(M.ClassName), fmtle(M.Offset), fmtle(M.SecByteLength));1889P.formatLine(" flags = {0}",1890formatSegMapDescriptorFlag(1891P.getIndentLevel() + 13,1892static_cast<OMFSegDescFlags>(uint16_t(M.Flags))));1893++I;1894}1895return Error::success();1896}189718981899