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