1898 lines · cpp
1//===- DumpOutputStyle.cpp ------------------------------------ *- C++ --*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "DumpOutputStyle.h"10 11#include "MinimalSymbolDumper.h"12#include "MinimalTypeDumper.h"13#include "StreamUtil.h"14#include "TypeReferenceTracker.h"15#include "llvm-pdbutil.h"16 17#include "llvm/ADT/STLExtras.h"18#include "llvm/ADT/StringExtras.h"19#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h"20#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"21#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h"22#include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h"23#include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h"24#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h"25#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h"26#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h"27#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h"28#include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h"29#include "llvm/DebugInfo/CodeView/Formatters.h"30#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"31#include "llvm/DebugInfo/CodeView/Line.h"32#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"33#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h"34#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h"35#include "llvm/DebugInfo/CodeView/TypeHashing.h"36#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"37#include "llvm/DebugInfo/MSF/MappedBlockStream.h"38#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"39#include "llvm/DebugInfo/PDB/Native/DbiStream.h"40#include "llvm/DebugInfo/PDB/Native/FormatUtil.h"41#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"42#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h"43#include "llvm/DebugInfo/PDB/Native/InfoStream.h"44#include "llvm/DebugInfo/PDB/Native/InputFile.h"45#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"46#include "llvm/DebugInfo/PDB/Native/NativeSession.h"47#include "llvm/DebugInfo/PDB/Native/PDBFile.h"48#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"49#include "llvm/DebugInfo/PDB/Native/RawError.h"50#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"51#include "llvm/DebugInfo/PDB/Native/TpiHashing.h"52#include "llvm/DebugInfo/PDB/Native/TpiStream.h"53#include "llvm/Object/COFF.h"54#include "llvm/Support/BinaryStreamReader.h"55#include "llvm/Support/FormatAdapters.h"56#include "llvm/Support/FormatVariadic.h"57 58#include <cctype>59 60using namespace llvm;61using namespace llvm::codeview;62using namespace llvm::msf;63using namespace llvm::pdb;64 65DumpOutputStyle::DumpOutputStyle(InputFile &File)66 : File(File), P(2, false, outs(), opts::Filters) {67 if (opts::dump::DumpTypeRefStats)68 RefTracker.reset(new TypeReferenceTracker(File));69}70 71DumpOutputStyle::~DumpOutputStyle() = default;72 73PDBFile &DumpOutputStyle::getPdb() { return File.pdb(); }74object::COFFObjectFile &DumpOutputStyle::getObj() { return File.obj(); }75 76void DumpOutputStyle::printStreamNotValidForObj() {77 AutoIndent Indent(P, 4);78 P.formatLine("Dumping this stream is not valid for object files");79}80 81void DumpOutputStyle::printStreamNotPresent(StringRef StreamName) {82 AutoIndent Indent(P, 4);83 P.formatLine("{0} stream not present", StreamName);84}85 86Error DumpOutputStyle::dump() {87 // Walk symbols & globals if we are supposed to mark types referenced.88 if (opts::dump::DumpTypeRefStats)89 RefTracker->mark();90 91 if (opts::dump::DumpSummary) {92 if (auto EC = dumpFileSummary())93 return EC;94 P.NewLine();95 }96 97 if (opts::dump::DumpStreams) {98 if (auto EC = dumpStreamSummary())99 return EC;100 P.NewLine();101 }102 103 if (opts::dump::DumpSymbolStats) {104 ExitOnError Err("Unexpected error processing module stats: ");105 Err(dumpSymbolStats());106 P.NewLine();107 }108 109 if (opts::dump::DumpUdtStats) {110 if (auto EC = dumpUdtStats())111 return EC;112 P.NewLine();113 }114 115 if (opts::dump::DumpTypeStats || opts::dump::DumpIDStats) {116 if (auto EC = dumpTypeStats())117 return EC;118 P.NewLine();119 }120 121 if (opts::dump::DumpNamedStreams) {122 if (auto EC = dumpNamedStreams())123 return EC;124 P.NewLine();125 }126 127 if (opts::dump::DumpStringTable || opts::dump::DumpStringTableDetails) {128 if (auto EC = dumpStringTable())129 return EC;130 P.NewLine();131 }132 133 if (opts::dump::DumpModules) {134 ExitOnError Err("Unexpected error processing modules: ");135 Err(dumpModules());136 }137 138 if (opts::dump::DumpModuleFiles) {139 ExitOnError Err("Unexpected error processing files: ");140 Err(dumpModuleFiles());141 }142 143 if (opts::dump::DumpLines) {144 ExitOnError Err("Unexpected error processing lines: ");145 Err(dumpLines());146 }147 148 if (opts::dump::DumpInlineeLines) {149 ExitOnError Err("Unexpected error processing inlinee lines: ");150 Err(dumpInlineeLines());151 }152 153 if (opts::dump::DumpXmi) {154 ExitOnError Err("Unexpected error processing cross module imports: ");155 Err(dumpXmi());156 }157 158 if (opts::dump::DumpXme) {159 ExitOnError Err("Unexpected error processing cross module exports: ");160 Err(dumpXme());161 }162 163 if (opts::dump::DumpFpo) {164 if (auto EC = dumpFpo())165 return EC;166 }167 168 if (File.isObj()) {169 if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() ||170 opts::dump::DumpTypeExtras)171 if (auto EC = dumpTypesFromObjectFile())172 return EC;173 } else {174 if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() ||175 opts::dump::DumpTypeExtras) {176 if (auto EC = dumpTpiStream(StreamTPI))177 return EC;178 }179 180 if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() ||181 opts::dump::DumpIdExtras) {182 if (auto EC = dumpTpiStream(StreamIPI))183 return EC;184 }185 }186 187 if (opts::dump::DumpGSIRecords) {188 if (auto EC = dumpGSIRecords())189 return EC;190 }191 192 if (opts::dump::DumpGlobals) {193 if (auto EC = dumpGlobals())194 return EC;195 }196 197 if (opts::dump::DumpPublics) {198 if (auto EC = dumpPublics())199 return EC;200 }201 202 if (opts::dump::DumpSymbols) {203 ExitOnError Err("Unexpected error processing symbols: ");204 Err(File.isPdb() ? dumpModuleSymsForPdb() : dumpModuleSymsForObj());205 }206 207 if (opts::dump::DumpTypeRefStats) {208 if (auto EC = dumpTypeRefStats())209 return EC;210 }211 212 if (opts::dump::DumpSectionHeaders) {213 if (auto EC = dumpSectionHeaders())214 return EC;215 }216 217 if (opts::dump::DumpSectionContribs) {218 if (auto EC = dumpSectionContribs())219 return EC;220 }221 222 if (opts::dump::DumpSectionMap) {223 if (auto EC = dumpSectionMap())224 return EC;225 }226 227 P.NewLine();228 229 return Error::success();230}231 232static void printHeader(LinePrinter &P, const Twine &S) {233 P.NewLine();234 P.formatLine("{0,=60}", S);235 P.formatLine("{0}", fmt_repeat('=', 60));236}237 238Error DumpOutputStyle::dumpFileSummary() {239 printHeader(P, "Summary");240 241 if (File.isObj()) {242 printStreamNotValidForObj();243 return Error::success();244 }245 246 AutoIndent Indent(P);247 ExitOnError Err("Invalid PDB Format: ");248 249 P.formatLine("Block Size: {0}", getPdb().getBlockSize());250 P.formatLine("Number of blocks: {0}", getPdb().getBlockCount());251 P.formatLine("Number of streams: {0}", getPdb().getNumStreams());252 253 auto &PS = Err(getPdb().getPDBInfoStream());254 P.formatLine("Signature: {0}", PS.getSignature());255 P.formatLine("Age: {0}", PS.getAge());256 P.formatLine("GUID: {0}", fmt_guid(PS.getGuid().Guid));257 P.formatLine("Features: {0:x+}", static_cast<uint32_t>(PS.getFeatures()));258 P.formatLine("Has Debug Info: {0}", getPdb().hasPDBDbiStream());259 P.formatLine("Has Types: {0}", getPdb().hasPDBTpiStream());260 P.formatLine("Has IDs: {0}", getPdb().hasPDBIpiStream());261 P.formatLine("Has Globals: {0}", getPdb().hasPDBGlobalsStream());262 P.formatLine("Has Publics: {0}", getPdb().hasPDBPublicsStream());263 if (getPdb().hasPDBDbiStream()) {264 DbiStream &DBI = Err(getPdb().getPDBDbiStream());265 P.formatLine("Is incrementally linked: {0}", DBI.isIncrementallyLinked());266 P.formatLine("Has conflicting types: {0}", DBI.hasCTypes());267 P.formatLine("Is stripped: {0}", DBI.isStripped());268 }269 270 return Error::success();271}272 273static StatCollection getSymbolStats(const SymbolGroup &SG,274 StatCollection &CumulativeStats) {275 StatCollection Stats;276 if (SG.getFile().isPdb() && SG.hasDebugStream()) {277 // For PDB files, all symbols are packed into one stream.278 for (const auto &S : SG.getPdbModuleStream().symbols(nullptr)) {279 Stats.update(S.kind(), S.length());280 CumulativeStats.update(S.kind(), S.length());281 }282 return Stats;283 }284 285 for (const auto &SS : SG.getDebugSubsections()) {286 // For object files, all symbols are spread across multiple Symbol287 // subsections of a given .debug$S section.288 if (SS.kind() != DebugSubsectionKind::Symbols)289 continue;290 DebugSymbolsSubsectionRef Symbols;291 BinaryStreamReader Reader(SS.getRecordData());292 cantFail(Symbols.initialize(Reader));293 for (const auto &S : Symbols) {294 Stats.update(S.kind(), S.length());295 CumulativeStats.update(S.kind(), S.length());296 }297 }298 return Stats;299}300 301static StatCollection getChunkStats(const SymbolGroup &SG,302 StatCollection &CumulativeStats) {303 StatCollection Stats;304 for (const auto &Chunk : SG.getDebugSubsections()) {305 Stats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength());306 CumulativeStats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength());307 }308 return Stats;309}310 311static inline std::string formatModuleDetailKind(DebugSubsectionKind K) {312 return formatChunkKind(K, false);313}314 315static inline std::string formatModuleDetailKind(SymbolKind K) {316 return formatSymbolKind(K);317}318 319// Get the stats sorted by size, descending.320std::vector<StatCollection::KindAndStat>321StatCollection::getStatsSortedBySize() const {322 std::vector<KindAndStat> SortedStats(Individual.begin(), Individual.end());323 llvm::stable_sort(SortedStats,324 [](const KindAndStat &LHS, const KindAndStat &RHS) {325 return LHS.second.Size > RHS.second.Size;326 });327 return SortedStats;328}329 330template <typename Kind>331static void printModuleDetailStats(LinePrinter &P, StringRef Label,332 const StatCollection &Stats) {333 P.NewLine();334 P.formatLine(" {0}", Label);335 AutoIndent Indent(P);336 P.formatLine("{0,40}: {1,7} entries ({2,12:N} bytes)", "Total",337 Stats.Totals.Count, Stats.Totals.Size);338 P.formatLine("{0}", fmt_repeat('-', 74));339 340 for (const auto &K : Stats.getStatsSortedBySize()) {341 std::string KindName = formatModuleDetailKind(Kind(K.first));342 P.formatLine("{0,40}: {1,7} entries ({2,12:N} bytes)", KindName,343 K.second.Count, K.second.Size);344 }345}346 347Error DumpOutputStyle::dumpStreamSummary() {348 printHeader(P, "Streams");349 350 if (File.isObj()) {351 printStreamNotValidForObj();352 return Error::success();353 }354 355 AutoIndent Indent(P);356 357 if (StreamPurposes.empty())358 discoverStreamPurposes(getPdb(), StreamPurposes);359 360 uint32_t StreamCount = getPdb().getNumStreams();361 uint32_t MaxStreamSize = getPdb().getMaxStreamSize();362 363 for (uint32_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {364 P.formatLine(365 "Stream {0} ({1} bytes): [{2}]",366 fmt_align(StreamIdx, AlignStyle::Right, NumDigits(StreamCount)),367 fmt_align(getPdb().getStreamByteSize(StreamIdx), AlignStyle::Right,368 NumDigits(MaxStreamSize)),369 StreamPurposes[StreamIdx].getLongName());370 371 if (opts::dump::DumpStreamBlocks) {372 auto Blocks = getPdb().getStreamBlockList(StreamIdx);373 std::vector<uint32_t> BV(Blocks.begin(), Blocks.end());374 P.formatLine(" {0} Blocks: [{1}]",375 fmt_repeat(' ', NumDigits(StreamCount)),376 make_range(BV.begin(), BV.end()));377 }378 }379 380 return Error::success();381}382 383static Expected<std::pair<std::unique_ptr<MappedBlockStream>,384 ArrayRef<llvm::object::coff_section>>>385loadSectionHeaders(PDBFile &File, DbgHeaderType Type) {386 if (!File.hasPDBDbiStream())387 return make_error<StringError>(388 "Section headers require a DBI Stream, which could not be loaded",389 inconvertibleErrorCode());390 391 DbiStream &Dbi = cantFail(File.getPDBDbiStream());392 uint32_t SI = Dbi.getDebugStreamIndex(Type);393 394 if (SI == kInvalidStreamIndex)395 return make_error<StringError>(396 "PDB does not contain the requested image section header type",397 inconvertibleErrorCode());398 399 auto Stream = File.createIndexedStream(SI);400 if (!Stream)401 return make_error<StringError>("Could not load the required stream data",402 inconvertibleErrorCode());403 404 ArrayRef<object::coff_section> Headers;405 if (Stream->getLength() % sizeof(object::coff_section) != 0)406 return make_error<StringError>(407 "Section header array size is not a multiple of section header size",408 inconvertibleErrorCode());409 410 uint32_t NumHeaders = Stream->getLength() / sizeof(object::coff_section);411 BinaryStreamReader Reader(*Stream);412 cantFail(Reader.readArray(Headers, NumHeaders));413 return std::make_pair(std::move(Stream), Headers);414}415 416static Expected<std::vector<std::string>> getSectionNames(PDBFile &File) {417 auto ExpectedHeaders = loadSectionHeaders(File, DbgHeaderType::SectionHdr);418 if (!ExpectedHeaders)419 return ExpectedHeaders.takeError();420 421 std::unique_ptr<MappedBlockStream> Stream;422 ArrayRef<object::coff_section> Headers;423 std::tie(Stream, Headers) = std::move(*ExpectedHeaders);424 std::vector<std::string> Names;425 for (const auto &H : Headers)426 Names.push_back(H.Name);427 return Names;428}429 430static void dumpSectionContrib(LinePrinter &P, const SectionContrib &SC,431 ArrayRef<std::string> SectionNames,432 uint32_t FieldWidth) {433 std::string NameInsert;434 if (SC.ISect > 0 && SC.ISect <= SectionNames.size()) {435 StringRef SectionName = SectionNames[SC.ISect - 1];436 NameInsert = formatv("[{0}]", SectionName).str();437 } else438 NameInsert = "[???]";439 P.formatLine("SC{5} | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "440 "crc = {4}",441 formatSegmentOffset(SC.ISect, SC.Off), fmtle(SC.Size),442 fmtle(SC.Imod), fmtle(SC.DataCrc), fmtle(SC.RelocCrc),443 fmt_align(NameInsert, AlignStyle::Left, FieldWidth + 2));444 AutoIndent Indent(P, FieldWidth + 2);445 P.formatLine(" {0}",446 formatSectionCharacteristics(P.getIndentLevel() + 6,447 SC.Characteristics, 3, " | "));448}449 450static void dumpSectionContrib(LinePrinter &P, const SectionContrib2 &SC,451 ArrayRef<std::string> SectionNames,452 uint32_t FieldWidth) {453 P.formatLine("SC2[{6}] | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "454 "crc = {4}, coff section = {5}",455 formatSegmentOffset(SC.Base.ISect, SC.Base.Off),456 fmtle(SC.Base.Size), fmtle(SC.Base.Imod), fmtle(SC.Base.DataCrc),457 fmtle(SC.Base.RelocCrc), fmtle(SC.ISectCoff));458 P.formatLine(" {0}",459 formatSectionCharacteristics(P.getIndentLevel() + 6,460 SC.Base.Characteristics, 3, " | "));461}462 463Error DumpOutputStyle::dumpModules() {464 printHeader(P, "Modules");465 466 if (File.isObj()) {467 printStreamNotValidForObj();468 return Error::success();469 }470 471 if (!getPdb().hasPDBDbiStream()) {472 printStreamNotPresent("DBI");473 return Error::success();474 }475 476 AutoIndent Indent(P);477 478 Expected<DbiStream &> StreamOrErr = getPdb().getPDBDbiStream();479 if (!StreamOrErr)480 return StreamOrErr.takeError();481 DbiStream &Stream = *StreamOrErr;482 483 const DbiModuleList &Modules = Stream.modules();484 return iterateSymbolGroups(485 File, PrintScope{P, 11},486 [&](uint32_t Modi, const SymbolGroup &Strings) -> Error {487 auto Desc = Modules.getModuleDescriptor(Modi);488 if (opts::dump::DumpSectionContribs) {489 auto SectionsOrErr = getSectionNames(getPdb());490 if (!SectionsOrErr)491 return SectionsOrErr.takeError();492 ArrayRef<std::string> Sections = *SectionsOrErr;493 dumpSectionContrib(P, Desc.getSectionContrib(), Sections, 0);494 }495 P.formatLine("Obj: `{0}`: ", Desc.getObjFileName());496 P.formatLine("debug stream: {0}, # files: {1}, has ec info: {2}",497 Desc.getModuleStreamIndex(), Desc.getNumberOfFiles(),498 Desc.hasECInfo());499 500 auto PdbPathOrErr = Stream.getECName(Desc.getPdbFilePathNameIndex());501 if (!PdbPathOrErr)502 return PdbPathOrErr.takeError();503 StringRef PdbFilePath = *PdbPathOrErr;504 505 auto SrcPathOrErr = Stream.getECName(Desc.getSourceFileNameIndex());506 if (!SrcPathOrErr)507 return SrcPathOrErr.takeError();508 StringRef SrcFilePath = *SrcPathOrErr;509 510 P.formatLine("pdb file ni: {0} `{1}`, src file ni: {2} `{3}`",511 Desc.getPdbFilePathNameIndex(), PdbFilePath,512 Desc.getSourceFileNameIndex(), SrcFilePath);513 return Error::success();514 });515}516 517Error DumpOutputStyle::dumpModuleFiles() {518 printHeader(P, "Files");519 520 if (File.isObj()) {521 printStreamNotValidForObj();522 return Error::success();523 }524 525 if (!getPdb().hasPDBDbiStream()) {526 printStreamNotPresent("DBI");527 return Error::success();528 }529 530 return iterateSymbolGroups(531 File, PrintScope{P, 11},532 [this](uint32_t Modi, const SymbolGroup &Strings) -> Error {533 Expected<DbiStream &> StreamOrErr = getPdb().getPDBDbiStream();534 if (!StreamOrErr)535 return StreamOrErr.takeError();536 DbiStream &Stream = *StreamOrErr;537 538 const DbiModuleList &Modules = Stream.modules();539 for (const auto &F : Modules.source_files(Modi)) {540 Strings.formatFromFileName(P, F);541 }542 return Error::success();543 });544}545 546Error DumpOutputStyle::dumpSymbolStats() {547 printHeader(P, "Module Stats");548 549 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {550 printStreamNotPresent("DBI");551 return Error::success();552 }553 554 StatCollection SymStats;555 StatCollection ChunkStats;556 PrintScope Scope(P, 2);557 558 if (Error Err = iterateSymbolGroups(559 File, Scope, [&](uint32_t Modi, const SymbolGroup &SG) -> Error {560 StatCollection SS = getSymbolStats(SG, SymStats);561 StatCollection CS = getChunkStats(SG, ChunkStats);562 563 if (!SG.getFile().isPdb())564 return Error::success();565 566 AutoIndent Indent(P);567 auto Modules = cantFail(File.pdb().getPDBDbiStream()).modules();568 uint32_t ModCount = Modules.getModuleCount();569 DbiModuleDescriptor Desc = Modules.getModuleDescriptor(Modi);570 uint32_t StreamIdx = Desc.getModuleStreamIndex();571 572 if (StreamIdx == kInvalidStreamIndex) {573 P.formatLine(574 "Mod {0} (debug info not present): [{1}]",575 fmt_align(Modi, AlignStyle::Right, NumDigits(ModCount)),576 Desc.getModuleName());577 return Error::success();578 }579 P.formatLine("Stream {0}, {1} bytes", StreamIdx,580 getPdb().getStreamByteSize(StreamIdx));581 582 printModuleDetailStats<SymbolKind>(P, "Symbols", SS);583 printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", CS);584 585 return Error::success();586 }))587 return Err;588 589 if (SymStats.Totals.Count > 0) {590 P.printLine(" Summary |");591 AutoIndent Indent(P, 4);592 printModuleDetailStats<SymbolKind>(P, "Symbols", SymStats);593 printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", ChunkStats);594 }595 596 return Error::success();597}598 599Error DumpOutputStyle::dumpTypeStats() {600 printHeader(P, "Type Record Stats");601 602 // Iterate the types, categorize by kind, accumulate size stats.603 StatCollection TypeStats;604 LazyRandomTypeCollection &Types =605 opts::dump::DumpTypeStats ? File.types() : File.ids();606 for (std::optional<TypeIndex> TI = Types.getFirst(); TI;607 TI = Types.getNext(*TI)) {608 CVType Type = Types.getType(*TI);609 TypeStats.update(uint32_t(Type.kind()), Type.length());610 }611 612 P.NewLine();613 P.formatLine(" Types");614 AutoIndent Indent(P);615 P.formatLine("{0,16}: {1,7} entries ({2,12:N} bytes, {3,7} avg)", "Total",616 TypeStats.Totals.Count, TypeStats.Totals.Size,617 (double)TypeStats.Totals.Size / TypeStats.Totals.Count);618 P.formatLine("{0}", fmt_repeat('-', 74));619 620 for (const auto &K : TypeStats.getStatsSortedBySize()) {621 P.formatLine("{0,16}: {1,7} entries ({2,12:N} bytes, {3,7} avg)",622 formatTypeLeafKind(TypeLeafKind(K.first)), K.second.Count,623 K.second.Size, (double)K.second.Size / K.second.Count);624 }625 return Error::success();626}627 628static bool isValidNamespaceIdentifier(StringRef S) {629 if (S.empty())630 return false;631 632 if (std::isdigit(S[0]))633 return false;634 635 return llvm::all_of(S, [](char C) { return std::isalnum(C); });636}637 638namespace {639constexpr uint32_t kNoneUdtKind = 0;640constexpr uint32_t kSimpleUdtKind = 1;641constexpr uint32_t kUnknownUdtKind = 2;642} // namespace643 644static std::string getUdtStatLabel(uint32_t Kind) {645 if (Kind == kNoneUdtKind)646 return "<none type>";647 648 if (Kind == kSimpleUdtKind)649 return "<simple type>";650 651 if (Kind == kUnknownUdtKind)652 return "<unknown type>";653 654 return formatTypeLeafKind(static_cast<TypeLeafKind>(Kind));655}656 657static uint32_t getLongestTypeLeafName(const StatCollection &Stats) {658 size_t L = 0;659 for (const auto &Stat : Stats.Individual) {660 std::string Label = getUdtStatLabel(Stat.first);661 L = std::max(L, Label.size());662 }663 return static_cast<uint32_t>(L);664}665 666Error DumpOutputStyle::dumpUdtStats() {667 printHeader(P, "S_UDT Record Stats");668 669 if (File.isPdb() && !getPdb().hasPDBGlobalsStream()) {670 printStreamNotPresent("Globals");671 return Error::success();672 }673 674 StatCollection UdtStats;675 StatCollection UdtTargetStats;676 AutoIndent Indent(P, 4);677 678 auto &TpiTypes = File.types();679 680 StringMap<StatCollection::Stat> NamespacedStats;681 682 size_t LongestNamespace = 0;683 auto HandleOneSymbol = [&](const CVSymbol &Sym) {684 if (Sym.kind() != SymbolKind::S_UDT)685 return;686 UdtStats.update(SymbolKind::S_UDT, Sym.length());687 688 UDTSym UDT = cantFail(SymbolDeserializer::deserializeAs<UDTSym>(Sym));689 690 uint32_t Kind = 0;691 uint32_t RecordSize = 0;692 693 if (UDT.Type.isNoneType())694 Kind = kNoneUdtKind;695 else if (UDT.Type.isSimple())696 Kind = kSimpleUdtKind;697 else if (std::optional<CVType> T = TpiTypes.tryGetType(UDT.Type)) {698 Kind = T->kind();699 RecordSize = T->length();700 } else701 Kind = kUnknownUdtKind;702 703 UdtTargetStats.update(Kind, RecordSize);704 705 size_t Pos = UDT.Name.find("::");706 if (Pos == StringRef::npos)707 return;708 709 StringRef Scope = UDT.Name.take_front(Pos);710 if (Scope.empty() || !isValidNamespaceIdentifier(Scope))711 return;712 713 LongestNamespace = std::max(LongestNamespace, Scope.size());714 NamespacedStats[Scope].update(RecordSize);715 };716 717 P.NewLine();718 719 if (File.isPdb()) {720 auto &SymbolRecords = cantFail(getPdb().getPDBSymbolStream());721 auto ExpGlobals = getPdb().getPDBGlobalsStream();722 if (!ExpGlobals)723 return ExpGlobals.takeError();724 725 for (uint32_t PubSymOff : ExpGlobals->getGlobalsTable()) {726 CVSymbol Sym = SymbolRecords.readRecord(PubSymOff);727 HandleOneSymbol(Sym);728 }729 } else {730 for (const auto &Sec : File.symbol_groups()) {731 for (const auto &SS : Sec.getDebugSubsections()) {732 if (SS.kind() != DebugSubsectionKind::Symbols)733 continue;734 735 DebugSymbolsSubsectionRef Symbols;736 BinaryStreamReader Reader(SS.getRecordData());737 cantFail(Symbols.initialize(Reader));738 for (const auto &S : Symbols)739 HandleOneSymbol(S);740 }741 }742 }743 744 LongestNamespace += StringRef(" namespace ''").size();745 size_t LongestTypeLeafKind = getLongestTypeLeafName(UdtTargetStats);746 size_t FieldWidth = std::max(LongestNamespace, LongestTypeLeafKind);747 748 // Compute the max number of digits for count and size fields, including comma749 // separators.750 StringRef CountHeader("Count");751 StringRef SizeHeader("Size");752 size_t CD = NumDigits(UdtStats.Totals.Count);753 CD += (CD - 1) / 3;754 CD = std::max(CD, CountHeader.size());755 756 size_t SD = NumDigits(UdtStats.Totals.Size);757 SD += (SD - 1) / 3;758 SD = std::max(SD, SizeHeader.size());759 760 uint32_t TableWidth = FieldWidth + 3 + CD + 2 + SD + 1;761 762 P.formatLine("{0} | {1} {2}",763 fmt_align("Record Kind", AlignStyle::Right, FieldWidth),764 fmt_align(CountHeader, AlignStyle::Right, CD),765 fmt_align(SizeHeader, AlignStyle::Right, SD));766 767 P.formatLine("{0}", fmt_repeat('-', TableWidth));768 for (const auto &Stat : UdtTargetStats.getStatsSortedBySize()) {769 std::string Label = getUdtStatLabel(Stat.first);770 P.formatLine("{0} | {1:N} {2:N}",771 fmt_align(Label, AlignStyle::Right, FieldWidth),772 fmt_align(Stat.second.Count, AlignStyle::Right, CD),773 fmt_align(Stat.second.Size, AlignStyle::Right, SD));774 }775 P.formatLine("{0}", fmt_repeat('-', TableWidth));776 P.formatLine("{0} | {1:N} {2:N}",777 fmt_align("Total (S_UDT)", AlignStyle::Right, FieldWidth),778 fmt_align(UdtStats.Totals.Count, AlignStyle::Right, CD),779 fmt_align(UdtStats.Totals.Size, AlignStyle::Right, SD));780 P.formatLine("{0}", fmt_repeat('-', TableWidth));781 struct StrAndStat {782 StringRef Key;783 StatCollection::Stat Stat;784 };785 786 // Print namespace stats in descending order of size.787 std::vector<StrAndStat> NamespacedStatsSorted;788 for (const auto &Stat : NamespacedStats)789 NamespacedStatsSorted.push_back({Stat.getKey(), Stat.second});790 llvm::stable_sort(NamespacedStatsSorted,791 [](const StrAndStat &L, const StrAndStat &R) {792 return L.Stat.Size > R.Stat.Size;793 });794 for (const auto &Stat : NamespacedStatsSorted) {795 std::string Label = std::string(formatv("namespace '{0}'", Stat.Key));796 P.formatLine("{0} | {1:N} {2:N}",797 fmt_align(Label, AlignStyle::Right, FieldWidth),798 fmt_align(Stat.Stat.Count, AlignStyle::Right, CD),799 fmt_align(Stat.Stat.Size, AlignStyle::Right, SD));800 }801 return Error::success();802}803 804static void typesetLinesAndColumns(LinePrinter &P, uint32_t Start,805 const LineColumnEntry &E) {806 const uint32_t kMaxCharsPerLineNumber = 4; // 4 digit line number807 uint32_t MinColumnWidth = kMaxCharsPerLineNumber + 5;808 809 // Let's try to keep it under 100 characters810 constexpr uint32_t kMaxRowLength = 100;811 // At least 3 spaces between columns.812 uint32_t ColumnsPerRow = kMaxRowLength / (MinColumnWidth + 3);813 uint32_t ItemsLeft = E.LineNumbers.size();814 auto LineIter = E.LineNumbers.begin();815 while (ItemsLeft != 0) {816 uint32_t RowColumns = std::min(ItemsLeft, ColumnsPerRow);817 for (uint32_t I = 0; I < RowColumns; ++I) {818 LineInfo Line(LineIter->Flags);819 std::string LineStr;820 if (Line.isAlwaysStepInto())821 LineStr = "ASI";822 else if (Line.isNeverStepInto())823 LineStr = "NSI";824 else825 LineStr = utostr(Line.getStartLine());826 char Statement = Line.isStatement() ? ' ' : '!';827 P.format("{0} {1:X-} {2} ",828 fmt_align(LineStr, AlignStyle::Right, kMaxCharsPerLineNumber),829 fmt_align(Start + LineIter->Offset, AlignStyle::Right, 8, '0'),830 Statement);831 ++LineIter;832 --ItemsLeft;833 }834 P.NewLine();835 }836}837 838Error DumpOutputStyle::dumpLines() {839 printHeader(P, "Lines");840 841 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {842 printStreamNotPresent("DBI");843 return Error::success();844 }845 846 uint32_t LastModi = UINT32_MAX;847 uint32_t LastNameIndex = UINT32_MAX;848 return iterateModuleSubsections<DebugLinesSubsectionRef>(849 File, PrintScope{P, 4},850 [this, &LastModi,851 &LastNameIndex](uint32_t Modi, const SymbolGroup &Strings,852 DebugLinesSubsectionRef &Lines) -> Error {853 uint16_t Segment = Lines.header()->RelocSegment;854 uint32_t Begin = Lines.header()->RelocOffset;855 uint32_t End = Begin + Lines.header()->CodeSize;856 for (const auto &Block : Lines) {857 if (LastModi != Modi || LastNameIndex != Block.NameIndex) {858 LastModi = Modi;859 LastNameIndex = Block.NameIndex;860 Strings.formatFromChecksumsOffset(P, Block.NameIndex);861 }862 863 AutoIndent Indent(P, 2);864 P.formatLine("{0:X-4}:{1:X-8}-{2:X-8}, ", Segment, Begin, End);865 uint32_t Count = Block.LineNumbers.size();866 if (Lines.hasColumnInfo())867 P.format("line/column/addr entries = {0}", Count);868 else869 P.format("line/addr entries = {0}", Count);870 871 P.NewLine();872 typesetLinesAndColumns(P, Begin, Block);873 }874 return Error::success();875 });876}877 878Error DumpOutputStyle::dumpInlineeLines() {879 printHeader(P, "Inlinee Lines");880 881 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {882 printStreamNotPresent("DBI");883 return Error::success();884 }885 886 return iterateModuleSubsections<DebugInlineeLinesSubsectionRef>(887 File, PrintScope{P, 2},888 [this](uint32_t Modi, const SymbolGroup &Strings,889 DebugInlineeLinesSubsectionRef &Lines) -> Error {890 P.formatLine("{0,+8} | {1,+5} | {2}", "Inlinee", "Line", "Source File");891 for (const auto &Entry : Lines) {892 P.formatLine("{0,+8} | {1,+5} | ", Entry.Header->Inlinee,893 fmtle(Entry.Header->SourceLineNum));894 Strings.formatFromChecksumsOffset(P, Entry.Header->FileID, true);895 for (const auto &ExtraFileID : Entry.ExtraFiles) {896 P.formatLine(" ");897 Strings.formatFromChecksumsOffset(P, ExtraFileID, true);898 }899 }900 P.NewLine();901 return Error::success();902 });903}904 905Error DumpOutputStyle::dumpXmi() {906 printHeader(P, "Cross Module Imports");907 908 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {909 printStreamNotPresent("DBI");910 return Error::success();911 }912 913 return iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>(914 File, PrintScope{P, 2},915 [this](uint32_t Modi, const SymbolGroup &Strings,916 DebugCrossModuleImportsSubsectionRef &Imports) -> Error {917 P.formatLine("{0,=32} | {1}", "Imported Module", "Type IDs");918 919 for (const auto &Xmi : Imports) {920 auto ExpectedModule =921 Strings.getNameFromStringTable(Xmi.Header->ModuleNameOffset);922 StringRef Module;923 SmallString<32> ModuleStorage;924 if (!ExpectedModule) {925 Module = "(unknown module)";926 consumeError(ExpectedModule.takeError());927 } else928 Module = *ExpectedModule;929 if (Module.size() > 32) {930 ModuleStorage = "...";931 ModuleStorage += Module.take_back(32 - 3);932 Module = ModuleStorage;933 }934 std::vector<std::string> TIs;935 for (const auto I : Xmi.Imports)936 TIs.push_back(std::string(formatv("{0,+10:X+}", fmtle(I))));937 std::string Result =938 typesetItemList(TIs, P.getIndentLevel() + 35, 12, " ");939 P.formatLine("{0,+32} | {1}", Module, Result);940 }941 return Error::success();942 });943}944 945Error DumpOutputStyle::dumpXme() {946 printHeader(P, "Cross Module Exports");947 948 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {949 printStreamNotPresent("DBI");950 return Error::success();951 }952 953 return iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>(954 File, PrintScope{P, 2},955 [this](uint32_t Modi, const SymbolGroup &Strings,956 DebugCrossModuleExportsSubsectionRef &Exports) -> Error {957 P.formatLine("{0,-10} | {1}", "Local ID", "Global ID");958 for (const auto &Export : Exports) {959 P.formatLine("{0,+10:X+} | {1}", TypeIndex(Export.Local),960 TypeIndex(Export.Global));961 }962 return Error::success();963 });964}965 966std::string formatFrameType(object::frame_type FT) {967 switch (FT) {968 case object::frame_type::Fpo:969 return "FPO";970 case object::frame_type::NonFpo:971 return "Non-FPO";972 case object::frame_type::Trap:973 return "Trap";974 case object::frame_type::Tss:975 return "TSS";976 }977 return "<unknown>";978}979 980Error DumpOutputStyle::dumpOldFpo(PDBFile &File) {981 printHeader(P, "Old FPO Data");982 983 ExitOnError Err("Error dumping old fpo data:");984 DbiStream &Dbi = Err(File.getPDBDbiStream());985 986 if (!Dbi.hasOldFpoRecords()) {987 printStreamNotPresent("FPO");988 return Error::success();989 }990 991 const FixedStreamArray<object::FpoData>& Records = Dbi.getOldFpoRecords();992 993 P.printLine(" RVA | Code | Locals | Params | Prolog | Saved Regs | Use "994 "BP | Has SEH | Frame Type");995 996 for (const object::FpoData &FD : Records) {997 P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,6} | {5,10} | {6,6} | "998 "{7,7} | {8,9}",999 uint32_t(FD.Offset), uint32_t(FD.Size), uint32_t(FD.NumLocals),1000 uint32_t(FD.NumParams), FD.getPrologSize(),1001 FD.getNumSavedRegs(), FD.useBP(), FD.hasSEH(),1002 formatFrameType(FD.getFP()));1003 }1004 return Error::success();1005}1006 1007Error DumpOutputStyle::dumpNewFpo(PDBFile &File) {1008 printHeader(P, "New FPO Data");1009 1010 ExitOnError Err("Error dumping new fpo data:");1011 DbiStream &Dbi = Err(File.getPDBDbiStream());1012 1013 if (!Dbi.hasNewFpoRecords()) {1014 printStreamNotPresent("New FPO");1015 return Error::success();1016 }1017 1018 const DebugFrameDataSubsectionRef& FDS = Dbi.getNewFpoRecords();1019 1020 P.printLine(" RVA | Code | Locals | Params | Stack | Prolog | Saved Regs "1021 "| Has SEH | Has C++EH | Start | Program");1022 for (const FrameData &FD : FDS) {1023 bool IsFuncStart = FD.Flags & FrameData::IsFunctionStart;1024 bool HasEH = FD.Flags & FrameData::HasEH;1025 bool HasSEH = FD.Flags & FrameData::HasSEH;1026 1027 auto &StringTable = Err(File.getStringTable());1028 1029 auto Program = Err(StringTable.getStringForID(FD.FrameFunc));1030 P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,5} | {5,6} | {6,10} | "1031 "{7,7} | {8,9} | {9,5} | {10}",1032 uint32_t(FD.RvaStart), uint32_t(FD.CodeSize),1033 uint32_t(FD.LocalSize), uint32_t(FD.ParamsSize),1034 uint32_t(FD.MaxStackSize), uint16_t(FD.PrologSize),1035 uint16_t(FD.SavedRegsSize), HasSEH, HasEH, IsFuncStart,1036 Program);1037 }1038 return Error::success();1039}1040 1041Error DumpOutputStyle::dumpFpo() {1042 if (!File.isPdb()) {1043 printStreamNotValidForObj();1044 return Error::success();1045 }1046 1047 PDBFile &File = getPdb();1048 if (!File.hasPDBDbiStream()) {1049 printStreamNotPresent("DBI");1050 return Error::success();1051 }1052 1053 if (auto EC = dumpOldFpo(File))1054 return EC;1055 if (auto EC = dumpNewFpo(File))1056 return EC;1057 return Error::success();1058}1059 1060Error DumpOutputStyle::dumpStringTableFromPdb() {1061 AutoIndent Indent(P);1062 auto IS = getPdb().getStringTable();1063 if (!IS) {1064 P.formatLine("Not present in file");1065 consumeError(IS.takeError());1066 return Error::success();1067 }1068 1069 if (opts::dump::DumpStringTable) {1070 if (IS->name_ids().empty())1071 P.formatLine("Empty");1072 else {1073 auto MaxID = llvm::max_element(IS->name_ids());1074 uint32_t Digits = NumDigits(*MaxID);1075 1076 P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits),1077 "String");1078 1079 std::vector<uint32_t> SortedIDs(IS->name_ids().begin(),1080 IS->name_ids().end());1081 llvm::sort(SortedIDs);1082 for (uint32_t I : SortedIDs) {1083 auto ES = IS->getStringForID(I);1084 llvm::SmallString<32> Str;1085 if (!ES) {1086 consumeError(ES.takeError());1087 Str = "Error reading string";1088 } else if (!ES->empty()) {1089 Str.append("'");1090 Str.append(*ES);1091 Str.append("'");1092 }1093 1094 if (!Str.empty())1095 P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits),1096 Str);1097 }1098 }1099 }1100 1101 if (opts::dump::DumpStringTableDetails) {1102 P.NewLine();1103 {1104 P.printLine("String Table Header:");1105 AutoIndent Indent(P);1106 P.formatLine("Signature: {0}", IS->getSignature());1107 P.formatLine("Hash Version: {0}", IS->getHashVersion());1108 P.formatLine("Name Buffer Size: {0}", IS->getByteSize());1109 P.NewLine();1110 }1111 1112 BinaryStreamRef NameBuffer = IS->getStringTable().getBuffer();1113 ArrayRef<uint8_t> Contents;1114 cantFail(NameBuffer.readBytes(0, NameBuffer.getLength(), Contents));1115 P.formatBinary("Name Buffer", Contents, 0);1116 P.NewLine();1117 {1118 P.printLine("Hash Table:");1119 AutoIndent Indent(P);1120 P.formatLine("Bucket Count: {0}", IS->name_ids().size());1121 for (const auto &Entry : enumerate(IS->name_ids()))1122 P.formatLine("Bucket[{0}] : {1}", Entry.index(),1123 uint32_t(Entry.value()));1124 P.formatLine("Name Count: {0}", IS->getNameCount());1125 }1126 }1127 return Error::success();1128}1129 1130Error DumpOutputStyle::dumpStringTableFromObj() {1131 return iterateModuleSubsections<DebugStringTableSubsectionRef>(1132 File, PrintScope{P, 4},1133 [&](uint32_t Modi, const SymbolGroup &Strings,1134 DebugStringTableSubsectionRef &Strings2) -> Error {1135 BinaryStreamRef StringTableBuffer = Strings2.getBuffer();1136 BinaryStreamReader Reader(StringTableBuffer);1137 while (Reader.bytesRemaining() > 0) {1138 StringRef Str;1139 uint32_t Offset = Reader.getOffset();1140 cantFail(Reader.readCString(Str));1141 if (Str.empty())1142 continue;1143 1144 P.formatLine("{0} | {1}", fmt_align(Offset, AlignStyle::Right, 4),1145 Str);1146 }1147 return Error::success();1148 });1149}1150 1151Error DumpOutputStyle::dumpNamedStreams() {1152 printHeader(P, "Named Streams");1153 1154 if (File.isObj()) {1155 printStreamNotValidForObj();1156 return Error::success();1157 }1158 1159 AutoIndent Indent(P);1160 ExitOnError Err("Invalid PDB File: ");1161 1162 auto &IS = Err(File.pdb().getPDBInfoStream());1163 const NamedStreamMap &NS = IS.getNamedStreams();1164 for (const auto &Entry : NS.entries()) {1165 P.printLine(Entry.getKey());1166 AutoIndent Indent2(P, 2);1167 P.formatLine("Index: {0}", Entry.getValue());1168 P.formatLine("Size in bytes: {0}",1169 File.pdb().getStreamByteSize(Entry.getValue()));1170 }1171 1172 return Error::success();1173}1174 1175Error DumpOutputStyle::dumpStringTable() {1176 printHeader(P, "String Table");1177 1178 if (File.isPdb())1179 return dumpStringTableFromPdb();1180 1181 return dumpStringTableFromObj();1182}1183 1184static void buildDepSet(LazyRandomTypeCollection &Types,1185 ArrayRef<TypeIndex> Indices,1186 std::map<TypeIndex, CVType> &DepSet) {1187 SmallVector<TypeIndex, 4> DepList;1188 for (const auto &I : Indices) {1189 TypeIndex TI(I);1190 if (DepSet.find(TI) != DepSet.end() || TI.isSimple() || TI.isNoneType())1191 continue;1192 1193 CVType Type = Types.getType(TI);1194 DepSet[TI] = Type;1195 codeview::discoverTypeIndices(Type, DepList);1196 buildDepSet(Types, DepList, DepSet);1197 }1198}1199 1200static void1201dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types,1202 TypeReferenceTracker *RefTracker, uint32_t NumTypeRecords,1203 uint32_t NumHashBuckets,1204 FixedStreamArray<support::ulittle32_t> HashValues,1205 TpiStream *Stream, bool Bytes, bool Extras) {1206 1207 Printer.formatLine("Showing {0:N} records", NumTypeRecords);1208 uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + NumTypeRecords);1209 1210 MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker,1211 NumHashBuckets, HashValues, Stream);1212 1213 if (auto EC = codeview::visitTypeStream(Types, V)) {1214 Printer.formatLine("An error occurred dumping type records: {0}",1215 toString(std::move(EC)));1216 }1217}1218 1219static void dumpPartialTypeStream(LinePrinter &Printer,1220 LazyRandomTypeCollection &Types,1221 TypeReferenceTracker *RefTracker,1222 TpiStream &Stream, ArrayRef<TypeIndex> TiList,1223 bool Bytes, bool Extras, bool Deps) {1224 uint32_t Width =1225 NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords());1226 1227 MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker,1228 Stream.getNumHashBuckets(), Stream.getHashValues(),1229 &Stream);1230 1231 if (opts::dump::DumpTypeDependents) {1232 // If we need to dump all dependents, then iterate each index and find1233 // all dependents, adding them to a map ordered by TypeIndex.1234 std::map<TypeIndex, CVType> DepSet;1235 buildDepSet(Types, TiList, DepSet);1236 1237 Printer.formatLine(1238 "Showing {0:N} records and their dependents ({1:N} records total)",1239 TiList.size(), DepSet.size());1240 1241 for (auto &Dep : DepSet) {1242 if (auto EC = codeview::visitTypeRecord(Dep.second, Dep.first, V))1243 Printer.formatLine("An error occurred dumping type record {0}: {1}",1244 Dep.first, toString(std::move(EC)));1245 }1246 } else {1247 Printer.formatLine("Showing {0:N} records.", TiList.size());1248 1249 for (const auto &I : TiList) {1250 TypeIndex TI(I);1251 if (TI.isSimple()) {1252 Printer.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Width),1253 Types.getTypeName(TI));1254 } else if (std::optional<CVType> Type = Types.tryGetType(TI)) {1255 if (auto EC = codeview::visitTypeRecord(*Type, TI, V))1256 Printer.formatLine("An error occurred dumping type record {0}: {1}",1257 TI, toString(std::move(EC)));1258 } else {1259 Printer.formatLine("Type {0} doesn't exist in TPI stream", TI);1260 }1261 }1262 }1263}1264 1265Error DumpOutputStyle::dumpTypesFromObjectFile() {1266 LazyRandomTypeCollection Types(100);1267 1268 for (const auto &S : getObj().sections()) {1269 Expected<StringRef> NameOrErr = S.getName();1270 if (!NameOrErr)1271 return NameOrErr.takeError();1272 StringRef SectionName = *NameOrErr;1273 1274 // .debug$T is a standard CodeView type section, while .debug$P is the same1275 // format but used for MSVC precompiled header object files.1276 if (SectionName == ".debug$T")1277 printHeader(P, "Types (.debug$T)");1278 else if (SectionName == ".debug$P")1279 printHeader(P, "Precompiled Types (.debug$P)");1280 else1281 continue;1282 1283 Expected<StringRef> ContentsOrErr = S.getContents();1284 if (!ContentsOrErr)1285 return ContentsOrErr.takeError();1286 1287 uint32_t Magic;1288 BinaryStreamReader Reader(*ContentsOrErr, llvm::endianness::little);1289 if (auto EC = Reader.readInteger(Magic))1290 return EC;1291 if (Magic != COFF::DEBUG_SECTION_MAGIC)1292 return make_error<StringError>("Invalid CodeView debug section.",1293 inconvertibleErrorCode());1294 1295 Types.reset(Reader, 100);1296 1297 if (opts::dump::DumpTypes) {1298 dumpFullTypeStream(P, Types, RefTracker.get(), 0, 0, {}, nullptr,1299 opts::dump::DumpTypeData, false);1300 } else if (opts::dump::DumpTypeExtras) {1301 auto LocalHashes = LocallyHashedType::hashTypeCollection(Types);1302 auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types);1303 assert(LocalHashes.size() == GlobalHashes.size());1304 1305 P.formatLine("Local / Global hashes:");1306 TypeIndex TI(TypeIndex::FirstNonSimpleIndex);1307 for (auto H : zip(LocalHashes, GlobalHashes)) {1308 AutoIndent Indent2(P);1309 LocallyHashedType &L = std::get<0>(H);1310 GloballyHashedType &G = std::get<1>(H);1311 1312 P.formatLine("TI: {0}, LocalHash: {1:X}, GlobalHash: {2}", TI, L, G);1313 1314 ++TI;1315 }1316 P.NewLine();1317 }1318 }1319 1320 return Error::success();1321}1322 1323Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {1324 assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);1325 1326 if (StreamIdx == StreamTPI) {1327 printHeader(P, "Types (TPI Stream)");1328 } else if (StreamIdx == StreamIPI) {1329 printHeader(P, "Types (IPI Stream)");1330 }1331 1332 assert(!File.isObj());1333 1334 bool Present = false;1335 bool DumpTypes = false;1336 bool DumpBytes = false;1337 bool DumpExtras = false;1338 std::vector<uint32_t> Indices;1339 if (StreamIdx == StreamTPI) {1340 Present = getPdb().hasPDBTpiStream();1341 DumpTypes = opts::dump::DumpTypes;1342 DumpBytes = opts::dump::DumpTypeData;1343 DumpExtras = opts::dump::DumpTypeExtras;1344 Indices.assign(opts::dump::DumpTypeIndex.begin(),1345 opts::dump::DumpTypeIndex.end());1346 } else if (StreamIdx == StreamIPI) {1347 Present = getPdb().hasPDBIpiStream();1348 DumpTypes = opts::dump::DumpIds;1349 DumpBytes = opts::dump::DumpIdData;1350 DumpExtras = opts::dump::DumpIdExtras;1351 Indices.assign(opts::dump::DumpIdIndex.begin(),1352 opts::dump::DumpIdIndex.end());1353 }1354 1355 if (!Present) {1356 printStreamNotPresent(StreamIdx == StreamTPI ? "TPI" : "IPI");1357 return Error::success();1358 }1359 1360 AutoIndent Indent(P);1361 ExitOnError Err("Unexpected error processing types: ");1362 1363 auto &Stream = Err((StreamIdx == StreamTPI) ? getPdb().getPDBTpiStream()1364 : getPdb().getPDBIpiStream());1365 1366 auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids();1367 1368 // Only emit notes about referenced/unreferenced for types.1369 TypeReferenceTracker *MaybeTracker =1370 (StreamIdx == StreamTPI) ? RefTracker.get() : nullptr;1371 1372 // Enable resolving forward decls.1373 Stream.buildHashMap();1374 1375 if (DumpTypes || !Indices.empty()) {1376 if (Indices.empty())1377 dumpFullTypeStream(P, Types, MaybeTracker, Stream.getNumTypeRecords(),1378 Stream.getNumHashBuckets(), Stream.getHashValues(),1379 &Stream, DumpBytes, DumpExtras);1380 else {1381 std::vector<TypeIndex> TiList(Indices.begin(), Indices.end());1382 dumpPartialTypeStream(P, Types, MaybeTracker, Stream, TiList, DumpBytes,1383 DumpExtras, opts::dump::DumpTypeDependents);1384 }1385 }1386 1387 if (DumpExtras) {1388 P.NewLine();1389 1390 P.formatLine("Header Version: {0}",1391 static_cast<uint32_t>(Stream.getTpiVersion()));1392 P.formatLine("Hash Stream Index: {0}", Stream.getTypeHashStreamIndex());1393 P.formatLine("Aux Hash Stream Index: {0}",1394 Stream.getTypeHashStreamAuxIndex());1395 P.formatLine("Hash Key Size: {0}", Stream.getHashKeySize());1396 P.formatLine("Num Hash Buckets: {0}", Stream.getNumHashBuckets());1397 1398 auto IndexOffsets = Stream.getTypeIndexOffsets();1399 P.formatLine("Type Index Offsets:");1400 for (const auto &IO : IndexOffsets) {1401 AutoIndent Indent2(P);1402 P.formatLine("TI: {0}, Offset: {1}", IO.Type, fmtle(IO.Offset));1403 }1404 1405 if (getPdb().hasPDBStringTable()) {1406 P.NewLine();1407 P.formatLine("Hash Adjusters:");1408 auto &Adjusters = Stream.getHashAdjusters();1409 auto &Strings = Err(getPdb().getStringTable());1410 for (const auto &A : Adjusters) {1411 AutoIndent Indent2(P);1412 auto ExpectedStr = Strings.getStringForID(A.first);1413 TypeIndex TI(A.second);1414 if (ExpectedStr)1415 P.formatLine("`{0}` -> {1}", *ExpectedStr, TI);1416 else {1417 P.formatLine("unknown str id ({0}) -> {1}", A.first, TI);1418 consumeError(ExpectedStr.takeError());1419 }1420 }1421 }1422 }1423 return Error::success();1424}1425 1426Error DumpOutputStyle::dumpModuleSymsForObj() {1427 printHeader(P, "Symbols");1428 1429 AutoIndent Indent(P);1430 1431 auto &Types = File.types();1432 1433 SymbolVisitorCallbackPipeline Pipeline;1434 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::ObjectFile);1435 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types, Types);1436 1437 Pipeline.addCallbackToPipeline(Deserializer);1438 Pipeline.addCallbackToPipeline(Dumper);1439 CVSymbolVisitor Visitor(Pipeline);1440 1441 return iterateModuleSubsections<DebugSymbolsSubsectionRef>(1442 File, PrintScope{P, 2},1443 [&](uint32_t Modi, const SymbolGroup &Strings,1444 DebugSymbolsSubsectionRef &Symbols) -> Error {1445 Dumper.setSymbolGroup(&Strings);1446 for (auto Symbol : Symbols) {1447 if (auto EC = Visitor.visitSymbolRecord(Symbol)) {1448 return EC;1449 }1450 }1451 return Error::success();1452 });1453}1454 1455Error DumpOutputStyle::dumpModuleSymsForPdb() {1456 printHeader(P, "Symbols");1457 1458 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {1459 printStreamNotPresent("DBI");1460 return Error::success();1461 }1462 1463 AutoIndent Indent(P);1464 1465 auto &Ids = File.ids();1466 auto &Types = File.types();1467 1468 return iterateSymbolGroups(1469 File, PrintScope{P, 2},1470 [&](uint32_t I, const SymbolGroup &Strings) -> Error {1471 auto ExpectedModS = getModuleDebugStream(File.pdb(), I);1472 if (!ExpectedModS) {1473 P.formatLine("Error loading module stream {0}. {1}", I,1474 toString(ExpectedModS.takeError()));1475 return Error::success();1476 }1477 1478 ModuleDebugStreamRef &ModS = *ExpectedModS;1479 1480 SymbolVisitorCallbackPipeline Pipeline;1481 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);1482 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Strings,1483 Ids, Types);1484 1485 Pipeline.addCallbackToPipeline(Deserializer);1486 Pipeline.addCallbackToPipeline(Dumper);1487 CVSymbolVisitor Visitor(Pipeline);1488 auto SS = ModS.getSymbolsSubstream();1489 if (opts::Filters.SymbolOffset) {1490 CVSymbolVisitor::FilterOptions Filter;1491 Filter.SymbolOffset = opts::Filters.SymbolOffset;1492 Filter.ParentRecursiveDepth = opts::Filters.ParentRecurseDepth;1493 Filter.ChildRecursiveDepth = opts::Filters.ChildrenRecurseDepth;1494 if (auto EC = Visitor.visitSymbolStreamFiltered(ModS.getSymbolArray(),1495 Filter)) {1496 P.formatLine("Error while processing symbol records. {0}",1497 toStringWithoutConsuming(EC));1498 return EC;1499 }1500 } else if (auto EC = Visitor.visitSymbolStream(ModS.getSymbolArray(),1501 SS.Offset)) {1502 P.formatLine("Error while processing symbol records. {0}",1503 toStringWithoutConsuming(EC));1504 return EC;1505 }1506 return Error::success();1507 });1508}1509 1510Error DumpOutputStyle::dumpTypeRefStats() {1511 printHeader(P, "Type Reference Statistics");1512 AutoIndent Indent(P);1513 1514 // Sum the byte size of all type records, and the size and count of all1515 // referenced records.1516 size_t TotalRecs = File.types().size();1517 size_t RefRecs = 0;1518 size_t TotalBytes = 0;1519 size_t RefBytes = 0;1520 auto &Types = File.types();1521 for (std::optional<TypeIndex> TI = Types.getFirst(); TI;1522 TI = Types.getNext(*TI)) {1523 CVType Type = File.types().getType(*TI);1524 TotalBytes += Type.length();1525 if (RefTracker->isTypeReferenced(*TI)) {1526 ++RefRecs;1527 RefBytes += Type.length();1528 }1529 }1530 1531 P.formatLine("Records referenced: {0:N} / {1:N} {2:P}", RefRecs, TotalRecs,1532 (double)RefRecs / TotalRecs);1533 P.formatLine("Bytes referenced: {0:N} / {1:N} {2:P}", RefBytes, TotalBytes,1534 (double)RefBytes / TotalBytes);1535 1536 return Error::success();1537}1538 1539Error DumpOutputStyle::dumpGSIRecords() {1540 printHeader(P, "GSI Records");1541 1542 if (File.isObj()) {1543 printStreamNotValidForObj();1544 return Error::success();1545 }1546 1547 if (!getPdb().hasPDBSymbolStream()) {1548 printStreamNotPresent("GSI Common Symbol");1549 return Error::success();1550 }1551 1552 AutoIndent Indent(P);1553 1554 auto &Records = cantFail(getPdb().getPDBSymbolStream());1555 auto &Types = File.types();1556 auto &Ids = File.ids();1557 1558 P.printLine("Records");1559 SymbolVisitorCallbackPipeline Pipeline;1560 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);1561 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);1562 1563 Pipeline.addCallbackToPipeline(Deserializer);1564 Pipeline.addCallbackToPipeline(Dumper);1565 CVSymbolVisitor Visitor(Pipeline);1566 1567 BinaryStreamRef SymStream = Records.getSymbolArray().getUnderlyingStream();1568 if (auto E = Visitor.visitSymbolStream(Records.getSymbolArray(), 0))1569 return E;1570 return Error::success();1571}1572 1573Error DumpOutputStyle::dumpGlobals() {1574 printHeader(P, "Global Symbols");1575 1576 if (File.isObj()) {1577 printStreamNotValidForObj();1578 return Error::success();1579 }1580 1581 if (!getPdb().hasPDBGlobalsStream()) {1582 printStreamNotPresent("Globals");1583 return Error::success();1584 }1585 1586 AutoIndent Indent(P);1587 ExitOnError Err("Error dumping globals stream: ");1588 auto &Globals = Err(getPdb().getPDBGlobalsStream());1589 1590 if (opts::dump::DumpGlobalNames.empty()) {1591 const GSIHashTable &Table = Globals.getGlobalsTable();1592 Err(dumpSymbolsFromGSI(Table, opts::dump::DumpGlobalExtras));1593 } else {1594 SymbolStream &SymRecords = cantFail(getPdb().getPDBSymbolStream());1595 auto &Types = File.types();1596 auto &Ids = File.ids();1597 1598 SymbolVisitorCallbackPipeline Pipeline;1599 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);1600 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);1601 1602 Pipeline.addCallbackToPipeline(Deserializer);1603 Pipeline.addCallbackToPipeline(Dumper);1604 CVSymbolVisitor Visitor(Pipeline);1605 1606 using ResultEntryType = std::pair<uint32_t, CVSymbol>;1607 for (StringRef Name : opts::dump::DumpGlobalNames) {1608 AutoIndent Indent(P);1609 P.formatLine("Global Name `{0}`", Name);1610 std::vector<ResultEntryType> Results =1611 Globals.findRecordsByName(Name, SymRecords);1612 if (Results.empty()) {1613 AutoIndent Indent(P);1614 P.printLine("(no matching records found)");1615 continue;1616 }1617 1618 for (ResultEntryType Result : Results) {1619 if (auto E = Visitor.visitSymbolRecord(Result.second, Result.first))1620 return E;1621 }1622 }1623 }1624 return Error::success();1625}1626 1627Error DumpOutputStyle::dumpPublics() {1628 printHeader(P, "Public Symbols");1629 1630 if (File.isObj()) {1631 printStreamNotValidForObj();1632 return Error::success();1633 }1634 1635 if (!getPdb().hasPDBPublicsStream()) {1636 printStreamNotPresent("Publics");1637 return Error::success();1638 }1639 1640 AutoIndent Indent(P);1641 ExitOnError Err("Error dumping publics stream: ");1642 auto &Publics = Err(getPdb().getPDBPublicsStream());1643 1644 const GSIHashTable &PublicsTable = Publics.getPublicsTable();1645 if (opts::dump::DumpPublicExtras) {1646 P.printLine("Publics Header");1647 AutoIndent Indent(P);1648 P.formatLine("sym hash = {0}, thunk table addr = {1}", Publics.getSymHash(),1649 formatSegmentOffset(Publics.getThunkTableSection(),1650 Publics.getThunkTableOffset()));1651 }1652 Err(dumpSymbolsFromGSI(PublicsTable, opts::dump::DumpPublicExtras));1653 1654 // Skip the rest if we aren't dumping extras.1655 if (!opts::dump::DumpPublicExtras)1656 return Error::success();1657 1658 P.formatLine("Address Map");1659 {1660 // These are offsets into the publics stream sorted by secidx:secrel.1661 AutoIndent Indent2(P);1662 for (uint32_t Addr : Publics.getAddressMap())1663 P.formatLine("off = {0}", Addr);1664 }1665 1666 // The thunk map is optional debug info used for ILT thunks.1667 if (!Publics.getThunkMap().empty()) {1668 P.formatLine("Thunk Map");1669 AutoIndent Indent2(P);1670 for (uint32_t Addr : Publics.getThunkMap())1671 P.formatLine("{0:x8}", Addr);1672 }1673 1674 // The section offsets table appears to be empty when incremental linking1675 // isn't in use.1676 if (!Publics.getSectionOffsets().empty()) {1677 P.formatLine("Section Offsets");1678 AutoIndent Indent2(P);1679 for (const SectionOffset &SO : Publics.getSectionOffsets())1680 P.formatLine("{0:x4}:{1:x8}", uint16_t(SO.Isect), uint32_t(SO.Off));1681 }1682 1683 return Error::success();1684}1685 1686Error DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable &Table,1687 bool HashExtras) {1688 auto ExpectedSyms = getPdb().getPDBSymbolStream();1689 if (!ExpectedSyms)1690 return ExpectedSyms.takeError();1691 auto &Types = File.types();1692 auto &Ids = File.ids();1693 1694 if (HashExtras) {1695 P.printLine("GSI Header");1696 AutoIndent Indent(P);1697 P.formatLine("sig = {0:X}, hdr = {1:X}, hr size = {2}, num buckets = {3}",1698 Table.getVerSignature(), Table.getVerHeader(),1699 Table.getHashRecordSize(), Table.getNumBuckets());1700 }1701 1702 {1703 P.printLine("Records");1704 SymbolVisitorCallbackPipeline Pipeline;1705 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);1706 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);1707 1708 Pipeline.addCallbackToPipeline(Deserializer);1709 Pipeline.addCallbackToPipeline(Dumper);1710 CVSymbolVisitor Visitor(Pipeline);1711 1712 1713 BinaryStreamRef SymStream =1714 ExpectedSyms->getSymbolArray().getUnderlyingStream();1715 for (uint32_t PubSymOff : Table) {1716 Expected<CVSymbol> Sym = readSymbolFromStream(SymStream, PubSymOff);1717 if (!Sym)1718 return Sym.takeError();1719 if (auto E = Visitor.visitSymbolRecord(*Sym, PubSymOff))1720 return E;1721 }1722 }1723 1724 // Return early if we aren't dumping public hash table and address map info.1725 if (HashExtras) {1726 P.formatLine("Hash Entries");1727 {1728 AutoIndent Indent2(P);1729 for (const PSHashRecord &HR : Table.HashRecords)1730 P.formatLine("off = {0}, refcnt = {1}", uint32_t(HR.Off),1731 uint32_t(HR.CRef));1732 }1733 1734 P.formatLine("Hash Buckets");1735 {1736 AutoIndent Indent2(P);1737 for (uint32_t Hash : Table.HashBuckets)1738 P.formatLine("{0:x8}", Hash);1739 }1740 }1741 1742 return Error::success();1743}1744 1745static std::string formatSegMapDescriptorFlag(uint32_t IndentLevel,1746 OMFSegDescFlags Flags) {1747 std::vector<std::string> Opts;1748 if (Flags == OMFSegDescFlags::None)1749 return "none";1750 1751 PUSH_FLAG(OMFSegDescFlags, Read, Flags, "read");1752 PUSH_FLAG(OMFSegDescFlags, Write, Flags, "write");1753 PUSH_FLAG(OMFSegDescFlags, Execute, Flags, "execute");1754 PUSH_FLAG(OMFSegDescFlags, AddressIs32Bit, Flags, "32 bit addr");1755 PUSH_FLAG(OMFSegDescFlags, IsSelector, Flags, "selector");1756 PUSH_FLAG(OMFSegDescFlags, IsAbsoluteAddress, Flags, "absolute addr");1757 PUSH_FLAG(OMFSegDescFlags, IsGroup, Flags, "group");1758 return typesetItemList(Opts, IndentLevel, 4, " | ");1759}1760 1761Error DumpOutputStyle::dumpSectionHeaders() {1762 dumpSectionHeaders("Section Headers", DbgHeaderType::SectionHdr);1763 dumpSectionHeaders("Original Section Headers", DbgHeaderType::SectionHdrOrig);1764 return Error::success();1765}1766 1767void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) {1768 printHeader(P, Label);1769 1770 if (File.isObj()) {1771 printStreamNotValidForObj();1772 return;1773 }1774 1775 if (!getPdb().hasPDBDbiStream()) {1776 printStreamNotPresent("DBI");1777 return;1778 }1779 1780 AutoIndent Indent(P);1781 ExitOnError Err("Error dumping section headers: ");1782 std::unique_ptr<MappedBlockStream> Stream;1783 ArrayRef<object::coff_section> Headers;1784 auto ExpectedHeaders = loadSectionHeaders(getPdb(), Type);1785 if (!ExpectedHeaders) {1786 P.printLine(toString(ExpectedHeaders.takeError()));1787 return;1788 }1789 std::tie(Stream, Headers) = std::move(*ExpectedHeaders);1790 1791 uint32_t I = 1;1792 for (const auto &Header : Headers) {1793 P.NewLine();1794 P.formatLine("SECTION HEADER #{0}", I);1795 P.formatLine("{0,8} name", Header.Name);1796 P.formatLine("{0,8:X-} virtual size", uint32_t(Header.VirtualSize));1797 P.formatLine("{0,8:X-} virtual address", uint32_t(Header.VirtualAddress));1798 P.formatLine("{0,8:X-} size of raw data", uint32_t(Header.SizeOfRawData));1799 P.formatLine("{0,8:X-} file pointer to raw data",1800 uint32_t(Header.PointerToRawData));1801 P.formatLine("{0,8:X-} file pointer to relocation table",1802 uint32_t(Header.PointerToRelocations));1803 P.formatLine("{0,8:X-} file pointer to line numbers",1804 uint32_t(Header.PointerToLinenumbers));1805 P.formatLine("{0,8:X-} number of relocations",1806 uint32_t(Header.NumberOfRelocations));1807 P.formatLine("{0,8:X-} number of line numbers",1808 uint32_t(Header.NumberOfLinenumbers));1809 P.formatLine("{0,8:X-} flags", uint32_t(Header.Characteristics));1810 AutoIndent IndentMore(P, 9);1811 P.formatLine("{0}", formatSectionCharacteristics(1812 P.getIndentLevel(), Header.Characteristics, 1, ""));1813 ++I;1814 }1815}1816 1817Error DumpOutputStyle::dumpSectionContribs() {1818 printHeader(P, "Section Contributions");1819 1820 if (File.isObj()) {1821 printStreamNotValidForObj();1822 return Error::success();1823 }1824 1825 if (!getPdb().hasPDBDbiStream()) {1826 printStreamNotPresent("DBI");1827 return Error::success();1828 }1829 1830 AutoIndent Indent(P);1831 ExitOnError Err("Error dumping section contributions: ");1832 1833 DbiStream &Dbi = Err(getPdb().getPDBDbiStream());1834 1835 class Visitor : public ISectionContribVisitor {1836 public:1837 Visitor(LinePrinter &P, ArrayRef<std::string> Names) : P(P), Names(Names) {1838 auto Max = llvm::max_element(Names, [](StringRef S1, StringRef S2) {1839 return S1.size() < S2.size();1840 });1841 MaxNameLen = (Max == Names.end() ? 0 : Max->size());1842 }1843 void visit(const SectionContrib &SC) override {1844 dumpSectionContrib(P, SC, Names, MaxNameLen);1845 }1846 void visit(const SectionContrib2 &SC) override {1847 dumpSectionContrib(P, SC, Names, MaxNameLen);1848 }1849 1850 private:1851 LinePrinter &P;1852 uint32_t MaxNameLen;1853 ArrayRef<std::string> Names;1854 };1855 1856 auto NamesOrErr = getSectionNames(getPdb());1857 if (!NamesOrErr)1858 return NamesOrErr.takeError();1859 ArrayRef<std::string> Names = *NamesOrErr;1860 Visitor V(P, Names);1861 Dbi.visitSectionContributions(V);1862 return Error::success();1863}1864 1865Error DumpOutputStyle::dumpSectionMap() {1866 printHeader(P, "Section Map");1867 1868 if (File.isObj()) {1869 printStreamNotValidForObj();1870 return Error::success();1871 }1872 1873 if (!getPdb().hasPDBDbiStream()) {1874 printStreamNotPresent("DBI");1875 return Error::success();1876 }1877 1878 AutoIndent Indent(P);1879 ExitOnError Err("Error dumping section map: ");1880 1881 DbiStream &Dbi = Err(getPdb().getPDBDbiStream());1882 1883 uint32_t I = 0;1884 for (auto &M : Dbi.getSectionMap()) {1885 P.formatLine(1886 "Section {0:4} | ovl = {1}, group = {2}, frame = {3}, name = {4}", I,1887 fmtle(M.Ovl), fmtle(M.Group), fmtle(M.Frame), fmtle(M.SecName));1888 P.formatLine(" class = {0}, offset = {1}, size = {2}",1889 fmtle(M.ClassName), fmtle(M.Offset), fmtle(M.SecByteLength));1890 P.formatLine(" flags = {0}",1891 formatSegMapDescriptorFlag(1892 P.getIndentLevel() + 13,1893 static_cast<OMFSegDescFlags>(uint16_t(M.Flags))));1894 ++I;1895 }1896 return Error::success();1897}1898