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