1 //===- DumpOutputStyle.cpp ------------------------------------ *- C++ --*-===//
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
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"
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
)
91 if (opts::dump::DumpSummary
) {
92 if (auto EC
= dumpFileSummary())
97 if (opts::dump::DumpStreams
) {
98 if (auto EC
= dumpStreamSummary())
103 if (opts::dump::DumpSymbolStats
) {
104 ExitOnError
Err("Unexpected error processing module stats: ");
105 Err(dumpSymbolStats());
109 if (opts::dump::DumpUdtStats
) {
110 if (auto EC
= dumpUdtStats())
115 if (opts::dump::DumpTypeStats
|| opts::dump::DumpIDStats
) {
116 if (auto EC
= dumpTypeStats())
121 if (opts::dump::DumpNamedStreams
) {
122 if (auto EC
= dumpNamedStreams())
127 if (opts::dump::DumpStringTable
|| opts::dump::DumpStringTableDetails
) {
128 if (auto EC
= dumpStringTable())
133 if (opts::dump::DumpModules
) {
134 ExitOnError
Err("Unexpected error processing modules: ");
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: ");
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: ");
158 if (opts::dump::DumpXme
) {
159 ExitOnError
Err("Unexpected error processing cross module exports: ");
163 if (opts::dump::DumpFpo
) {
164 if (auto EC
= dumpFpo())
169 if (opts::dump::DumpTypes
|| !opts::dump::DumpTypeIndex
.empty() ||
170 opts::dump::DumpTypeExtras
)
171 if (auto EC
= dumpTypesFromObjectFile())
174 if (opts::dump::DumpTypes
|| !opts::dump::DumpTypeIndex
.empty() ||
175 opts::dump::DumpTypeExtras
) {
176 if (auto EC
= dumpTpiStream(StreamTPI
))
180 if (opts::dump::DumpIds
|| !opts::dump::DumpIdIndex
.empty() ||
181 opts::dump::DumpIdExtras
) {
182 if (auto EC
= dumpTpiStream(StreamIPI
))
187 if (opts::dump::DumpGSIRecords
) {
188 if (auto EC
= dumpGSIRecords())
192 if (opts::dump::DumpGlobals
) {
193 if (auto EC
= dumpGlobals())
197 if (opts::dump::DumpPublics
) {
198 if (auto EC
= dumpPublics())
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())
212 if (opts::dump::DumpSectionHeaders
) {
213 if (auto EC
= dumpSectionHeaders())
217 if (opts::dump::DumpSectionContribs
) {
218 if (auto EC
= dumpSectionContribs())
222 if (opts::dump::DumpSectionMap
) {
223 if (auto EC
= dumpSectionMap())
229 return Error::success();
232 static void printHeader(LinePrinter
&P
, const Twine
&S
) {
234 P
.formatLine("{0,=60}", S
);
235 P
.formatLine("{0}", fmt_repeat('=', 60));
238 Error
DumpOutputStyle::dumpFileSummary() {
239 printHeader(P
, "Summary");
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());
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
)
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());
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());
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
;
330 template <typename Kind
>
331 static void printModuleDetailStats(LinePrinter
&P
, StringRef Label
,
332 const StatCollection
&Stats
) {
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");
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
) {
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
);
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
);
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();
438 NameInsert
= "[???]";
439 P
.formatLine("SC{5} | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "
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);
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
));
459 formatSectionCharacteristics(P
.getIndentLevel() + 6,
460 SC
.Base
.Characteristics
, 3, " | "));
463 Error
DumpOutputStyle::dumpModules() {
464 printHeader(P
, "Modules");
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();
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());
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(),
500 auto PdbPathOrErr
= Stream
.getECName(Desc
.getPdbFilePathNameIndex());
502 return PdbPathOrErr
.takeError();
503 StringRef PdbFilePath
= *PdbPathOrErr
;
505 auto SrcPathOrErr
= Stream
.getECName(Desc
.getSourceFileNameIndex());
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");
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();
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
) {
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();
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());
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
) {
632 if (std::isdigit(S
[0]))
635 return llvm::all_of(S
, [](char C
) { return std::isalnum(C
); });
639 constexpr uint32_t kNoneUdtKind
= 0;
640 constexpr uint32_t kSimpleUdtKind
= 1;
641 constexpr uint32_t kUnknownUdtKind
= 2;
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
) {
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
)
686 UdtStats
.update(SymbolKind::S_UDT
, Sym
.length());
688 UDTSym UDT
= cantFail(SymbolDeserializer::deserializeAs
<UDTSym
>(Sym
));
691 uint32_t RecordSize
= 0;
693 if (UDT
.Type
.isNoneType())
695 else if (UDT
.Type
.isSimple())
696 Kind
= kSimpleUdtKind
;
697 else if (std::optional
<CVType
> T
= TpiTypes
.tryGetType(UDT
.Type
)) {
699 RecordSize
= T
->length();
701 Kind
= kUnknownUdtKind
;
703 UdtTargetStats
.update(Kind
, RecordSize
);
705 size_t Pos
= UDT
.Name
.find("::");
706 if (Pos
== StringRef::npos
)
709 StringRef Scope
= UDT
.Name
.take_front(Pos
);
710 if (Scope
.empty() || !isValidNamespaceIdentifier(Scope
))
713 LongestNamespace
= std::max(LongestNamespace
, Scope
.size());
714 NamespacedStats
[Scope
].update(RecordSize
);
720 auto &SymbolRecords
= cantFail(getPdb().getPDBSymbolStream());
721 auto ExpGlobals
= getPdb().getPDBGlobalsStream();
723 return ExpGlobals
.takeError();
725 for (uint32_t PubSymOff
: ExpGlobals
->getGlobalsTable()) {
726 CVSymbol Sym
= SymbolRecords
.readRecord(PubSymOff
);
727 HandleOneSymbol(Sym
);
730 for (const auto &Sec
: File
.symbol_groups()) {
731 for (const auto &SS
: Sec
.getDebugSubsections()) {
732 if (SS
.kind() != DebugSubsectionKind::Symbols
)
735 DebugSymbolsSubsectionRef Symbols
;
736 BinaryStreamReader
Reader(SS
.getRecordData());
737 cantFail(Symbols
.initialize(Reader
));
738 for (const auto &S
: Symbols
)
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
750 StringRef
CountHeader("Count");
751 StringRef
SizeHeader("Size");
752 size_t CD
= NumDigits(UdtStats
.Totals
.Count
);
754 CD
= std::max(CD
, CountHeader
.size());
756 size_t SD
= NumDigits(UdtStats
.Totals
.Size
);
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
));
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
);
820 if (Line
.isAlwaysStepInto())
822 else if (Line
.isNeverStepInto())
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'),
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},
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
) {
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
);
869 P
.format("line/addr entries = {0}", Count
);
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
) {
897 Strings
.formatFromChecksumsOffset(P
, ExtraFileID
, true);
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
);
923 SmallString
<32> ModuleStorage
;
924 if (!ExpectedModule
) {
925 Module
= "(unknown module)";
926 consumeError(ExpectedModule
.takeError());
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
))));
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
) {
968 case object::frame_type::Fpo
:
970 case object::frame_type::NonFpo
:
972 case object::frame_type::Trap
:
974 case object::frame_type::Tss
:
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} | "
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
,
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
))
1055 if (auto EC
= dumpNewFpo(File
))
1057 return Error::success();
1060 Error
DumpOutputStyle::dumpStringTableFromPdb() {
1061 AutoIndent
Indent(P
);
1062 auto IS
= getPdb().getStringTable();
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");
1074 std::max_element(IS
->name_ids().begin(), IS
->name_ids().end());
1075 uint32_t Digits
= NumDigits(*MaxID
);
1077 P
.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right
, Digits
),
1080 std::vector
<uint32_t> SortedIDs(IS
->name_ids().begin(),
1081 IS
->name_ids().end());
1082 llvm::sort(SortedIDs
);
1083 for (uint32_t I
: SortedIDs
) {
1084 auto ES
= IS
->getStringForID(I
);
1085 llvm::SmallString
<32> Str
;
1087 consumeError(ES
.takeError());
1088 Str
= "Error reading string";
1089 } else if (!ES
->empty()) {
1096 P
.formatLine("{0} | {1}", fmt_align(I
, AlignStyle::Right
, Digits
),
1102 if (opts::dump::DumpStringTableDetails
) {
1105 P
.printLine("String Table Header:");
1106 AutoIndent
Indent(P
);
1107 P
.formatLine("Signature: {0}", IS
->getSignature());
1108 P
.formatLine("Hash Version: {0}", IS
->getHashVersion());
1109 P
.formatLine("Name Buffer Size: {0}", IS
->getByteSize());
1113 BinaryStreamRef NameBuffer
= IS
->getStringTable().getBuffer();
1114 ArrayRef
<uint8_t> Contents
;
1115 cantFail(NameBuffer
.readBytes(0, NameBuffer
.getLength(), Contents
));
1116 P
.formatBinary("Name Buffer", Contents
, 0);
1119 P
.printLine("Hash Table:");
1120 AutoIndent
Indent(P
);
1121 P
.formatLine("Bucket Count: {0}", IS
->name_ids().size());
1122 for (const auto &Entry
: enumerate(IS
->name_ids()))
1123 P
.formatLine("Bucket[{0}] : {1}", Entry
.index(),
1124 uint32_t(Entry
.value()));
1125 P
.formatLine("Name Count: {0}", IS
->getNameCount());
1128 return Error::success();
1131 Error
DumpOutputStyle::dumpStringTableFromObj() {
1132 return iterateModuleSubsections
<DebugStringTableSubsectionRef
>(
1133 File
, PrintScope
{P
, 4},
1134 [&](uint32_t Modi
, const SymbolGroup
&Strings
,
1135 DebugStringTableSubsectionRef
&Strings2
) -> Error
{
1136 BinaryStreamRef StringTableBuffer
= Strings2
.getBuffer();
1137 BinaryStreamReader
Reader(StringTableBuffer
);
1138 while (Reader
.bytesRemaining() > 0) {
1140 uint32_t Offset
= Reader
.getOffset();
1141 cantFail(Reader
.readCString(Str
));
1145 P
.formatLine("{0} | {1}", fmt_align(Offset
, AlignStyle::Right
, 4),
1148 return Error::success();
1152 Error
DumpOutputStyle::dumpNamedStreams() {
1153 printHeader(P
, "Named Streams");
1156 printStreamNotValidForObj();
1157 return Error::success();
1160 AutoIndent
Indent(P
);
1161 ExitOnError
Err("Invalid PDB File: ");
1163 auto &IS
= Err(File
.pdb().getPDBInfoStream());
1164 const NamedStreamMap
&NS
= IS
.getNamedStreams();
1165 for (const auto &Entry
: NS
.entries()) {
1166 P
.printLine(Entry
.getKey());
1167 AutoIndent
Indent2(P
, 2);
1168 P
.formatLine("Index: {0}", Entry
.getValue());
1169 P
.formatLine("Size in bytes: {0}",
1170 File
.pdb().getStreamByteSize(Entry
.getValue()));
1173 return Error::success();
1176 Error
DumpOutputStyle::dumpStringTable() {
1177 printHeader(P
, "String Table");
1180 return dumpStringTableFromPdb();
1182 return dumpStringTableFromObj();
1185 static void buildDepSet(LazyRandomTypeCollection
&Types
,
1186 ArrayRef
<TypeIndex
> Indices
,
1187 std::map
<TypeIndex
, CVType
> &DepSet
) {
1188 SmallVector
<TypeIndex
, 4> DepList
;
1189 for (const auto &I
: Indices
) {
1191 if (DepSet
.find(TI
) != DepSet
.end() || TI
.isSimple() || TI
.isNoneType())
1194 CVType Type
= Types
.getType(TI
);
1196 codeview::discoverTypeIndices(Type
, DepList
);
1197 buildDepSet(Types
, DepList
, DepSet
);
1202 dumpFullTypeStream(LinePrinter
&Printer
, LazyRandomTypeCollection
&Types
,
1203 TypeReferenceTracker
*RefTracker
, uint32_t NumTypeRecords
,
1204 uint32_t NumHashBuckets
,
1205 FixedStreamArray
<support::ulittle32_t
> HashValues
,
1206 TpiStream
*Stream
, bool Bytes
, bool Extras
) {
1208 Printer
.formatLine("Showing {0:N} records", NumTypeRecords
);
1209 uint32_t Width
= NumDigits(TypeIndex::FirstNonSimpleIndex
+ NumTypeRecords
);
1211 MinimalTypeDumpVisitor
V(Printer
, Width
+ 2, Bytes
, Extras
, Types
, RefTracker
,
1212 NumHashBuckets
, HashValues
, Stream
);
1214 if (auto EC
= codeview::visitTypeStream(Types
, V
)) {
1215 Printer
.formatLine("An error occurred dumping type records: {0}",
1216 toString(std::move(EC
)));
1220 static void dumpPartialTypeStream(LinePrinter
&Printer
,
1221 LazyRandomTypeCollection
&Types
,
1222 TypeReferenceTracker
*RefTracker
,
1223 TpiStream
&Stream
, ArrayRef
<TypeIndex
> TiList
,
1224 bool Bytes
, bool Extras
, bool Deps
) {
1226 NumDigits(TypeIndex::FirstNonSimpleIndex
+ Stream
.getNumTypeRecords());
1228 MinimalTypeDumpVisitor
V(Printer
, Width
+ 2, Bytes
, Extras
, Types
, RefTracker
,
1229 Stream
.getNumHashBuckets(), Stream
.getHashValues(),
1232 if (opts::dump::DumpTypeDependents
) {
1233 // If we need to dump all dependents, then iterate each index and find
1234 // all dependents, adding them to a map ordered by TypeIndex.
1235 std::map
<TypeIndex
, CVType
> DepSet
;
1236 buildDepSet(Types
, TiList
, DepSet
);
1239 "Showing {0:N} records and their dependents ({1:N} records total)",
1240 TiList
.size(), DepSet
.size());
1242 for (auto &Dep
: DepSet
) {
1243 if (auto EC
= codeview::visitTypeRecord(Dep
.second
, Dep
.first
, V
))
1244 Printer
.formatLine("An error occurred dumping type record {0}: {1}",
1245 Dep
.first
, toString(std::move(EC
)));
1248 Printer
.formatLine("Showing {0:N} records.", TiList
.size());
1250 for (const auto &I
: TiList
) {
1252 if (TI
.isSimple()) {
1253 Printer
.formatLine("{0} | {1}", fmt_align(I
, AlignStyle::Right
, Width
),
1254 Types
.getTypeName(TI
));
1255 } else if (std::optional
<CVType
> Type
= Types
.tryGetType(TI
)) {
1256 if (auto EC
= codeview::visitTypeRecord(*Type
, TI
, V
))
1257 Printer
.formatLine("An error occurred dumping type record {0}: {1}",
1258 TI
, toString(std::move(EC
)));
1260 Printer
.formatLine("Type {0} doesn't exist in TPI stream", TI
);
1266 Error
DumpOutputStyle::dumpTypesFromObjectFile() {
1267 LazyRandomTypeCollection
Types(100);
1269 for (const auto &S
: getObj().sections()) {
1270 Expected
<StringRef
> NameOrErr
= S
.getName();
1272 return NameOrErr
.takeError();
1273 StringRef SectionName
= *NameOrErr
;
1275 // .debug$T is a standard CodeView type section, while .debug$P is the same
1276 // format but used for MSVC precompiled header object files.
1277 if (SectionName
== ".debug$T")
1278 printHeader(P
, "Types (.debug$T)");
1279 else if (SectionName
== ".debug$P")
1280 printHeader(P
, "Precompiled Types (.debug$P)");
1284 Expected
<StringRef
> ContentsOrErr
= S
.getContents();
1286 return ContentsOrErr
.takeError();
1289 BinaryStreamReader
Reader(*ContentsOrErr
, llvm::endianness::little
);
1290 if (auto EC
= Reader
.readInteger(Magic
))
1292 if (Magic
!= COFF::DEBUG_SECTION_MAGIC
)
1293 return make_error
<StringError
>("Invalid CodeView debug section.",
1294 inconvertibleErrorCode());
1296 Types
.reset(Reader
, 100);
1298 if (opts::dump::DumpTypes
) {
1299 dumpFullTypeStream(P
, Types
, RefTracker
.get(), 0, 0, {}, nullptr,
1300 opts::dump::DumpTypeData
, false);
1301 } else if (opts::dump::DumpTypeExtras
) {
1302 auto LocalHashes
= LocallyHashedType::hashTypeCollection(Types
);
1303 auto GlobalHashes
= GloballyHashedType::hashTypeCollection(Types
);
1304 assert(LocalHashes
.size() == GlobalHashes
.size());
1306 P
.formatLine("Local / Global hashes:");
1307 TypeIndex
TI(TypeIndex::FirstNonSimpleIndex
);
1308 for (auto H
: zip(LocalHashes
, GlobalHashes
)) {
1309 AutoIndent
Indent2(P
);
1310 LocallyHashedType
&L
= std::get
<0>(H
);
1311 GloballyHashedType
&G
= std::get
<1>(H
);
1313 P
.formatLine("TI: {0}, LocalHash: {1:X}, GlobalHash: {2}", TI
, L
, G
);
1321 return Error::success();
1324 Error
DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx
) {
1325 assert(StreamIdx
== StreamTPI
|| StreamIdx
== StreamIPI
);
1327 if (StreamIdx
== StreamTPI
) {
1328 printHeader(P
, "Types (TPI Stream)");
1329 } else if (StreamIdx
== StreamIPI
) {
1330 printHeader(P
, "Types (IPI Stream)");
1333 assert(!File
.isObj());
1335 bool Present
= false;
1336 bool DumpTypes
= false;
1337 bool DumpBytes
= false;
1338 bool DumpExtras
= false;
1339 std::vector
<uint32_t> Indices
;
1340 if (StreamIdx
== StreamTPI
) {
1341 Present
= getPdb().hasPDBTpiStream();
1342 DumpTypes
= opts::dump::DumpTypes
;
1343 DumpBytes
= opts::dump::DumpTypeData
;
1344 DumpExtras
= opts::dump::DumpTypeExtras
;
1345 Indices
.assign(opts::dump::DumpTypeIndex
.begin(),
1346 opts::dump::DumpTypeIndex
.end());
1347 } else if (StreamIdx
== StreamIPI
) {
1348 Present
= getPdb().hasPDBIpiStream();
1349 DumpTypes
= opts::dump::DumpIds
;
1350 DumpBytes
= opts::dump::DumpIdData
;
1351 DumpExtras
= opts::dump::DumpIdExtras
;
1352 Indices
.assign(opts::dump::DumpIdIndex
.begin(),
1353 opts::dump::DumpIdIndex
.end());
1357 printStreamNotPresent(StreamIdx
== StreamTPI
? "TPI" : "IPI");
1358 return Error::success();
1361 AutoIndent
Indent(P
);
1362 ExitOnError
Err("Unexpected error processing types: ");
1364 auto &Stream
= Err((StreamIdx
== StreamTPI
) ? getPdb().getPDBTpiStream()
1365 : getPdb().getPDBIpiStream());
1367 auto &Types
= (StreamIdx
== StreamTPI
) ? File
.types() : File
.ids();
1369 // Only emit notes about referenced/unreferenced for types.
1370 TypeReferenceTracker
*MaybeTracker
=
1371 (StreamIdx
== StreamTPI
) ? RefTracker
.get() : nullptr;
1373 // Enable resolving forward decls.
1374 Stream
.buildHashMap();
1376 if (DumpTypes
|| !Indices
.empty()) {
1377 if (Indices
.empty())
1378 dumpFullTypeStream(P
, Types
, MaybeTracker
, Stream
.getNumTypeRecords(),
1379 Stream
.getNumHashBuckets(), Stream
.getHashValues(),
1380 &Stream
, DumpBytes
, DumpExtras
);
1382 std::vector
<TypeIndex
> TiList(Indices
.begin(), Indices
.end());
1383 dumpPartialTypeStream(P
, Types
, MaybeTracker
, Stream
, TiList
, DumpBytes
,
1384 DumpExtras
, opts::dump::DumpTypeDependents
);
1391 P
.formatLine("Header Version: {0}",
1392 static_cast<uint32_t>(Stream
.getTpiVersion()));
1393 P
.formatLine("Hash Stream Index: {0}", Stream
.getTypeHashStreamIndex());
1394 P
.formatLine("Aux Hash Stream Index: {0}",
1395 Stream
.getTypeHashStreamAuxIndex());
1396 P
.formatLine("Hash Key Size: {0}", Stream
.getHashKeySize());
1397 P
.formatLine("Num Hash Buckets: {0}", Stream
.getNumHashBuckets());
1399 auto IndexOffsets
= Stream
.getTypeIndexOffsets();
1400 P
.formatLine("Type Index Offsets:");
1401 for (const auto &IO
: IndexOffsets
) {
1402 AutoIndent
Indent2(P
);
1403 P
.formatLine("TI: {0}, Offset: {1}", IO
.Type
, fmtle(IO
.Offset
));
1406 if (getPdb().hasPDBStringTable()) {
1408 P
.formatLine("Hash Adjusters:");
1409 auto &Adjusters
= Stream
.getHashAdjusters();
1410 auto &Strings
= Err(getPdb().getStringTable());
1411 for (const auto &A
: Adjusters
) {
1412 AutoIndent
Indent2(P
);
1413 auto ExpectedStr
= Strings
.getStringForID(A
.first
);
1414 TypeIndex
TI(A
.second
);
1416 P
.formatLine("`{0}` -> {1}", *ExpectedStr
, TI
);
1418 P
.formatLine("unknown str id ({0}) -> {1}", A
.first
, TI
);
1419 consumeError(ExpectedStr
.takeError());
1424 return Error::success();
1427 Error
DumpOutputStyle::dumpModuleSymsForObj() {
1428 printHeader(P
, "Symbols");
1430 AutoIndent
Indent(P
);
1432 auto &Types
= File
.types();
1434 SymbolVisitorCallbackPipeline Pipeline
;
1435 SymbolDeserializer
Deserializer(nullptr, CodeViewContainer::ObjectFile
);
1436 MinimalSymbolDumper
Dumper(P
, opts::dump::DumpSymRecordBytes
, Types
, Types
);
1438 Pipeline
.addCallbackToPipeline(Deserializer
);
1439 Pipeline
.addCallbackToPipeline(Dumper
);
1440 CVSymbolVisitor
Visitor(Pipeline
);
1442 return iterateModuleSubsections
<DebugSymbolsSubsectionRef
>(
1443 File
, PrintScope
{P
, 2},
1444 [&](uint32_t Modi
, const SymbolGroup
&Strings
,
1445 DebugSymbolsSubsectionRef
&Symbols
) -> Error
{
1446 Dumper
.setSymbolGroup(&Strings
);
1447 for (auto Symbol
: Symbols
) {
1448 if (auto EC
= Visitor
.visitSymbolRecord(Symbol
)) {
1452 return Error::success();
1456 Error
DumpOutputStyle::dumpModuleSymsForPdb() {
1457 printHeader(P
, "Symbols");
1459 if (File
.isPdb() && !getPdb().hasPDBDbiStream()) {
1460 printStreamNotPresent("DBI");
1461 return Error::success();
1464 AutoIndent
Indent(P
);
1466 auto &Ids
= File
.ids();
1467 auto &Types
= File
.types();
1469 return iterateSymbolGroups(
1470 File
, PrintScope
{P
, 2},
1471 [&](uint32_t I
, const SymbolGroup
&Strings
) -> Error
{
1472 auto ExpectedModS
= getModuleDebugStream(File
.pdb(), I
);
1473 if (!ExpectedModS
) {
1474 P
.formatLine("Error loading module stream {0}. {1}", I
,
1475 toString(ExpectedModS
.takeError()));
1476 return Error::success();
1479 ModuleDebugStreamRef
&ModS
= *ExpectedModS
;
1481 SymbolVisitorCallbackPipeline Pipeline
;
1482 SymbolDeserializer
Deserializer(nullptr, CodeViewContainer::Pdb
);
1483 MinimalSymbolDumper
Dumper(P
, opts::dump::DumpSymRecordBytes
, Strings
,
1486 Pipeline
.addCallbackToPipeline(Deserializer
);
1487 Pipeline
.addCallbackToPipeline(Dumper
);
1488 CVSymbolVisitor
Visitor(Pipeline
);
1489 auto SS
= ModS
.getSymbolsSubstream();
1490 if (opts::Filters
.SymbolOffset
) {
1491 CVSymbolVisitor::FilterOptions Filter
;
1492 Filter
.SymbolOffset
= opts::Filters
.SymbolOffset
;
1493 Filter
.ParentRecursiveDepth
= opts::Filters
.ParentRecurseDepth
;
1494 Filter
.ChildRecursiveDepth
= opts::Filters
.ChildrenRecurseDepth
;
1495 if (auto EC
= Visitor
.visitSymbolStreamFiltered(ModS
.getSymbolArray(),
1497 P
.formatLine("Error while processing symbol records. {0}",
1498 toString(std::move(EC
)));
1501 } else if (auto EC
= Visitor
.visitSymbolStream(ModS
.getSymbolArray(),
1503 P
.formatLine("Error while processing symbol records. {0}",
1504 toString(std::move(EC
)));
1507 return Error::success();
1511 Error
DumpOutputStyle::dumpTypeRefStats() {
1512 printHeader(P
, "Type Reference Statistics");
1513 AutoIndent
Indent(P
);
1515 // Sum the byte size of all type records, and the size and count of all
1516 // referenced records.
1517 size_t TotalRecs
= File
.types().size();
1519 size_t TotalBytes
= 0;
1520 size_t RefBytes
= 0;
1521 auto &Types
= File
.types();
1522 for (std::optional
<TypeIndex
> TI
= Types
.getFirst(); TI
;
1523 TI
= Types
.getNext(*TI
)) {
1524 CVType Type
= File
.types().getType(*TI
);
1525 TotalBytes
+= Type
.length();
1526 if (RefTracker
->isTypeReferenced(*TI
)) {
1528 RefBytes
+= Type
.length();
1532 P
.formatLine("Records referenced: {0:N} / {1:N} {2:P}", RefRecs
, TotalRecs
,
1533 (double)RefRecs
/ TotalRecs
);
1534 P
.formatLine("Bytes referenced: {0:N} / {1:N} {2:P}", RefBytes
, TotalBytes
,
1535 (double)RefBytes
/ TotalBytes
);
1537 return Error::success();
1540 Error
DumpOutputStyle::dumpGSIRecords() {
1541 printHeader(P
, "GSI Records");
1544 printStreamNotValidForObj();
1545 return Error::success();
1548 if (!getPdb().hasPDBSymbolStream()) {
1549 printStreamNotPresent("GSI Common Symbol");
1550 return Error::success();
1553 AutoIndent
Indent(P
);
1555 auto &Records
= cantFail(getPdb().getPDBSymbolStream());
1556 auto &Types
= File
.types();
1557 auto &Ids
= File
.ids();
1559 P
.printLine("Records");
1560 SymbolVisitorCallbackPipeline Pipeline
;
1561 SymbolDeserializer
Deserializer(nullptr, CodeViewContainer::Pdb
);
1562 MinimalSymbolDumper
Dumper(P
, opts::dump::DumpSymRecordBytes
, Ids
, Types
);
1564 Pipeline
.addCallbackToPipeline(Deserializer
);
1565 Pipeline
.addCallbackToPipeline(Dumper
);
1566 CVSymbolVisitor
Visitor(Pipeline
);
1568 BinaryStreamRef SymStream
= Records
.getSymbolArray().getUnderlyingStream();
1569 if (auto E
= Visitor
.visitSymbolStream(Records
.getSymbolArray(), 0))
1571 return Error::success();
1574 Error
DumpOutputStyle::dumpGlobals() {
1575 printHeader(P
, "Global Symbols");
1578 printStreamNotValidForObj();
1579 return Error::success();
1582 if (!getPdb().hasPDBGlobalsStream()) {
1583 printStreamNotPresent("Globals");
1584 return Error::success();
1587 AutoIndent
Indent(P
);
1588 ExitOnError
Err("Error dumping globals stream: ");
1589 auto &Globals
= Err(getPdb().getPDBGlobalsStream());
1591 if (opts::dump::DumpGlobalNames
.empty()) {
1592 const GSIHashTable
&Table
= Globals
.getGlobalsTable();
1593 Err(dumpSymbolsFromGSI(Table
, opts::dump::DumpGlobalExtras
));
1595 SymbolStream
&SymRecords
= cantFail(getPdb().getPDBSymbolStream());
1596 auto &Types
= File
.types();
1597 auto &Ids
= File
.ids();
1599 SymbolVisitorCallbackPipeline Pipeline
;
1600 SymbolDeserializer
Deserializer(nullptr, CodeViewContainer::Pdb
);
1601 MinimalSymbolDumper
Dumper(P
, opts::dump::DumpSymRecordBytes
, Ids
, Types
);
1603 Pipeline
.addCallbackToPipeline(Deserializer
);
1604 Pipeline
.addCallbackToPipeline(Dumper
);
1605 CVSymbolVisitor
Visitor(Pipeline
);
1607 using ResultEntryType
= std::pair
<uint32_t, CVSymbol
>;
1608 for (StringRef Name
: opts::dump::DumpGlobalNames
) {
1609 AutoIndent
Indent(P
);
1610 P
.formatLine("Global Name `{0}`", Name
);
1611 std::vector
<ResultEntryType
> Results
=
1612 Globals
.findRecordsByName(Name
, SymRecords
);
1613 if (Results
.empty()) {
1614 AutoIndent
Indent(P
);
1615 P
.printLine("(no matching records found)");
1619 for (ResultEntryType Result
: Results
) {
1620 if (auto E
= Visitor
.visitSymbolRecord(Result
.second
, Result
.first
))
1625 return Error::success();
1628 Error
DumpOutputStyle::dumpPublics() {
1629 printHeader(P
, "Public Symbols");
1632 printStreamNotValidForObj();
1633 return Error::success();
1636 if (!getPdb().hasPDBPublicsStream()) {
1637 printStreamNotPresent("Publics");
1638 return Error::success();
1641 AutoIndent
Indent(P
);
1642 ExitOnError
Err("Error dumping publics stream: ");
1643 auto &Publics
= Err(getPdb().getPDBPublicsStream());
1645 const GSIHashTable
&PublicsTable
= Publics
.getPublicsTable();
1646 if (opts::dump::DumpPublicExtras
) {
1647 P
.printLine("Publics Header");
1648 AutoIndent
Indent(P
);
1649 P
.formatLine("sym hash = {0}, thunk table addr = {1}", Publics
.getSymHash(),
1650 formatSegmentOffset(Publics
.getThunkTableSection(),
1651 Publics
.getThunkTableOffset()));
1653 Err(dumpSymbolsFromGSI(PublicsTable
, opts::dump::DumpPublicExtras
));
1655 // Skip the rest if we aren't dumping extras.
1656 if (!opts::dump::DumpPublicExtras
)
1657 return Error::success();
1659 P
.formatLine("Address Map");
1661 // These are offsets into the publics stream sorted by secidx:secrel.
1662 AutoIndent
Indent2(P
);
1663 for (uint32_t Addr
: Publics
.getAddressMap())
1664 P
.formatLine("off = {0}", Addr
);
1667 // The thunk map is optional debug info used for ILT thunks.
1668 if (!Publics
.getThunkMap().empty()) {
1669 P
.formatLine("Thunk Map");
1670 AutoIndent
Indent2(P
);
1671 for (uint32_t Addr
: Publics
.getThunkMap())
1672 P
.formatLine("{0:x8}", Addr
);
1675 // The section offsets table appears to be empty when incremental linking
1677 if (!Publics
.getSectionOffsets().empty()) {
1678 P
.formatLine("Section Offsets");
1679 AutoIndent
Indent2(P
);
1680 for (const SectionOffset
&SO
: Publics
.getSectionOffsets())
1681 P
.formatLine("{0:x4}:{1:x8}", uint16_t(SO
.Isect
), uint32_t(SO
.Off
));
1684 return Error::success();
1687 Error
DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable
&Table
,
1689 auto ExpectedSyms
= getPdb().getPDBSymbolStream();
1691 return ExpectedSyms
.takeError();
1692 auto &Types
= File
.types();
1693 auto &Ids
= File
.ids();
1696 P
.printLine("GSI Header");
1697 AutoIndent
Indent(P
);
1698 P
.formatLine("sig = {0:X}, hdr = {1:X}, hr size = {2}, num buckets = {3}",
1699 Table
.getVerSignature(), Table
.getVerHeader(),
1700 Table
.getHashRecordSize(), Table
.getNumBuckets());
1704 P
.printLine("Records");
1705 SymbolVisitorCallbackPipeline Pipeline
;
1706 SymbolDeserializer
Deserializer(nullptr, CodeViewContainer::Pdb
);
1707 MinimalSymbolDumper
Dumper(P
, opts::dump::DumpSymRecordBytes
, Ids
, Types
);
1709 Pipeline
.addCallbackToPipeline(Deserializer
);
1710 Pipeline
.addCallbackToPipeline(Dumper
);
1711 CVSymbolVisitor
Visitor(Pipeline
);
1714 BinaryStreamRef SymStream
=
1715 ExpectedSyms
->getSymbolArray().getUnderlyingStream();
1716 for (uint32_t PubSymOff
: Table
) {
1717 Expected
<CVSymbol
> Sym
= readSymbolFromStream(SymStream
, PubSymOff
);
1719 return Sym
.takeError();
1720 if (auto E
= Visitor
.visitSymbolRecord(*Sym
, PubSymOff
))
1725 // Return early if we aren't dumping public hash table and address map info.
1727 P
.formatLine("Hash Entries");
1729 AutoIndent
Indent2(P
);
1730 for (const PSHashRecord
&HR
: Table
.HashRecords
)
1731 P
.formatLine("off = {0}, refcnt = {1}", uint32_t(HR
.Off
),
1735 P
.formatLine("Hash Buckets");
1737 AutoIndent
Indent2(P
);
1738 for (uint32_t Hash
: Table
.HashBuckets
)
1739 P
.formatLine("{0:x8}", Hash
);
1743 return Error::success();
1746 static std::string
formatSegMapDescriptorFlag(uint32_t IndentLevel
,
1747 OMFSegDescFlags Flags
) {
1748 std::vector
<std::string
> Opts
;
1749 if (Flags
== OMFSegDescFlags::None
)
1752 PUSH_FLAG(OMFSegDescFlags
, Read
, Flags
, "read");
1753 PUSH_FLAG(OMFSegDescFlags
, Write
, Flags
, "write");
1754 PUSH_FLAG(OMFSegDescFlags
, Execute
, Flags
, "execute");
1755 PUSH_FLAG(OMFSegDescFlags
, AddressIs32Bit
, Flags
, "32 bit addr");
1756 PUSH_FLAG(OMFSegDescFlags
, IsSelector
, Flags
, "selector");
1757 PUSH_FLAG(OMFSegDescFlags
, IsAbsoluteAddress
, Flags
, "absolute addr");
1758 PUSH_FLAG(OMFSegDescFlags
, IsGroup
, Flags
, "group");
1759 return typesetItemList(Opts
, IndentLevel
, 4, " | ");
1762 Error
DumpOutputStyle::dumpSectionHeaders() {
1763 dumpSectionHeaders("Section Headers", DbgHeaderType::SectionHdr
);
1764 dumpSectionHeaders("Original Section Headers", DbgHeaderType::SectionHdrOrig
);
1765 return Error::success();
1768 void DumpOutputStyle::dumpSectionHeaders(StringRef Label
, DbgHeaderType Type
) {
1769 printHeader(P
, Label
);
1772 printStreamNotValidForObj();
1776 if (!getPdb().hasPDBDbiStream()) {
1777 printStreamNotPresent("DBI");
1781 AutoIndent
Indent(P
);
1782 ExitOnError
Err("Error dumping section headers: ");
1783 std::unique_ptr
<MappedBlockStream
> Stream
;
1784 ArrayRef
<object::coff_section
> Headers
;
1785 auto ExpectedHeaders
= loadSectionHeaders(getPdb(), Type
);
1786 if (!ExpectedHeaders
) {
1787 P
.printLine(toString(ExpectedHeaders
.takeError()));
1790 std::tie(Stream
, Headers
) = std::move(*ExpectedHeaders
);
1793 for (const auto &Header
: Headers
) {
1795 P
.formatLine("SECTION HEADER #{0}", I
);
1796 P
.formatLine("{0,8} name", Header
.Name
);
1797 P
.formatLine("{0,8:X-} virtual size", uint32_t(Header
.VirtualSize
));
1798 P
.formatLine("{0,8:X-} virtual address", uint32_t(Header
.VirtualAddress
));
1799 P
.formatLine("{0,8:X-} size of raw data", uint32_t(Header
.SizeOfRawData
));
1800 P
.formatLine("{0,8:X-} file pointer to raw data",
1801 uint32_t(Header
.PointerToRawData
));
1802 P
.formatLine("{0,8:X-} file pointer to relocation table",
1803 uint32_t(Header
.PointerToRelocations
));
1804 P
.formatLine("{0,8:X-} file pointer to line numbers",
1805 uint32_t(Header
.PointerToLinenumbers
));
1806 P
.formatLine("{0,8:X-} number of relocations",
1807 uint32_t(Header
.NumberOfRelocations
));
1808 P
.formatLine("{0,8:X-} number of line numbers",
1809 uint32_t(Header
.NumberOfLinenumbers
));
1810 P
.formatLine("{0,8:X-} flags", uint32_t(Header
.Characteristics
));
1811 AutoIndent
IndentMore(P
, 9);
1812 P
.formatLine("{0}", formatSectionCharacteristics(
1813 P
.getIndentLevel(), Header
.Characteristics
, 1, ""));
1818 Error
DumpOutputStyle::dumpSectionContribs() {
1819 printHeader(P
, "Section Contributions");
1822 printStreamNotValidForObj();
1823 return Error::success();
1826 if (!getPdb().hasPDBDbiStream()) {
1827 printStreamNotPresent("DBI");
1828 return Error::success();
1831 AutoIndent
Indent(P
);
1832 ExitOnError
Err("Error dumping section contributions: ");
1834 DbiStream
&Dbi
= Err(getPdb().getPDBDbiStream());
1836 class Visitor
: public ISectionContribVisitor
{
1838 Visitor(LinePrinter
&P
, ArrayRef
<std::string
> Names
) : P(P
), Names(Names
) {
1839 auto Max
= std::max_element(
1840 Names
.begin(), Names
.end(),
1841 [](StringRef S1
, StringRef S2
) { return S1
.size() < S2
.size(); });
1842 MaxNameLen
= (Max
== Names
.end() ? 0 : Max
->size());
1844 void visit(const SectionContrib
&SC
) override
{
1845 dumpSectionContrib(P
, SC
, Names
, MaxNameLen
);
1847 void visit(const SectionContrib2
&SC
) override
{
1848 dumpSectionContrib(P
, SC
, Names
, MaxNameLen
);
1853 uint32_t MaxNameLen
;
1854 ArrayRef
<std::string
> Names
;
1857 auto NamesOrErr
= getSectionNames(getPdb());
1859 return NamesOrErr
.takeError();
1860 ArrayRef
<std::string
> Names
= *NamesOrErr
;
1861 Visitor
V(P
, Names
);
1862 Dbi
.visitSectionContributions(V
);
1863 return Error::success();
1866 Error
DumpOutputStyle::dumpSectionMap() {
1867 printHeader(P
, "Section Map");
1870 printStreamNotValidForObj();
1871 return Error::success();
1874 if (!getPdb().hasPDBDbiStream()) {
1875 printStreamNotPresent("DBI");
1876 return Error::success();
1879 AutoIndent
Indent(P
);
1880 ExitOnError
Err("Error dumping section map: ");
1882 DbiStream
&Dbi
= Err(getPdb().getPDBDbiStream());
1885 for (auto &M
: Dbi
.getSectionMap()) {
1887 "Section {0:4} | ovl = {1}, group = {2}, frame = {3}, name = {4}", I
,
1888 fmtle(M
.Ovl
), fmtle(M
.Group
), fmtle(M
.Frame
), fmtle(M
.SecName
));
1889 P
.formatLine(" class = {0}, offset = {1}, size = {2}",
1890 fmtle(M
.ClassName
), fmtle(M
.Offset
), fmtle(M
.SecByteLength
));
1891 P
.formatLine(" flags = {0}",
1892 formatSegMapDescriptorFlag(
1893 P
.getIndentLevel() + 13,
1894 static_cast<OMFSegDescFlags
>(uint16_t(M
.Flags
))));
1897 return Error::success();