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