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");
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
),
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
;
1086 consumeError(ES
.takeError());
1087 Str
= "Error reading string";
1088 } else if (!ES
->empty()) {
1095 P
.formatLine("{0} | {1}", fmt_align(I
, AlignStyle::Right
, Digits
),
1101 if (opts::dump::DumpStringTableDetails
) {
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());
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);
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) {
1139 uint32_t Offset
= Reader
.getOffset();
1140 cantFail(Reader
.readCString(Str
));
1144 P
.formatLine("{0} | {1}", fmt_align(Offset
, AlignStyle::Right
, 4),
1147 return Error::success();
1151 Error
DumpOutputStyle::dumpNamedStreams() {
1152 printHeader(P
, "Named Streams");
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");
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
) {
1190 if (DepSet
.find(TI
) != DepSet
.end() || TI
.isSimple() || TI
.isNoneType())
1193 CVType Type
= Types
.getType(TI
);
1195 codeview::discoverTypeIndices(Type
, DepList
);
1196 buildDepSet(Types
, DepList
, DepSet
);
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
) {
1225 NumDigits(TypeIndex::FirstNonSimpleIndex
+ Stream
.getNumTypeRecords());
1227 MinimalTypeDumpVisitor
V(Printer
, Width
+ 2, Bytes
, Extras
, Types
, RefTracker
,
1228 Stream
.getNumHashBuckets(), Stream
.getHashValues(),
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
);
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
)));
1247 Printer
.formatLine("Showing {0:N} records.", TiList
.size());
1249 for (const auto &I
: TiList
) {
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
)));
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();
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)");
1283 Expected
<StringRef
> ContentsOrErr
= S
.getContents();
1285 return ContentsOrErr
.takeError();
1288 BinaryStreamReader
Reader(*ContentsOrErr
, llvm::endianness::little
);
1289 if (auto EC
= Reader
.readInteger(Magic
))
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
);
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());
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
);
1381 std::vector
<TypeIndex
> TiList(Indices
.begin(), Indices
.end());
1382 dumpPartialTypeStream(P
, Types
, MaybeTracker
, Stream
, TiList
, DumpBytes
,
1383 DumpExtras
, opts::dump::DumpTypeDependents
);
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()) {
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
);
1415 P
.formatLine("`{0}` -> {1}", *ExpectedStr
, TI
);
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
)) {
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
,
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(),
1496 P
.formatLine("Error while processing symbol records. {0}",
1497 toStringWithoutConsuming(EC
));
1500 } else if (auto EC
= Visitor
.visitSymbolStream(ModS
.getSymbolArray(),
1502 P
.formatLine("Error while processing symbol records. {0}",
1503 toStringWithoutConsuming(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();
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
)) {
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");
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))
1570 return Error::success();
1573 Error
DumpOutputStyle::dumpGlobals() {
1574 printHeader(P
, "Global Symbols");
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
));
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)");
1618 for (ResultEntryType Result
: Results
) {
1619 if (auto E
= Visitor
.visitSymbolRecord(Result
.second
, Result
.first
))
1624 return Error::success();
1627 Error
DumpOutputStyle::dumpPublics() {
1628 printHeader(P
, "Public Symbols");
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
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
,
1688 auto ExpectedSyms
= getPdb().getPDBSymbolStream();
1690 return ExpectedSyms
.takeError();
1691 auto &Types
= File
.types();
1692 auto &Ids
= File
.ids();
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
);
1718 return Sym
.takeError();
1719 if (auto E
= Visitor
.visitSymbolRecord(*Sym
, PubSymOff
))
1724 // Return early if we aren't dumping public hash table and address map info.
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
),
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
)
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
);
1771 printStreamNotValidForObj();
1775 if (!getPdb().hasPDBDbiStream()) {
1776 printStreamNotPresent("DBI");
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()));
1789 std::tie(Stream
, Headers
) = std::move(*ExpectedHeaders
);
1792 for (const auto &Header
: Headers
) {
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, ""));
1817 Error
DumpOutputStyle::dumpSectionContribs() {
1818 printHeader(P
, "Section Contributions");
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
{
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
);
1852 uint32_t MaxNameLen
;
1853 ArrayRef
<std::string
> Names
;
1856 auto NamesOrErr
= getSectionNames(getPdb());
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");
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());
1884 for (auto &M
: Dbi
.getSectionMap()) {
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
))));
1896 return Error::success();