Fix test failures introduced by PR #113697 (#116941)
[llvm-project.git] / llvm / tools / llvm-pdbutil / DumpOutputStyle.cpp
blobb2362ecb75703b304cb36ba9f6bd6f3d59dadef5
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-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include "DumpOutputStyle.h"
11 #include "MinimalSymbolDumper.h"
12 #include "MinimalTypeDumper.h"
13 #include "StreamUtil.h"
14 #include "TypeReferenceTracker.h"
15 #include "llvm-pdbutil.h"
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"
58 #include <cctype>
60 using namespace llvm;
61 using namespace llvm::codeview;
62 using namespace llvm::msf;
63 using namespace llvm::pdb;
65 DumpOutputStyle::DumpOutputStyle(InputFile &File)
66 : File(File), P(2, false, outs(), opts::Filters) {
67 if (opts::dump::DumpTypeRefStats)
68 RefTracker.reset(new TypeReferenceTracker(File));
71 DumpOutputStyle::~DumpOutputStyle() {}
73 PDBFile &DumpOutputStyle::getPdb() { return File.pdb(); }
74 object::COFFObjectFile &DumpOutputStyle::getObj() { return File.obj(); }
76 void DumpOutputStyle::printStreamNotValidForObj() {
77 AutoIndent Indent(P, 4);
78 P.formatLine("Dumping this stream is not valid for object files");
81 void DumpOutputStyle::printStreamNotPresent(StringRef StreamName) {
82 AutoIndent Indent(P, 4);
83 P.formatLine("{0} stream not present", StreamName);
86 Error DumpOutputStyle::dump() {
87 // Walk symbols & globals if we are supposed to mark types referenced.
88 if (opts::dump::DumpTypeRefStats)
89 RefTracker->mark();
91 if (opts::dump::DumpSummary) {
92 if (auto EC = dumpFileSummary())
93 return EC;
94 P.NewLine();
97 if (opts::dump::DumpStreams) {
98 if (auto EC = dumpStreamSummary())
99 return EC;
100 P.NewLine();
103 if (opts::dump::DumpSymbolStats) {
104 ExitOnError Err("Unexpected error processing module stats: ");
105 Err(dumpSymbolStats());
106 P.NewLine();
109 if (opts::dump::DumpUdtStats) {
110 if (auto EC = dumpUdtStats())
111 return EC;
112 P.NewLine();
115 if (opts::dump::DumpTypeStats || opts::dump::DumpIDStats) {
116 if (auto EC = dumpTypeStats())
117 return EC;
118 P.NewLine();
121 if (opts::dump::DumpNamedStreams) {
122 if (auto EC = dumpNamedStreams())
123 return EC;
124 P.NewLine();
127 if (opts::dump::DumpStringTable || opts::dump::DumpStringTableDetails) {
128 if (auto EC = dumpStringTable())
129 return EC;
130 P.NewLine();
133 if (opts::dump::DumpModules) {
134 ExitOnError Err("Unexpected error processing modules: ");
135 Err(dumpModules());
138 if (opts::dump::DumpModuleFiles) {
139 ExitOnError Err("Unexpected error processing files: ");
140 Err(dumpModuleFiles());
143 if (opts::dump::DumpLines) {
144 ExitOnError Err("Unexpected error processing lines: ");
145 Err(dumpLines());
148 if (opts::dump::DumpInlineeLines) {
149 ExitOnError Err("Unexpected error processing inlinee lines: ");
150 Err(dumpInlineeLines());
153 if (opts::dump::DumpXmi) {
154 ExitOnError Err("Unexpected error processing cross module imports: ");
155 Err(dumpXmi());
158 if (opts::dump::DumpXme) {
159 ExitOnError Err("Unexpected error processing cross module exports: ");
160 Err(dumpXme());
163 if (opts::dump::DumpFpo) {
164 if (auto EC = dumpFpo())
165 return EC;
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;
180 if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() ||
181 opts::dump::DumpIdExtras) {
182 if (auto EC = dumpTpiStream(StreamIPI))
183 return EC;
187 if (opts::dump::DumpGSIRecords) {
188 if (auto EC = dumpGSIRecords())
189 return EC;
192 if (opts::dump::DumpGlobals) {
193 if (auto EC = dumpGlobals())
194 return EC;
197 if (opts::dump::DumpPublics) {
198 if (auto EC = dumpPublics())
199 return EC;
202 if (opts::dump::DumpSymbols) {
203 ExitOnError Err("Unexpected error processing symbols: ");
204 Err(File.isPdb() ? dumpModuleSymsForPdb() : dumpModuleSymsForObj());
207 if (opts::dump::DumpTypeRefStats) {
208 if (auto EC = dumpTypeRefStats())
209 return EC;
212 if (opts::dump::DumpSectionHeaders) {
213 if (auto EC = dumpSectionHeaders())
214 return EC;
217 if (opts::dump::DumpSectionContribs) {
218 if (auto EC = dumpSectionContribs())
219 return EC;
222 if (opts::dump::DumpSectionMap) {
223 if (auto EC = dumpSectionMap())
224 return EC;
227 P.NewLine();
229 return Error::success();
232 static void printHeader(LinePrinter &P, const Twine &S) {
233 P.NewLine();
234 P.formatLine("{0,=60}", S);
235 P.formatLine("{0}", fmt_repeat('=', 60));
238 Error DumpOutputStyle::dumpFileSummary() {
239 printHeader(P, "Summary");
241 if (File.isObj()) {
242 printStreamNotValidForObj();
243 return Error::success();
246 AutoIndent Indent(P);
247 ExitOnError Err("Invalid PDB Format: ");
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());
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());
270 return Error::success();
273 static 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());
282 return Stats;
285 for (const auto &SS : SG.getDebugSubsections()) {
286 // For object files, all symbols are spread across multiple Symbol
287 // 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());
298 return Stats;
301 static 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());
308 return Stats;
311 static inline std::string formatModuleDetailKind(DebugSubsectionKind K) {
312 return formatChunkKind(K, false);
315 static inline std::string formatModuleDetailKind(SymbolKind K) {
316 return formatSymbolKind(K);
319 // Get the stats sorted by size, descending.
320 std::vector<StatCollection::KindAndStat>
321 StatCollection::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;
327 return SortedStats;
330 template <typename Kind>
331 static 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));
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);
347 Error DumpOutputStyle::dumpStreamSummary() {
348 printHeader(P, "Streams");
350 if (File.isObj()) {
351 printStreamNotValidForObj();
352 return Error::success();
355 AutoIndent Indent(P);
357 if (StreamPurposes.empty())
358 discoverStreamPurposes(getPdb(), StreamPurposes);
360 uint32_t StreamCount = getPdb().getNumStreams();
361 uint32_t MaxStreamSize = getPdb().getMaxStreamSize();
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());
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()));
380 return Error::success();
383 static Expected<std::pair<std::unique_ptr<MappedBlockStream>,
384 ArrayRef<llvm::object::coff_section>>>
385 loadSectionHeaders(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());
391 DbiStream &Dbi = cantFail(File.getPDBDbiStream());
392 uint32_t SI = Dbi.getDebugStreamIndex(Type);
394 if (SI == kInvalidStreamIndex)
395 return make_error<StringError>(
396 "PDB does not contain the requested image section header type",
397 inconvertibleErrorCode());
399 auto Stream = File.createIndexedStream(SI);
400 if (!Stream)
401 return make_error<StringError>("Could not load the required stream data",
402 inconvertibleErrorCode());
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());
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);
416 static Expected<std::vector<std::string>> getSectionNames(PDBFile &File) {
417 auto ExpectedHeaders = loadSectionHeaders(File, DbgHeaderType::SectionHdr);
418 if (!ExpectedHeaders)
419 return ExpectedHeaders.takeError();
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;
430 static 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 } else
438 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, " | "));
450 static 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, " | "));
463 Error DumpOutputStyle::dumpModules() {
464 printHeader(P, "Modules");
466 if (File.isObj()) {
467 printStreamNotValidForObj();
468 return Error::success();
471 if (!getPdb().hasPDBDbiStream()) {
472 printStreamNotPresent("DBI");
473 return Error::success();
476 AutoIndent Indent(P);
478 Expected<DbiStream &> StreamOrErr = getPdb().getPDBDbiStream();
479 if (!StreamOrErr)
480 return StreamOrErr.takeError();
481 DbiStream &Stream = *StreamOrErr;
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);
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());
500 auto PdbPathOrErr = Stream.getECName(Desc.getPdbFilePathNameIndex());
501 if (!PdbPathOrErr)
502 return PdbPathOrErr.takeError();
503 StringRef PdbFilePath = *PdbPathOrErr;
505 auto SrcPathOrErr = Stream.getECName(Desc.getSourceFileNameIndex());
506 if (!SrcPathOrErr)
507 return SrcPathOrErr.takeError();
508 StringRef SrcFilePath = *SrcPathOrErr;
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();
517 Error DumpOutputStyle::dumpModuleFiles() {
518 printHeader(P, "Files");
520 if (File.isObj()) {
521 printStreamNotValidForObj();
522 return Error::success();
525 if (!getPdb().hasPDBDbiStream()) {
526 printStreamNotPresent("DBI");
527 return Error::success();
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;
538 const DbiModuleList &Modules = Stream.modules();
539 for (const auto &F : Modules.source_files(Modi)) {
540 Strings.formatFromFileName(P, F);
542 return Error::success();
546 Error DumpOutputStyle::dumpSymbolStats() {
547 printHeader(P, "Module Stats");
549 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
550 printStreamNotPresent("DBI");
551 return Error::success();
554 StatCollection SymStats;
555 StatCollection ChunkStats;
556 PrintScope Scope(P, 2);
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);
563 if (!SG.getFile().isPdb())
564 return Error::success();
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();
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();
579 P.formatLine("Stream {0}, {1} bytes", StreamIdx,
580 getPdb().getStreamByteSize(StreamIdx));
582 printModuleDetailStats<SymbolKind>(P, "Symbols", SS);
583 printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", CS);
585 return Error::success();
587 return Err;
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);
596 return Error::success();
599 Error DumpOutputStyle::dumpTypeStats() {
600 printHeader(P, "Type Record Stats");
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());
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));
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);
625 return Error::success();
628 static bool isValidNamespaceIdentifier(StringRef S) {
629 if (S.empty())
630 return false;
632 if (std::isdigit(S[0]))
633 return false;
635 return llvm::all_of(S, [](char C) { return std::isalnum(C); });
638 namespace {
639 constexpr uint32_t kNoneUdtKind = 0;
640 constexpr uint32_t kSimpleUdtKind = 1;
641 constexpr uint32_t kUnknownUdtKind = 2;
642 } // namespace
644 static std::string getUdtStatLabel(uint32_t Kind) {
645 if (Kind == kNoneUdtKind)
646 return "<none type>";
648 if (Kind == kSimpleUdtKind)
649 return "<simple type>";
651 if (Kind == kUnknownUdtKind)
652 return "<unknown type>";
654 return formatTypeLeafKind(static_cast<TypeLeafKind>(Kind));
657 static 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());
663 return static_cast<uint32_t>(L);
666 Error DumpOutputStyle::dumpUdtStats() {
667 printHeader(P, "S_UDT Record Stats");
669 if (File.isPdb() && !getPdb().hasPDBGlobalsStream()) {
670 printStreamNotPresent("Globals");
671 return Error::success();
674 StatCollection UdtStats;
675 StatCollection UdtTargetStats;
676 AutoIndent Indent(P, 4);
678 auto &TpiTypes = File.types();
680 StringMap<StatCollection::Stat> NamespacedStats;
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());
688 UDTSym UDT = cantFail(SymbolDeserializer::deserializeAs<UDTSym>(Sym));
690 uint32_t Kind = 0;
691 uint32_t RecordSize = 0;
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 } else
701 Kind = kUnknownUdtKind;
703 UdtTargetStats.update(Kind, RecordSize);
705 size_t Pos = UDT.Name.find("::");
706 if (Pos == StringRef::npos)
707 return;
709 StringRef Scope = UDT.Name.take_front(Pos);
710 if (Scope.empty() || !isValidNamespaceIdentifier(Scope))
711 return;
713 LongestNamespace = std::max(LongestNamespace, Scope.size());
714 NamespacedStats[Scope].update(RecordSize);
717 P.NewLine();
719 if (File.isPdb()) {
720 auto &SymbolRecords = cantFail(getPdb().getPDBSymbolStream());
721 auto ExpGlobals = getPdb().getPDBGlobalsStream();
722 if (!ExpGlobals)
723 return ExpGlobals.takeError();
725 for (uint32_t PubSymOff : ExpGlobals->getGlobalsTable()) {
726 CVSymbol Sym = SymbolRecords.readRecord(PubSymOff);
727 HandleOneSymbol(Sym);
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;
735 DebugSymbolsSubsectionRef Symbols;
736 BinaryStreamReader Reader(SS.getRecordData());
737 cantFail(Symbols.initialize(Reader));
738 for (const auto &S : Symbols)
739 HandleOneSymbol(S);
744 LongestNamespace += StringRef(" namespace ''").size();
745 size_t LongestTypeLeafKind = getLongestTypeLeafName(UdtTargetStats);
746 size_t FieldWidth = std::max(LongestNamespace, LongestTypeLeafKind);
748 // Compute the max number of digits for count and size fields, including comma
749 // 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());
756 size_t SD = NumDigits(UdtStats.Totals.Size);
757 SD += (SD - 1) / 3;
758 SD = std::max(SD, SizeHeader.size());
760 uint32_t TableWidth = FieldWidth + 3 + CD + 2 + SD + 1;
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));
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));
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;
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;
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));
801 return Error::success();
804 static void typesetLinesAndColumns(LinePrinter &P, uint32_t Start,
805 const LineColumnEntry &E) {
806 const uint32_t kMaxCharsPerLineNumber = 4; // 4 digit line number
807 uint32_t MinColumnWidth = kMaxCharsPerLineNumber + 5;
809 // Let's try to keep it under 100 characters
810 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 else
825 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;
834 P.NewLine();
838 Error DumpOutputStyle::dumpLines() {
839 printHeader(P, "Lines");
841 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
842 printStreamNotPresent("DBI");
843 return Error::success();
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);
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 else
869 P.format("line/addr entries = {0}", Count);
871 P.NewLine();
872 typesetLinesAndColumns(P, Begin, Block);
874 return Error::success();
878 Error DumpOutputStyle::dumpInlineeLines() {
879 printHeader(P, "Inlinee Lines");
881 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
882 printStreamNotPresent("DBI");
883 return Error::success();
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);
900 P.NewLine();
901 return Error::success();
905 Error DumpOutputStyle::dumpXmi() {
906 printHeader(P, "Cross Module Imports");
908 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
909 printStreamNotPresent("DBI");
910 return Error::success();
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");
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 } else
928 Module = *ExpectedModule;
929 if (Module.size() > 32) {
930 ModuleStorage = "...";
931 ModuleStorage += Module.take_back(32 - 3);
932 Module = ModuleStorage;
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);
941 return Error::success();
945 Error DumpOutputStyle::dumpXme() {
946 printHeader(P, "Cross Module Exports");
948 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
949 printStreamNotPresent("DBI");
950 return Error::success();
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));
962 return Error::success();
966 std::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";
977 return "<unknown>";
980 Error DumpOutputStyle::dumpOldFpo(PDBFile &File) {
981 printHeader(P, "Old FPO Data");
983 ExitOnError Err("Error dumping old fpo data:");
984 DbiStream &Dbi = Err(File.getPDBDbiStream());
986 if (!Dbi.hasOldFpoRecords()) {
987 printStreamNotPresent("FPO");
988 return Error::success();
991 const FixedStreamArray<object::FpoData>& Records = Dbi.getOldFpoRecords();
993 P.printLine(" RVA | Code | Locals | Params | Prolog | Saved Regs | Use "
994 "BP | Has SEH | Frame Type");
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()));
1004 return Error::success();
1007 Error DumpOutputStyle::dumpNewFpo(PDBFile &File) {
1008 printHeader(P, "New FPO Data");
1010 ExitOnError Err("Error dumping new fpo data:");
1011 DbiStream &Dbi = Err(File.getPDBDbiStream());
1013 if (!Dbi.hasNewFpoRecords()) {
1014 printStreamNotPresent("New FPO");
1015 return Error::success();
1018 const DebugFrameDataSubsectionRef& FDS = Dbi.getNewFpoRecords();
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;
1027 auto &StringTable = Err(File.getStringTable());
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);
1038 return Error::success();
1041 Error DumpOutputStyle::dumpFpo() {
1042 if (!File.isPdb()) {
1043 printStreamNotValidForObj();
1044 return Error::success();
1047 PDBFile &File = getPdb();
1048 if (!File.hasPDBDbiStream()) {
1049 printStreamNotPresent("DBI");
1050 return Error::success();
1053 if (auto EC = dumpOldFpo(File))
1054 return EC;
1055 if (auto EC = dumpNewFpo(File))
1056 return EC;
1057 return Error::success();
1060 Error 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();
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);
1076 P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits),
1077 "String");
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("'");
1094 if (!Str.empty())
1095 P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits),
1096 Str);
1101 if (opts::dump::DumpStringTableDetails) {
1102 P.NewLine();
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();
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();
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());
1127 return Error::success();
1130 Error 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;
1144 P.formatLine("{0} | {1}", fmt_align(Offset, AlignStyle::Right, 4),
1145 Str);
1147 return Error::success();
1151 Error DumpOutputStyle::dumpNamedStreams() {
1152 printHeader(P, "Named Streams");
1154 if (File.isObj()) {
1155 printStreamNotValidForObj();
1156 return Error::success();
1159 AutoIndent Indent(P);
1160 ExitOnError Err("Invalid PDB File: ");
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()));
1172 return Error::success();
1175 Error DumpOutputStyle::dumpStringTable() {
1176 printHeader(P, "String Table");
1178 if (File.isPdb())
1179 return dumpStringTableFromPdb();
1181 return dumpStringTableFromObj();
1184 static 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;
1193 CVType Type = Types.getType(TI);
1194 DepSet[TI] = Type;
1195 codeview::discoverTypeIndices(Type, DepList);
1196 buildDepSet(Types, DepList, DepSet);
1200 static void
1201 dumpFullTypeStream(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) {
1207 Printer.formatLine("Showing {0:N} records", NumTypeRecords);
1208 uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + NumTypeRecords);
1210 MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker,
1211 NumHashBuckets, HashValues, Stream);
1213 if (auto EC = codeview::visitTypeStream(Types, V)) {
1214 Printer.formatLine("An error occurred dumping type records: {0}",
1215 toString(std::move(EC)));
1219 static 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());
1227 MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker,
1228 Stream.getNumHashBuckets(), Stream.getHashValues(),
1229 &Stream);
1231 if (opts::dump::DumpTypeDependents) {
1232 // If we need to dump all dependents, then iterate each index and find
1233 // all dependents, adding them to a map ordered by TypeIndex.
1234 std::map<TypeIndex, CVType> DepSet;
1235 buildDepSet(Types, TiList, DepSet);
1237 Printer.formatLine(
1238 "Showing {0:N} records and their dependents ({1:N} records total)",
1239 TiList.size(), DepSet.size());
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)));
1246 } else {
1247 Printer.formatLine("Showing {0:N} records.", TiList.size());
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);
1265 Error DumpOutputStyle::dumpTypesFromObjectFile() {
1266 LazyRandomTypeCollection Types(100);
1268 for (const auto &S : getObj().sections()) {
1269 Expected<StringRef> NameOrErr = S.getName();
1270 if (!NameOrErr)
1271 return NameOrErr.takeError();
1272 StringRef SectionName = *NameOrErr;
1274 // .debug$T is a standard CodeView type section, while .debug$P is the same
1275 // 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 else
1281 continue;
1283 Expected<StringRef> ContentsOrErr = S.getContents();
1284 if (!ContentsOrErr)
1285 return ContentsOrErr.takeError();
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());
1295 Types.reset(Reader, 100);
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());
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);
1312 P.formatLine("TI: {0}, LocalHash: {1:X}, GlobalHash: {2}", TI, L, G);
1314 ++TI;
1316 P.NewLine();
1320 return Error::success();
1323 Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
1324 assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);
1326 if (StreamIdx == StreamTPI) {
1327 printHeader(P, "Types (TPI Stream)");
1328 } else if (StreamIdx == StreamIPI) {
1329 printHeader(P, "Types (IPI Stream)");
1332 assert(!File.isObj());
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());
1355 if (!Present) {
1356 printStreamNotPresent(StreamIdx == StreamTPI ? "TPI" : "IPI");
1357 return Error::success();
1360 AutoIndent Indent(P);
1361 ExitOnError Err("Unexpected error processing types: ");
1363 auto &Stream = Err((StreamIdx == StreamTPI) ? getPdb().getPDBTpiStream()
1364 : getPdb().getPDBIpiStream());
1366 auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids();
1368 // Only emit notes about referenced/unreferenced for types.
1369 TypeReferenceTracker *MaybeTracker =
1370 (StreamIdx == StreamTPI) ? RefTracker.get() : nullptr;
1372 // Enable resolving forward decls.
1373 Stream.buildHashMap();
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);
1387 if (DumpExtras) {
1388 P.NewLine();
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());
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));
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());
1423 return Error::success();
1426 Error DumpOutputStyle::dumpModuleSymsForObj() {
1427 printHeader(P, "Symbols");
1429 AutoIndent Indent(P);
1431 auto &Types = File.types();
1433 SymbolVisitorCallbackPipeline Pipeline;
1434 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::ObjectFile);
1435 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types, Types);
1437 Pipeline.addCallbackToPipeline(Deserializer);
1438 Pipeline.addCallbackToPipeline(Dumper);
1439 CVSymbolVisitor Visitor(Pipeline);
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;
1451 return Error::success();
1455 Error DumpOutputStyle::dumpModuleSymsForPdb() {
1456 printHeader(P, "Symbols");
1458 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
1459 printStreamNotPresent("DBI");
1460 return Error::success();
1463 AutoIndent Indent(P);
1465 auto &Ids = File.ids();
1466 auto &Types = File.types();
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();
1478 ModuleDebugStreamRef &ModS = *ExpectedModS;
1480 SymbolVisitorCallbackPipeline Pipeline;
1481 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
1482 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Strings,
1483 Ids, Types);
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;
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;
1506 return Error::success();
1510 Error DumpOutputStyle::dumpTypeRefStats() {
1511 printHeader(P, "Type Reference Statistics");
1512 AutoIndent Indent(P);
1514 // Sum the byte size of all type records, and the size and count of all
1515 // 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();
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);
1536 return Error::success();
1539 Error DumpOutputStyle::dumpGSIRecords() {
1540 printHeader(P, "GSI Records");
1542 if (File.isObj()) {
1543 printStreamNotValidForObj();
1544 return Error::success();
1547 if (!getPdb().hasPDBSymbolStream()) {
1548 printStreamNotPresent("GSI Common Symbol");
1549 return Error::success();
1552 AutoIndent Indent(P);
1554 auto &Records = cantFail(getPdb().getPDBSymbolStream());
1555 auto &Types = File.types();
1556 auto &Ids = File.ids();
1558 P.printLine("Records");
1559 SymbolVisitorCallbackPipeline Pipeline;
1560 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
1561 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);
1563 Pipeline.addCallbackToPipeline(Deserializer);
1564 Pipeline.addCallbackToPipeline(Dumper);
1565 CVSymbolVisitor Visitor(Pipeline);
1567 BinaryStreamRef SymStream = Records.getSymbolArray().getUnderlyingStream();
1568 if (auto E = Visitor.visitSymbolStream(Records.getSymbolArray(), 0))
1569 return E;
1570 return Error::success();
1573 Error DumpOutputStyle::dumpGlobals() {
1574 printHeader(P, "Global Symbols");
1576 if (File.isObj()) {
1577 printStreamNotValidForObj();
1578 return Error::success();
1581 if (!getPdb().hasPDBGlobalsStream()) {
1582 printStreamNotPresent("Globals");
1583 return Error::success();
1586 AutoIndent Indent(P);
1587 ExitOnError Err("Error dumping globals stream: ");
1588 auto &Globals = Err(getPdb().getPDBGlobalsStream());
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();
1598 SymbolVisitorCallbackPipeline Pipeline;
1599 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
1600 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);
1602 Pipeline.addCallbackToPipeline(Deserializer);
1603 Pipeline.addCallbackToPipeline(Dumper);
1604 CVSymbolVisitor Visitor(Pipeline);
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;
1618 for (ResultEntryType Result : Results) {
1619 if (auto E = Visitor.visitSymbolRecord(Result.second, Result.first))
1620 return E;
1624 return Error::success();
1627 Error DumpOutputStyle::dumpPublics() {
1628 printHeader(P, "Public Symbols");
1630 if (File.isObj()) {
1631 printStreamNotValidForObj();
1632 return Error::success();
1635 if (!getPdb().hasPDBPublicsStream()) {
1636 printStreamNotPresent("Publics");
1637 return Error::success();
1640 AutoIndent Indent(P);
1641 ExitOnError Err("Error dumping publics stream: ");
1642 auto &Publics = Err(getPdb().getPDBPublicsStream());
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()));
1652 Err(dumpSymbolsFromGSI(PublicsTable, opts::dump::DumpPublicExtras));
1654 // Skip the rest if we aren't dumping extras.
1655 if (!opts::dump::DumpPublicExtras)
1656 return Error::success();
1658 P.formatLine("Address Map");
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);
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);
1674 // The section offsets table appears to be empty when incremental linking
1675 // 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));
1683 return Error::success();
1686 Error 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();
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());
1703 P.printLine("Records");
1704 SymbolVisitorCallbackPipeline Pipeline;
1705 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
1706 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);
1708 Pipeline.addCallbackToPipeline(Deserializer);
1709 Pipeline.addCallbackToPipeline(Dumper);
1710 CVSymbolVisitor Visitor(Pipeline);
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;
1724 // Return early if we aren't dumping public hash table and address map info.
1725 if (HashExtras) {
1726 P.formatLine("Hash Entries");
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));
1734 P.formatLine("Hash Buckets");
1736 AutoIndent Indent2(P);
1737 for (uint32_t Hash : Table.HashBuckets)
1738 P.formatLine("{0:x8}", Hash);
1742 return Error::success();
1745 static std::string formatSegMapDescriptorFlag(uint32_t IndentLevel,
1746 OMFSegDescFlags Flags) {
1747 std::vector<std::string> Opts;
1748 if (Flags == OMFSegDescFlags::None)
1749 return "none";
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, " | ");
1761 Error DumpOutputStyle::dumpSectionHeaders() {
1762 dumpSectionHeaders("Section Headers", DbgHeaderType::SectionHdr);
1763 dumpSectionHeaders("Original Section Headers", DbgHeaderType::SectionHdrOrig);
1764 return Error::success();
1767 void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) {
1768 printHeader(P, Label);
1770 if (File.isObj()) {
1771 printStreamNotValidForObj();
1772 return;
1775 if (!getPdb().hasPDBDbiStream()) {
1776 printStreamNotPresent("DBI");
1777 return;
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;
1789 std::tie(Stream, Headers) = std::move(*ExpectedHeaders);
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;
1817 Error DumpOutputStyle::dumpSectionContribs() {
1818 printHeader(P, "Section Contributions");
1820 if (File.isObj()) {
1821 printStreamNotValidForObj();
1822 return Error::success();
1825 if (!getPdb().hasPDBDbiStream()) {
1826 printStreamNotPresent("DBI");
1827 return Error::success();
1830 AutoIndent Indent(P);
1831 ExitOnError Err("Error dumping section contributions: ");
1833 DbiStream &Dbi = Err(getPdb().getPDBDbiStream());
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();
1841 MaxNameLen = (Max == Names.end() ? 0 : Max->size());
1843 void visit(const SectionContrib &SC) override {
1844 dumpSectionContrib(P, SC, Names, MaxNameLen);
1846 void visit(const SectionContrib2 &SC) override {
1847 dumpSectionContrib(P, SC, Names, MaxNameLen);
1850 private:
1851 LinePrinter &P;
1852 uint32_t MaxNameLen;
1853 ArrayRef<std::string> Names;
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();
1865 Error DumpOutputStyle::dumpSectionMap() {
1866 printHeader(P, "Section Map");
1868 if (File.isObj()) {
1869 printStreamNotValidForObj();
1870 return Error::success();
1873 if (!getPdb().hasPDBDbiStream()) {
1874 printStreamNotPresent("DBI");
1875 return Error::success();
1878 AutoIndent Indent(P);
1879 ExitOnError Err("Error dumping section map: ");
1881 DbiStream &Dbi = Err(getPdb().getPDBDbiStream());
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;
1896 return Error::success();