]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp
Upgrade our copies of clang, llvm, lld, lldb, compiler-rt and libc++ to
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / llvm-pdbutil / DumpOutputStyle.cpp
1 //===- DumpOutputStyle.cpp ------------------------------------ *- C++ --*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "DumpOutputStyle.h"
11
12 #include "FormatUtil.h"
13 #include "InputFile.h"
14 #include "MinimalSymbolDumper.h"
15 #include "MinimalTypeDumper.h"
16 #include "StreamUtil.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/DebugInlineeLinesSubsection.h"
26 #include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h"
27 #include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h"
28 #include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h"
29 #include "llvm/DebugInfo/CodeView/Formatters.h"
30 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
31 #include "llvm/DebugInfo/CodeView/Line.h"
32 #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
33 #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h"
34 #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h"
35 #include "llvm/DebugInfo/CodeView/TypeHashing.h"
36 #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
37 #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
38 #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"
39 #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
40 #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
41 #include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h"
42 #include "llvm/DebugInfo/PDB/Native/InfoStream.h"
43 #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
44 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
45 #include "llvm/DebugInfo/PDB/Native/PublicsStream.h"
46 #include "llvm/DebugInfo/PDB/Native/RawError.h"
47 #include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
48 #include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
49 #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
50 #include "llvm/Object/COFF.h"
51 #include "llvm/Support/BinaryStreamReader.h"
52 #include "llvm/Support/FormatAdapters.h"
53 #include "llvm/Support/FormatVariadic.h"
54
55 #include <cctype>
56
57 using namespace llvm;
58 using namespace llvm::codeview;
59 using namespace llvm::msf;
60 using namespace llvm::pdb;
61
62 DumpOutputStyle::DumpOutputStyle(InputFile &File)
63     : File(File), P(2, false, outs()) {}
64
65 PDBFile &DumpOutputStyle::getPdb() { return File.pdb(); }
66 object::COFFObjectFile &DumpOutputStyle::getObj() { return File.obj(); }
67
68 Error DumpOutputStyle::dump() {
69   if (opts::dump::DumpSummary) {
70     if (auto EC = dumpFileSummary())
71       return EC;
72     P.NewLine();
73   }
74
75   if (opts::dump::DumpStreams) {
76     if (auto EC = dumpStreamSummary())
77       return EC;
78     P.NewLine();
79   }
80
81   if (opts::dump::DumpSymbolStats) {
82     if (auto EC = dumpSymbolStats())
83       return EC;
84     P.NewLine();
85   }
86
87   if (opts::dump::DumpUdtStats) {
88     if (auto EC = dumpUdtStats())
89       return EC;
90     P.NewLine();
91   }
92
93   if (opts::dump::DumpStringTable) {
94     if (auto EC = dumpStringTable())
95       return EC;
96     P.NewLine();
97   }
98
99   if (opts::dump::DumpModules) {
100     if (auto EC = dumpModules())
101       return EC;
102   }
103
104   if (opts::dump::DumpModuleFiles) {
105     if (auto EC = dumpModuleFiles())
106       return EC;
107   }
108
109   if (opts::dump::DumpLines) {
110     if (auto EC = dumpLines())
111       return EC;
112   }
113
114   if (opts::dump::DumpInlineeLines) {
115     if (auto EC = dumpInlineeLines())
116       return EC;
117   }
118
119   if (opts::dump::DumpXmi) {
120     if (auto EC = dumpXmi())
121       return EC;
122   }
123
124   if (opts::dump::DumpXme) {
125     if (auto EC = dumpXme())
126       return EC;
127   }
128
129   if (File.isObj()) {
130     if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() ||
131         opts::dump::DumpTypeExtras)
132       if (auto EC = dumpTypesFromObjectFile())
133         return EC;
134   } else {
135     if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() ||
136         opts::dump::DumpTypeExtras) {
137       if (auto EC = dumpTpiStream(StreamTPI))
138         return EC;
139     }
140
141     if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() ||
142         opts::dump::DumpIdExtras) {
143       if (auto EC = dumpTpiStream(StreamIPI))
144         return EC;
145     }
146   }
147
148   if (opts::dump::DumpGlobals) {
149     if (auto EC = dumpGlobals())
150       return EC;
151   }
152
153   if (opts::dump::DumpPublics) {
154     if (auto EC = dumpPublics())
155       return EC;
156   }
157
158   if (opts::dump::DumpSymbols) {
159     auto EC = File.isPdb() ? dumpModuleSymsForPdb() : dumpModuleSymsForObj();
160     if (EC)
161       return EC;
162   }
163
164   if (opts::dump::DumpSectionHeaders) {
165     if (auto EC = dumpSectionHeaders())
166       return EC;
167   }
168
169   if (opts::dump::DumpSectionContribs) {
170     if (auto EC = dumpSectionContribs())
171       return EC;
172   }
173
174   if (opts::dump::DumpSectionMap) {
175     if (auto EC = dumpSectionMap())
176       return EC;
177   }
178
179   return Error::success();
180 }
181
182 static void printHeader(LinePrinter &P, const Twine &S) {
183   P.NewLine();
184   P.formatLine("{0,=60}", S);
185   P.formatLine("{0}", fmt_repeat('=', 60));
186 }
187
188 Error DumpOutputStyle::dumpFileSummary() {
189   printHeader(P, "Summary");
190
191   ExitOnError Err("Invalid PDB Format: ");
192
193   AutoIndent Indent(P);
194   if (File.isObj()) {
195     P.formatLine("Dumping File summary is not valid for object files");
196     return Error::success();
197   }
198
199   P.formatLine("Block Size: {0}", getPdb().getBlockSize());
200   P.formatLine("Number of blocks: {0}", getPdb().getBlockCount());
201   P.formatLine("Number of streams: {0}", getPdb().getNumStreams());
202
203   auto &PS = Err(getPdb().getPDBInfoStream());
204   P.formatLine("Signature: {0}", PS.getSignature());
205   P.formatLine("Age: {0}", PS.getAge());
206   P.formatLine("GUID: {0}", fmt_guid(PS.getGuid().Guid));
207   P.formatLine("Features: {0:x+}", static_cast<uint32_t>(PS.getFeatures()));
208   P.formatLine("Has Debug Info: {0}", getPdb().hasPDBDbiStream());
209   P.formatLine("Has Types: {0}", getPdb().hasPDBTpiStream());
210   P.formatLine("Has IDs: {0}", getPdb().hasPDBIpiStream());
211   P.formatLine("Has Globals: {0}", getPdb().hasPDBGlobalsStream());
212   P.formatLine("Has Publics: {0}", getPdb().hasPDBPublicsStream());
213   if (getPdb().hasPDBDbiStream()) {
214     auto &DBI = Err(getPdb().getPDBDbiStream());
215     P.formatLine("Is incrementally linked: {0}", DBI.isIncrementallyLinked());
216     P.formatLine("Has conflicting types: {0}", DBI.hasCTypes());
217     P.formatLine("Is stripped: {0}", DBI.isStripped());
218   }
219
220   return Error::success();
221 }
222
223 static StatCollection getSymbolStats(const SymbolGroup &SG,
224                                      StatCollection &CumulativeStats) {
225   StatCollection Stats;
226   if (SG.getFile().isPdb()) {
227     // For PDB files, all symbols are packed into one stream.
228     for (const auto &S : SG.getPdbModuleStream().symbols(nullptr)) {
229       Stats.update(S.kind(), S.length());
230       CumulativeStats.update(S.kind(), S.length());
231     }
232     return Stats;
233   }
234
235   for (const auto &SS : SG.getDebugSubsections()) {
236     // For object files, all symbols are spread across multiple Symbol
237     // subsections of a given .debug$S section.
238     if (SS.kind() != DebugSubsectionKind::Symbols)
239       continue;
240     DebugSymbolsSubsectionRef Symbols;
241     BinaryStreamReader Reader(SS.getRecordData());
242     cantFail(Symbols.initialize(Reader));
243     for (const auto &S : Symbols) {
244       Stats.update(S.kind(), S.length());
245       CumulativeStats.update(S.kind(), S.length());
246     }
247   }
248   return Stats;
249 }
250
251 static StatCollection getChunkStats(const SymbolGroup &SG,
252                                     StatCollection &CumulativeStats) {
253   StatCollection Stats;
254   for (const auto &Chunk : SG.getDebugSubsections()) {
255     Stats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength());
256     CumulativeStats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength());
257   }
258   return Stats;
259 }
260
261 static inline std::string formatModuleDetailKind(DebugSubsectionKind K) {
262   return formatChunkKind(K, false);
263 }
264
265 static inline std::string formatModuleDetailKind(SymbolKind K) {
266   return formatSymbolKind(K);
267 }
268
269 template <typename Kind>
270 static void printModuleDetailStats(LinePrinter &P, StringRef Label,
271                                    const StatCollection &Stats) {
272   P.NewLine();
273   P.formatLine("  {0}", Label);
274   AutoIndent Indent(P);
275   P.formatLine("{0,40}: {1,7} entries ({2,8} bytes)", "Total",
276                Stats.Totals.Count, Stats.Totals.Size);
277   P.formatLine("{0}", fmt_repeat('-', 74));
278   for (const auto &K : Stats.Individual) {
279     std::string KindName = formatModuleDetailKind(Kind(K.first));
280     P.formatLine("{0,40}: {1,7} entries ({2,8} bytes)", KindName,
281                  K.second.Count, K.second.Size);
282   }
283 }
284
285 static bool isMyCode(const SymbolGroup &Group) {
286   if (Group.getFile().isObj())
287     return true;
288
289   StringRef Name = Group.name();
290   if (Name.startswith("Import:"))
291     return false;
292   if (Name.endswith_lower(".dll"))
293     return false;
294   if (Name.equals_lower("* linker *"))
295     return false;
296   if (Name.startswith_lower("f:\\binaries\\Intermediate\\vctools"))
297     return false;
298   if (Name.startswith_lower("f:\\dd\\vctools\\crt"))
299     return false;
300   return true;
301 }
302
303 static bool shouldDumpSymbolGroup(uint32_t Idx, const SymbolGroup &Group) {
304   if (opts::dump::JustMyCode && !isMyCode(Group))
305     return false;
306
307   // If the arg was not specified on the command line, always dump all modules.
308   if (opts::dump::DumpModi.getNumOccurrences() == 0)
309     return true;
310
311   // Otherwise, only dump if this is the same module specified.
312   return (opts::dump::DumpModi == Idx);
313 }
314
315 Error DumpOutputStyle::dumpStreamSummary() {
316   printHeader(P, "Streams");
317
318   AutoIndent Indent(P);
319   if (File.isObj()) {
320     P.formatLine("Dumping streams is not valid for object files");
321     return Error::success();
322   }
323
324   if (StreamPurposes.empty())
325     discoverStreamPurposes(getPdb(), StreamPurposes);
326
327   uint32_t StreamCount = getPdb().getNumStreams();
328   uint32_t MaxStreamSize = getPdb().getMaxStreamSize();
329
330   for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {
331     P.formatLine(
332         "Stream {0} ({1} bytes): [{2}]",
333         fmt_align(StreamIdx, AlignStyle::Right, NumDigits(StreamCount)),
334         fmt_align(getPdb().getStreamByteSize(StreamIdx), AlignStyle::Right,
335                   NumDigits(MaxStreamSize)),
336         StreamPurposes[StreamIdx].getLongName());
337
338     if (opts::dump::DumpStreamBlocks) {
339       auto Blocks = getPdb().getStreamBlockList(StreamIdx);
340       std::vector<uint32_t> BV(Blocks.begin(), Blocks.end());
341       P.formatLine("       {0}  Blocks: [{1}]",
342                    fmt_repeat(' ', NumDigits(StreamCount)),
343                    make_range(BV.begin(), BV.end()));
344     }
345   }
346
347   return Error::success();
348 }
349
350 static Expected<ModuleDebugStreamRef> getModuleDebugStream(PDBFile &File,
351                                                            uint32_t Index) {
352   ExitOnError Err("Unexpected error: ");
353
354   auto &Dbi = Err(File.getPDBDbiStream());
355   const auto &Modules = Dbi.modules();
356   auto Modi = Modules.getModuleDescriptor(Index);
357
358   uint16_t ModiStream = Modi.getModuleStreamIndex();
359   if (ModiStream == kInvalidStreamIndex)
360     return make_error<RawError>(raw_error_code::no_stream,
361                                 "Module stream not present");
362
363   auto ModStreamData = File.createIndexedStream(ModiStream);
364
365   ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData));
366   if (auto EC = ModS.reload())
367     return make_error<RawError>(raw_error_code::corrupt_file,
368                                 "Invalid module stream");
369
370   return std::move(ModS);
371 }
372
373 template <typename CallbackT>
374 static void
375 iterateOneModule(InputFile &File, const Optional<PrintScope> &HeaderScope,
376                  const SymbolGroup &SG, uint32_t Modi, CallbackT Callback) {
377   if (HeaderScope) {
378     HeaderScope->P.formatLine(
379         "Mod {0:4} | `{1}`: ",
380         fmt_align(Modi, AlignStyle::Right, HeaderScope->LabelWidth), SG.name());
381   }
382
383   AutoIndent Indent(HeaderScope);
384   Callback(Modi, SG);
385 }
386
387 template <typename CallbackT>
388 static void iterateSymbolGroups(InputFile &Input,
389                                 const Optional<PrintScope> &HeaderScope,
390                                 CallbackT Callback) {
391   AutoIndent Indent(HeaderScope);
392
393   ExitOnError Err("Unexpected error processing modules: ");
394
395   if (opts::dump::DumpModi.getNumOccurrences() > 0) {
396     assert(opts::dump::DumpModi.getNumOccurrences() == 1);
397     uint32_t Modi = opts::dump::DumpModi;
398     SymbolGroup SG(&Input, Modi);
399     iterateOneModule(Input, withLabelWidth(HeaderScope, NumDigits(Modi)), SG,
400                      Modi, Callback);
401     return;
402   }
403
404   uint32_t I = 0;
405
406   for (const auto &SG : Input.symbol_groups()) {
407     if (shouldDumpSymbolGroup(I, SG))
408       iterateOneModule(Input, withLabelWidth(HeaderScope, NumDigits(I)), SG, I,
409                        Callback);
410
411     ++I;
412   }
413 }
414
415 template <typename SubsectionT>
416 static void iterateModuleSubsections(
417     InputFile &File, const Optional<PrintScope> &HeaderScope,
418     llvm::function_ref<void(uint32_t, const SymbolGroup &, SubsectionT &)>
419         Callback) {
420
421   iterateSymbolGroups(File, HeaderScope,
422                       [&](uint32_t Modi, const SymbolGroup &SG) {
423                         for (const auto &SS : SG.getDebugSubsections()) {
424                           SubsectionT Subsection;
425
426                           if (SS.kind() != Subsection.kind())
427                             continue;
428
429                           BinaryStreamReader Reader(SS.getRecordData());
430                           if (auto EC = Subsection.initialize(Reader))
431                             continue;
432                           Callback(Modi, SG, Subsection);
433                         }
434                       });
435 }
436
437 Error DumpOutputStyle::dumpModules() {
438   printHeader(P, "Modules");
439   AutoIndent Indent(P);
440
441   if (File.isObj()) {
442     P.formatLine("Dumping modules is not supported for object files");
443     return Error::success();
444   }
445
446   if (!getPdb().hasPDBDbiStream()) {
447     P.formatLine("DBI Stream not present");
448     return Error::success();
449   }
450
451   ExitOnError Err("Unexpected error processing modules: ");
452
453   auto &Stream = Err(getPdb().getPDBDbiStream());
454
455   const DbiModuleList &Modules = Stream.modules();
456   iterateSymbolGroups(
457       File, PrintScope{P, 11}, [&](uint32_t Modi, const SymbolGroup &Strings) {
458         auto Desc = Modules.getModuleDescriptor(Modi);
459         P.formatLine("Obj: `{0}`: ", Desc.getObjFileName());
460         P.formatLine("debug stream: {0}, # files: {1}, has ec info: {2}",
461                      Desc.getModuleStreamIndex(), Desc.getNumberOfFiles(),
462                      Desc.hasECInfo());
463         StringRef PdbFilePath =
464             Err(Stream.getECName(Desc.getPdbFilePathNameIndex()));
465         StringRef SrcFilePath =
466             Err(Stream.getECName(Desc.getSourceFileNameIndex()));
467         P.formatLine("pdb file ni: {0} `{1}`, src file ni: {2} `{3}`",
468                      Desc.getPdbFilePathNameIndex(), PdbFilePath,
469                      Desc.getSourceFileNameIndex(), SrcFilePath);
470       });
471   return Error::success();
472 }
473
474 Error DumpOutputStyle::dumpModuleFiles() {
475   printHeader(P, "Files");
476
477   if (File.isObj()) {
478     P.formatLine("Dumping files is not valid for object files");
479     return Error::success();
480   }
481
482   ExitOnError Err("Unexpected error processing modules: ");
483
484   iterateSymbolGroups(File, PrintScope{P, 11},
485                       [this, &Err](uint32_t Modi, const SymbolGroup &Strings) {
486                         auto &Stream = Err(getPdb().getPDBDbiStream());
487
488                         const DbiModuleList &Modules = Stream.modules();
489                         for (const auto &F : Modules.source_files(Modi)) {
490                           Strings.formatFromFileName(P, F);
491                         }
492                       });
493   return Error::success();
494 }
495
496 Error DumpOutputStyle::dumpSymbolStats() {
497   printHeader(P, "Module Stats");
498
499   ExitOnError Err("Unexpected error processing modules: ");
500
501   StatCollection SymStats;
502   StatCollection ChunkStats;
503
504   Optional<PrintScope> Scope;
505   if (File.isPdb())
506     Scope.emplace(P, 2);
507
508   iterateSymbolGroups(File, Scope, [&](uint32_t Modi, const SymbolGroup &SG) {
509     StatCollection SS = getSymbolStats(SG, SymStats);
510     StatCollection CS = getChunkStats(SG, ChunkStats);
511
512     if (SG.getFile().isPdb()) {
513       AutoIndent Indent(P);
514       auto Modules = cantFail(File.pdb().getPDBDbiStream()).modules();
515       uint32_t ModCount = Modules.getModuleCount();
516       DbiModuleDescriptor Desc = Modules.getModuleDescriptor(Modi);
517       uint32_t StreamIdx = Desc.getModuleStreamIndex();
518
519       if (StreamIdx == kInvalidStreamIndex) {
520         P.formatLine("Mod {0} (debug info not present): [{1}]",
521                      fmt_align(Modi, AlignStyle::Right, NumDigits(ModCount)),
522                      Desc.getModuleName());
523         return;
524       }
525       P.formatLine("Stream {0}, {1} bytes", StreamIdx,
526                    getPdb().getStreamByteSize(StreamIdx));
527
528       printModuleDetailStats<SymbolKind>(P, "Symbols", SS);
529       printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", CS);
530     }
531   });
532
533   P.printLine("  Summary |");
534   AutoIndent Indent(P, 4);
535   if (SymStats.Totals.Count > 0) {
536     printModuleDetailStats<SymbolKind>(P, "Symbols", SymStats);
537     printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", ChunkStats);
538   }
539
540   return Error::success();
541 }
542
543 static bool isValidNamespaceIdentifier(StringRef S) {
544   if (S.empty())
545     return false;
546
547   if (std::isdigit(S[0]))
548     return false;
549
550   return llvm::all_of(S, [](char C) { return std::isalnum(C); });
551 }
552
553 namespace {
554 constexpr uint32_t kNoneUdtKind = 0;
555 constexpr uint32_t kSimpleUdtKind = 1;
556 constexpr uint32_t kUnknownUdtKind = 2;
557 const StringRef NoneLabel("<none type>");
558 const StringRef SimpleLabel("<simple type>");
559 const StringRef UnknownLabel("<unknown type>");
560
561 } // namespace
562
563 static StringRef getUdtStatLabel(uint32_t Kind) {
564   if (Kind == kNoneUdtKind)
565     return NoneLabel;
566
567   if (Kind == kSimpleUdtKind)
568     return SimpleLabel;
569
570   if (Kind == kUnknownUdtKind)
571     return UnknownLabel;
572
573   return formatTypeLeafKind(static_cast<TypeLeafKind>(Kind));
574 }
575
576 static uint32_t getLongestTypeLeafName(const StatCollection &Stats) {
577   size_t L = 0;
578   for (const auto &Stat : Stats.Individual) {
579     StringRef Label = getUdtStatLabel(Stat.first);
580     L = std::max(L, Label.size());
581   }
582   return static_cast<uint32_t>(L);
583 }
584
585 Error DumpOutputStyle::dumpUdtStats() {
586   printHeader(P, "S_UDT Record Stats");
587
588   StatCollection UdtStats;
589   StatCollection UdtTargetStats;
590   AutoIndent Indent(P, 4);
591
592   auto &TpiTypes = File.types();
593
594   StringMap<StatCollection::Stat> NamespacedStats;
595
596   size_t LongestNamespace = 0;
597   auto HandleOneSymbol = [&](const CVSymbol &Sym) {
598     if (Sym.kind() != SymbolKind::S_UDT)
599       return;
600     UdtStats.update(SymbolKind::S_UDT, Sym.length());
601
602     UDTSym UDT = cantFail(SymbolDeserializer::deserializeAs<UDTSym>(Sym));
603
604     uint32_t Kind = 0;
605     uint32_t RecordSize = 0;
606
607     if (UDT.Type.isNoneType())
608       Kind = kNoneUdtKind;
609     else if (UDT.Type.isSimple())
610       Kind = kSimpleUdtKind;
611     else if (Optional<CVType> T = TpiTypes.tryGetType(UDT.Type)) {
612       Kind = T->kind();
613       RecordSize = T->length();
614     } else
615       Kind = kUnknownUdtKind;
616
617     UdtTargetStats.update(Kind, RecordSize);
618
619     size_t Pos = UDT.Name.find("::");
620     if (Pos == StringRef::npos)
621       return;
622
623     StringRef Scope = UDT.Name.take_front(Pos);
624     if (Scope.empty() || !isValidNamespaceIdentifier(Scope))
625       return;
626
627     LongestNamespace = std::max(LongestNamespace, Scope.size());
628     NamespacedStats[Scope].update(RecordSize);
629   };
630
631   P.NewLine();
632
633   if (File.isPdb()) {
634     if (!getPdb().hasPDBGlobalsStream()) {
635       P.printLine("- Error: globals stream not present");
636       return Error::success();
637     }
638
639     auto &SymbolRecords = cantFail(getPdb().getPDBSymbolStream());
640     auto ExpGlobals = getPdb().getPDBGlobalsStream();
641     if (!ExpGlobals)
642       return ExpGlobals.takeError();
643
644     for (uint32_t PubSymOff : ExpGlobals->getGlobalsTable()) {
645       CVSymbol Sym = SymbolRecords.readRecord(PubSymOff);
646       HandleOneSymbol(Sym);
647     }
648   } else {
649     for (const auto &Sec : File.symbol_groups()) {
650       for (const auto &SS : Sec.getDebugSubsections()) {
651         if (SS.kind() != DebugSubsectionKind::Symbols)
652           continue;
653
654         DebugSymbolsSubsectionRef Symbols;
655         BinaryStreamReader Reader(SS.getRecordData());
656         cantFail(Symbols.initialize(Reader));
657         for (const auto &S : Symbols)
658           HandleOneSymbol(S);
659       }
660     }
661   }
662
663   LongestNamespace += StringRef(" namespace ''").size();
664   size_t LongestTypeLeafKind = getLongestTypeLeafName(UdtTargetStats);
665   size_t FieldWidth = std::max(LongestNamespace, LongestTypeLeafKind);
666
667   // Compute the max number of digits for count and size fields, including comma
668   // separators.
669   StringRef CountHeader("Count");
670   StringRef SizeHeader("Size");
671   size_t CD = NumDigits(UdtStats.Totals.Count);
672   CD += (CD - 1) / 3;
673   CD = std::max(CD, CountHeader.size());
674
675   size_t SD = NumDigits(UdtStats.Totals.Size);
676   SD += (SD - 1) / 3;
677   SD = std::max(SD, SizeHeader.size());
678
679   uint32_t TableWidth = FieldWidth + 3 + CD + 2 + SD + 1;
680
681   P.formatLine("{0} | {1}  {2}",
682                fmt_align("Record Kind", AlignStyle::Right, FieldWidth),
683                fmt_align(CountHeader, AlignStyle::Right, CD),
684                fmt_align(SizeHeader, AlignStyle::Right, SD));
685
686   P.formatLine("{0}", fmt_repeat('-', TableWidth));
687   for (const auto &Stat : UdtTargetStats.Individual) {
688     StringRef Label = getUdtStatLabel(Stat.first);
689     P.formatLine("{0} | {1:N}  {2:N}",
690                  fmt_align(Label, AlignStyle::Right, FieldWidth),
691                  fmt_align(Stat.second.Count, AlignStyle::Right, CD),
692                  fmt_align(Stat.second.Size, AlignStyle::Right, SD));
693   }
694   P.formatLine("{0}", fmt_repeat('-', TableWidth));
695   P.formatLine("{0} | {1:N}  {2:N}",
696                fmt_align("Total (S_UDT)", AlignStyle::Right, FieldWidth),
697                fmt_align(UdtStats.Totals.Count, AlignStyle::Right, CD),
698                fmt_align(UdtStats.Totals.Size, AlignStyle::Right, SD));
699   P.formatLine("{0}", fmt_repeat('-', TableWidth));
700   for (const auto &Stat : NamespacedStats) {
701     std::string Label = formatv("namespace '{0}'", Stat.getKey());
702     P.formatLine("{0} | {1:N}  {2:N}",
703                  fmt_align(Label, AlignStyle::Right, FieldWidth),
704                  fmt_align(Stat.second.Count, AlignStyle::Right, CD),
705                  fmt_align(Stat.second.Size, AlignStyle::Right, SD));
706   }
707   return Error::success();
708 }
709
710 static void typesetLinesAndColumns(LinePrinter &P, uint32_t Start,
711                                    const LineColumnEntry &E) {
712   const uint32_t kMaxCharsPerLineNumber = 4; // 4 digit line number
713   uint32_t MinColumnWidth = kMaxCharsPerLineNumber + 5;
714
715   // Let's try to keep it under 100 characters
716   constexpr uint32_t kMaxRowLength = 100;
717   // At least 3 spaces between columns.
718   uint32_t ColumnsPerRow = kMaxRowLength / (MinColumnWidth + 3);
719   uint32_t ItemsLeft = E.LineNumbers.size();
720   auto LineIter = E.LineNumbers.begin();
721   while (ItemsLeft != 0) {
722     uint32_t RowColumns = std::min(ItemsLeft, ColumnsPerRow);
723     for (uint32_t I = 0; I < RowColumns; ++I) {
724       LineInfo Line(LineIter->Flags);
725       std::string LineStr;
726       if (Line.isAlwaysStepInto())
727         LineStr = "ASI";
728       else if (Line.isNeverStepInto())
729         LineStr = "NSI";
730       else
731         LineStr = utostr(Line.getStartLine());
732       char Statement = Line.isStatement() ? ' ' : '!';
733       P.format("{0} {1:X-} {2} ",
734                fmt_align(LineStr, AlignStyle::Right, kMaxCharsPerLineNumber),
735                fmt_align(Start + LineIter->Offset, AlignStyle::Right, 8, '0'),
736                Statement);
737       ++LineIter;
738       --ItemsLeft;
739     }
740     P.NewLine();
741   }
742 }
743
744 Error DumpOutputStyle::dumpLines() {
745   printHeader(P, "Lines");
746
747   uint32_t LastModi = UINT32_MAX;
748   uint32_t LastNameIndex = UINT32_MAX;
749   iterateModuleSubsections<DebugLinesSubsectionRef>(
750       File, PrintScope{P, 4},
751       [this, &LastModi, &LastNameIndex](uint32_t Modi,
752                                         const SymbolGroup &Strings,
753                                         DebugLinesSubsectionRef &Lines) {
754         uint16_t Segment = Lines.header()->RelocSegment;
755         uint32_t Begin = Lines.header()->RelocOffset;
756         uint32_t End = Begin + Lines.header()->CodeSize;
757         for (const auto &Block : Lines) {
758           if (LastModi != Modi || LastNameIndex != Block.NameIndex) {
759             LastModi = Modi;
760             LastNameIndex = Block.NameIndex;
761             Strings.formatFromChecksumsOffset(P, Block.NameIndex);
762           }
763
764           AutoIndent Indent(P, 2);
765           P.formatLine("{0:X-4}:{1:X-8}-{2:X-8}, ", Segment, Begin, End);
766           uint32_t Count = Block.LineNumbers.size();
767           if (Lines.hasColumnInfo())
768             P.format("line/column/addr entries = {0}", Count);
769           else
770             P.format("line/addr entries = {0}", Count);
771
772           P.NewLine();
773           typesetLinesAndColumns(P, Begin, Block);
774         }
775       });
776
777   return Error::success();
778 }
779
780 Error DumpOutputStyle::dumpInlineeLines() {
781   printHeader(P, "Inlinee Lines");
782
783   iterateModuleSubsections<DebugInlineeLinesSubsectionRef>(
784       File, PrintScope{P, 2},
785       [this](uint32_t Modi, const SymbolGroup &Strings,
786              DebugInlineeLinesSubsectionRef &Lines) {
787         P.formatLine("{0,+8} | {1,+5} | {2}", "Inlinee", "Line", "Source File");
788         for (const auto &Entry : Lines) {
789           P.formatLine("{0,+8} | {1,+5} | ", Entry.Header->Inlinee,
790                        fmtle(Entry.Header->SourceLineNum));
791           Strings.formatFromChecksumsOffset(P, Entry.Header->FileID, true);
792         }
793         P.NewLine();
794       });
795
796   return Error::success();
797 }
798
799 Error DumpOutputStyle::dumpXmi() {
800   printHeader(P, "Cross Module Imports");
801   iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>(
802       File, PrintScope{P, 2},
803       [this](uint32_t Modi, const SymbolGroup &Strings,
804              DebugCrossModuleImportsSubsectionRef &Imports) {
805         P.formatLine("{0,=32} | {1}", "Imported Module", "Type IDs");
806
807         for (const auto &Xmi : Imports) {
808           auto ExpectedModule =
809               Strings.getNameFromStringTable(Xmi.Header->ModuleNameOffset);
810           StringRef Module;
811           SmallString<32> ModuleStorage;
812           if (!ExpectedModule) {
813             Module = "(unknown module)";
814             consumeError(ExpectedModule.takeError());
815           } else
816             Module = *ExpectedModule;
817           if (Module.size() > 32) {
818             ModuleStorage = "...";
819             ModuleStorage += Module.take_back(32 - 3);
820             Module = ModuleStorage;
821           }
822           std::vector<std::string> TIs;
823           for (const auto I : Xmi.Imports)
824             TIs.push_back(formatv("{0,+10:X+}", fmtle(I)));
825           std::string Result =
826               typesetItemList(TIs, P.getIndentLevel() + 35, 12, " ");
827           P.formatLine("{0,+32} | {1}", Module, Result);
828         }
829       });
830
831   return Error::success();
832 }
833
834 Error DumpOutputStyle::dumpXme() {
835   printHeader(P, "Cross Module Exports");
836
837   iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>(
838       File, PrintScope{P, 2},
839       [this](uint32_t Modi, const SymbolGroup &Strings,
840              DebugCrossModuleExportsSubsectionRef &Exports) {
841         P.formatLine("{0,-10} | {1}", "Local ID", "Global ID");
842         for (const auto &Export : Exports) {
843           P.formatLine("{0,+10:X+} | {1}", TypeIndex(Export.Local),
844                        TypeIndex(Export.Global));
845         }
846       });
847
848   return Error::success();
849 }
850
851 Error DumpOutputStyle::dumpStringTable() {
852   printHeader(P, "String Table");
853
854   if (File.isObj()) {
855     P.formatLine("Dumping string table is not supported for object files");
856     return Error::success();
857   }
858
859   AutoIndent Indent(P);
860   auto IS = getPdb().getStringTable();
861   if (!IS) {
862     P.formatLine("Not present in file");
863     consumeError(IS.takeError());
864     return Error::success();
865   }
866
867   if (IS->name_ids().empty()) {
868     P.formatLine("Empty");
869     return Error::success();
870   }
871
872   auto MaxID = std::max_element(IS->name_ids().begin(), IS->name_ids().end());
873   uint32_t Digits = NumDigits(*MaxID);
874
875   P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits),
876                "String");
877
878   std::vector<uint32_t> SortedIDs(IS->name_ids().begin(), IS->name_ids().end());
879   std::sort(SortedIDs.begin(), SortedIDs.end());
880   for (uint32_t I : SortedIDs) {
881     auto ES = IS->getStringForID(I);
882     llvm::SmallString<32> Str;
883     if (!ES) {
884       consumeError(ES.takeError());
885       Str = "Error reading string";
886     } else if (!ES->empty()) {
887       Str.append("'");
888       Str.append(*ES);
889       Str.append("'");
890     }
891
892     if (!Str.empty())
893       P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits), Str);
894   }
895   return Error::success();
896 }
897
898 static void buildDepSet(LazyRandomTypeCollection &Types,
899                         ArrayRef<TypeIndex> Indices,
900                         std::map<TypeIndex, CVType> &DepSet) {
901   SmallVector<TypeIndex, 4> DepList;
902   for (const auto &I : Indices) {
903     TypeIndex TI(I);
904     if (DepSet.find(TI) != DepSet.end() || TI.isSimple() || TI.isNoneType())
905       continue;
906
907     CVType Type = Types.getType(TI);
908     DepSet[TI] = Type;
909     codeview::discoverTypeIndices(Type, DepList);
910     buildDepSet(Types, DepList, DepSet);
911   }
912 }
913
914 static void
915 dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types,
916                    uint32_t NumTypeRecords, uint32_t NumHashBuckets,
917                    FixedStreamArray<support::ulittle32_t> HashValues,
918                    bool Bytes, bool Extras) {
919
920   Printer.formatLine("Showing {0:N} records", NumTypeRecords);
921   uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + NumTypeRecords);
922
923   MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types,
924                            NumHashBuckets, HashValues);
925
926   if (auto EC = codeview::visitTypeStream(Types, V)) {
927     Printer.formatLine("An error occurred dumping type records: {0}",
928                        toString(std::move(EC)));
929   }
930 }
931
932 static void dumpPartialTypeStream(LinePrinter &Printer,
933                                   LazyRandomTypeCollection &Types,
934                                   TpiStream &Stream, ArrayRef<TypeIndex> TiList,
935                                   bool Bytes, bool Extras, bool Deps) {
936   uint32_t Width =
937       NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords());
938
939   MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types,
940                            Stream.getNumHashBuckets(), Stream.getHashValues());
941
942   if (opts::dump::DumpTypeDependents) {
943     // If we need to dump all dependents, then iterate each index and find
944     // all dependents, adding them to a map ordered by TypeIndex.
945     std::map<TypeIndex, CVType> DepSet;
946     buildDepSet(Types, TiList, DepSet);
947
948     Printer.formatLine(
949         "Showing {0:N} records and their dependents ({1:N} records total)",
950         TiList.size(), DepSet.size());
951
952     for (auto &Dep : DepSet) {
953       if (auto EC = codeview::visitTypeRecord(Dep.second, Dep.first, V))
954         Printer.formatLine("An error occurred dumping type record {0}: {1}",
955                            Dep.first, toString(std::move(EC)));
956     }
957   } else {
958     Printer.formatLine("Showing {0:N} records.", TiList.size());
959
960     for (const auto &I : TiList) {
961       TypeIndex TI(I);
962       CVType Type = Types.getType(TI);
963       if (auto EC = codeview::visitTypeRecord(Type, TI, V))
964         Printer.formatLine("An error occurred dumping type record {0}: {1}", TI,
965                            toString(std::move(EC)));
966     }
967   }
968 }
969
970 Error DumpOutputStyle::dumpTypesFromObjectFile() {
971   LazyRandomTypeCollection Types(100);
972
973   for (const auto &S : getObj().sections()) {
974     StringRef SectionName;
975     if (auto EC = S.getName(SectionName))
976       return errorCodeToError(EC);
977
978     if (SectionName != ".debug$T")
979       continue;
980     StringRef Contents;
981     if (auto EC = S.getContents(Contents))
982       return errorCodeToError(EC);
983
984     uint32_t Magic;
985     BinaryStreamReader Reader(Contents, llvm::support::little);
986     if (auto EC = Reader.readInteger(Magic))
987       return EC;
988     if (Magic != COFF::DEBUG_SECTION_MAGIC)
989       return make_error<StringError>("Invalid CodeView debug section.",
990                                      inconvertibleErrorCode());
991
992     Types.reset(Reader, 100);
993
994     if (opts::dump::DumpTypes) {
995       dumpFullTypeStream(P, Types, 0, 0, {}, opts::dump::DumpTypeData, false);
996     } else if (opts::dump::DumpTypeExtras) {
997       auto LocalHashes = LocallyHashedType::hashTypeCollection(Types);
998       auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types);
999       assert(LocalHashes.size() == GlobalHashes.size());
1000
1001       P.formatLine("Local / Global hashes:");
1002       TypeIndex TI(TypeIndex::FirstNonSimpleIndex);
1003       for (const auto &H : zip(LocalHashes, GlobalHashes)) {
1004         AutoIndent Indent2(P);
1005         LocallyHashedType &L = std::get<0>(H);
1006         GloballyHashedType &G = std::get<1>(H);
1007
1008         P.formatLine("TI: {0}, LocalHash: {1:X}, GlobalHash: {2}", TI, L, G);
1009
1010         ++TI;
1011       }
1012       P.NewLine();
1013     }
1014   }
1015
1016   return Error::success();
1017 }
1018
1019 Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
1020   assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);
1021
1022   if (StreamIdx == StreamTPI) {
1023     printHeader(P, "Types (TPI Stream)");
1024   } else if (StreamIdx == StreamIPI) {
1025     printHeader(P, "Types (IPI Stream)");
1026   }
1027
1028   AutoIndent Indent(P);
1029   assert(!File.isObj());
1030
1031   bool Present = false;
1032   bool DumpTypes = false;
1033   bool DumpBytes = false;
1034   bool DumpExtras = false;
1035   std::vector<uint32_t> Indices;
1036   if (StreamIdx == StreamTPI) {
1037     Present = getPdb().hasPDBTpiStream();
1038     DumpTypes = opts::dump::DumpTypes;
1039     DumpBytes = opts::dump::DumpTypeData;
1040     DumpExtras = opts::dump::DumpTypeExtras;
1041     Indices.assign(opts::dump::DumpTypeIndex.begin(),
1042                    opts::dump::DumpTypeIndex.end());
1043   } else if (StreamIdx == StreamIPI) {
1044     Present = getPdb().hasPDBIpiStream();
1045     DumpTypes = opts::dump::DumpIds;
1046     DumpBytes = opts::dump::DumpIdData;
1047     DumpExtras = opts::dump::DumpIdExtras;
1048     Indices.assign(opts::dump::DumpIdIndex.begin(),
1049                    opts::dump::DumpIdIndex.end());
1050   }
1051
1052   if (!Present) {
1053     P.formatLine("Stream not present");
1054     return Error::success();
1055   }
1056
1057   ExitOnError Err("Unexpected error processing types: ");
1058
1059   auto &Stream = Err((StreamIdx == StreamTPI) ? getPdb().getPDBTpiStream()
1060                                               : getPdb().getPDBIpiStream());
1061
1062   auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids();
1063
1064   if (DumpTypes || !Indices.empty()) {
1065     if (Indices.empty())
1066       dumpFullTypeStream(P, Types, Stream.getNumTypeRecords(),
1067                          Stream.getNumHashBuckets(), Stream.getHashValues(),
1068                          DumpBytes, DumpExtras);
1069     else {
1070       std::vector<TypeIndex> TiList(Indices.begin(), Indices.end());
1071       dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras,
1072                             opts::dump::DumpTypeDependents);
1073     }
1074   }
1075
1076   if (DumpExtras) {
1077     P.NewLine();
1078     auto IndexOffsets = Stream.getTypeIndexOffsets();
1079     P.formatLine("Type Index Offsets:");
1080     for (const auto &IO : IndexOffsets) {
1081       AutoIndent Indent2(P);
1082       P.formatLine("TI: {0}, Offset: {1}", IO.Type, fmtle(IO.Offset));
1083     }
1084
1085     P.NewLine();
1086     P.formatLine("Hash Adjusters:");
1087     auto &Adjusters = Stream.getHashAdjusters();
1088     auto &Strings = Err(getPdb().getStringTable());
1089     for (const auto &A : Adjusters) {
1090       AutoIndent Indent2(P);
1091       auto ExpectedStr = Strings.getStringForID(A.first);
1092       TypeIndex TI(A.second);
1093       if (ExpectedStr)
1094         P.formatLine("`{0}` -> {1}", *ExpectedStr, TI);
1095       else {
1096         P.formatLine("unknown str id ({0}) -> {1}", A.first, TI);
1097         consumeError(ExpectedStr.takeError());
1098       }
1099     }
1100   }
1101   return Error::success();
1102 }
1103
1104 Error DumpOutputStyle::dumpModuleSymsForObj() {
1105   printHeader(P, "Symbols");
1106
1107   AutoIndent Indent(P);
1108
1109   ExitOnError Err("Unexpected error processing symbols: ");
1110
1111   auto &Types = File.types();
1112
1113   SymbolVisitorCallbackPipeline Pipeline;
1114   SymbolDeserializer Deserializer(nullptr, CodeViewContainer::ObjectFile);
1115   MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types, Types);
1116
1117   Pipeline.addCallbackToPipeline(Deserializer);
1118   Pipeline.addCallbackToPipeline(Dumper);
1119   CVSymbolVisitor Visitor(Pipeline);
1120
1121   std::unique_ptr<llvm::Error> SymbolError;
1122
1123   iterateModuleSubsections<DebugSymbolsSubsectionRef>(
1124       File, PrintScope{P, 2},
1125       [&](uint32_t Modi, const SymbolGroup &Strings,
1126           DebugSymbolsSubsectionRef &Symbols) {
1127         for (auto Symbol : Symbols) {
1128           if (auto EC = Visitor.visitSymbolRecord(Symbol)) {
1129             SymbolError = llvm::make_unique<Error>(std::move(EC));
1130             return;
1131           }
1132         }
1133       });
1134
1135   if (SymbolError)
1136     return std::move(*SymbolError);
1137
1138   return Error::success();
1139 }
1140
1141 Error DumpOutputStyle::dumpModuleSymsForPdb() {
1142   printHeader(P, "Symbols");
1143
1144   AutoIndent Indent(P);
1145   if (!getPdb().hasPDBDbiStream()) {
1146     P.formatLine("DBI Stream not present");
1147     return Error::success();
1148   }
1149
1150   ExitOnError Err("Unexpected error processing symbols: ");
1151
1152   auto &Ids = File.ids();
1153   auto &Types = File.types();
1154
1155   iterateSymbolGroups(
1156       File, PrintScope{P, 2}, [&](uint32_t I, const SymbolGroup &Strings) {
1157         auto ExpectedModS = getModuleDebugStream(File.pdb(), I);
1158         if (!ExpectedModS) {
1159           P.formatLine("Error loading module stream {0}.  {1}", I,
1160                        toString(ExpectedModS.takeError()));
1161           return;
1162         }
1163
1164         ModuleDebugStreamRef &ModS = *ExpectedModS;
1165
1166         SymbolVisitorCallbackPipeline Pipeline;
1167         SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
1168         MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids,
1169                                    Types);
1170
1171         Pipeline.addCallbackToPipeline(Deserializer);
1172         Pipeline.addCallbackToPipeline(Dumper);
1173         CVSymbolVisitor Visitor(Pipeline);
1174         auto SS = ModS.getSymbolsSubstream();
1175         if (auto EC =
1176                 Visitor.visitSymbolStream(ModS.getSymbolArray(), SS.Offset)) {
1177           P.formatLine("Error while processing symbol records.  {0}",
1178                        toString(std::move(EC)));
1179           return;
1180         }
1181       });
1182   return Error::success();
1183 }
1184
1185 Error DumpOutputStyle::dumpGlobals() {
1186   printHeader(P, "Global Symbols");
1187   AutoIndent Indent(P);
1188
1189   if (File.isObj()) {
1190     P.formatLine("Dumping Globals is not supported for object files");
1191     return Error::success();
1192   }
1193
1194   if (!getPdb().hasPDBGlobalsStream()) {
1195     P.formatLine("Globals stream not present");
1196     return Error::success();
1197   }
1198   ExitOnError Err("Error dumping globals stream: ");
1199   auto &Globals = Err(getPdb().getPDBGlobalsStream());
1200
1201   const GSIHashTable &Table = Globals.getGlobalsTable();
1202   Err(dumpSymbolsFromGSI(Table, opts::dump::DumpGlobalExtras));
1203   return Error::success();
1204 }
1205
1206 Error DumpOutputStyle::dumpPublics() {
1207   printHeader(P, "Public Symbols");
1208   AutoIndent Indent(P);
1209
1210   if (File.isObj()) {
1211     P.formatLine("Dumping Globals is not supported for object files");
1212     return Error::success();
1213   }
1214
1215   if (!getPdb().hasPDBPublicsStream()) {
1216     P.formatLine("Publics stream not present");
1217     return Error::success();
1218   }
1219   ExitOnError Err("Error dumping publics stream: ");
1220   auto &Publics = Err(getPdb().getPDBPublicsStream());
1221
1222   const GSIHashTable &PublicsTable = Publics.getPublicsTable();
1223   if (opts::dump::DumpPublicExtras) {
1224     P.printLine("Publics Header");
1225     AutoIndent Indent(P);
1226     P.formatLine("sym hash = {0}, thunk table addr = {1}", Publics.getSymHash(),
1227                  formatSegmentOffset(Publics.getThunkTableSection(),
1228                                      Publics.getThunkTableOffset()));
1229   }
1230   Err(dumpSymbolsFromGSI(PublicsTable, opts::dump::DumpPublicExtras));
1231
1232   // Skip the rest if we aren't dumping extras.
1233   if (!opts::dump::DumpPublicExtras)
1234     return Error::success();
1235
1236   P.formatLine("Address Map");
1237   {
1238     // These are offsets into the publics stream sorted by secidx:secrel.
1239     AutoIndent Indent2(P);
1240     for (uint32_t Addr : Publics.getAddressMap())
1241       P.formatLine("off = {0}", Addr);
1242   }
1243
1244   // The thunk map is optional debug info used for ILT thunks.
1245   if (!Publics.getThunkMap().empty()) {
1246     P.formatLine("Thunk Map");
1247     AutoIndent Indent2(P);
1248     for (uint32_t Addr : Publics.getThunkMap())
1249       P.formatLine("{0:x8}", Addr);
1250   }
1251
1252   // The section offsets table appears to be empty when incremental linking
1253   // isn't in use.
1254   if (!Publics.getSectionOffsets().empty()) {
1255     P.formatLine("Section Offsets");
1256     AutoIndent Indent2(P);
1257     for (const SectionOffset &SO : Publics.getSectionOffsets())
1258       P.formatLine("{0:x4}:{1:x8}", uint16_t(SO.Isect), uint32_t(SO.Off));
1259   }
1260
1261   return Error::success();
1262 }
1263
1264 Error DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable &Table,
1265                                           bool HashExtras) {
1266   auto ExpectedSyms = getPdb().getPDBSymbolStream();
1267   if (!ExpectedSyms)
1268     return ExpectedSyms.takeError();
1269   auto &Types = File.types();
1270   auto &Ids = File.ids();
1271
1272   if (HashExtras) {
1273     P.printLine("GSI Header");
1274     AutoIndent Indent(P);
1275     P.formatLine("sig = {0:X}, hdr = {1:X}, hr size = {2}, num buckets = {3}",
1276                  Table.getVerSignature(), Table.getVerHeader(),
1277                  Table.getHashRecordSize(), Table.getNumBuckets());
1278   }
1279
1280   {
1281     P.printLine("Records");
1282     SymbolVisitorCallbackPipeline Pipeline;
1283     SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
1284     MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);
1285
1286     Pipeline.addCallbackToPipeline(Deserializer);
1287     Pipeline.addCallbackToPipeline(Dumper);
1288     CVSymbolVisitor Visitor(Pipeline);
1289
1290     BinaryStreamRef SymStream =
1291         ExpectedSyms->getSymbolArray().getUnderlyingStream();
1292     for (uint32_t PubSymOff : Table) {
1293       Expected<CVSymbol> Sym = readSymbolFromStream(SymStream, PubSymOff);
1294       if (!Sym)
1295         return Sym.takeError();
1296       if (auto E = Visitor.visitSymbolRecord(*Sym, PubSymOff))
1297         return E;
1298     }
1299   }
1300
1301   // Return early if we aren't dumping public hash table and address map info.
1302   if (!HashExtras)
1303     return Error::success();
1304
1305   P.formatLine("Hash Entries");
1306   {
1307     AutoIndent Indent2(P);
1308     for (const PSHashRecord &HR : Table.HashRecords)
1309       P.formatLine("off = {0}, refcnt = {1}", uint32_t(HR.Off),
1310                    uint32_t(HR.CRef));
1311   }
1312
1313   // FIXME: Dump the bitmap.
1314
1315   P.formatLine("Hash Buckets");
1316   {
1317     AutoIndent Indent2(P);
1318     for (uint32_t Hash : Table.HashBuckets)
1319       P.formatLine("{0:x8}", Hash);
1320   }
1321
1322   return Error::success();
1323 }
1324
1325 static std::string formatSegMapDescriptorFlag(uint32_t IndentLevel,
1326                                               OMFSegDescFlags Flags) {
1327   std::vector<std::string> Opts;
1328   if (Flags == OMFSegDescFlags::None)
1329     return "none";
1330
1331   PUSH_FLAG(OMFSegDescFlags, Read, Flags, "read");
1332   PUSH_FLAG(OMFSegDescFlags, Write, Flags, "write");
1333   PUSH_FLAG(OMFSegDescFlags, Execute, Flags, "execute");
1334   PUSH_FLAG(OMFSegDescFlags, AddressIs32Bit, Flags, "32 bit addr");
1335   PUSH_FLAG(OMFSegDescFlags, IsSelector, Flags, "selector");
1336   PUSH_FLAG(OMFSegDescFlags, IsAbsoluteAddress, Flags, "absolute addr");
1337   PUSH_FLAG(OMFSegDescFlags, IsGroup, Flags, "group");
1338   return typesetItemList(Opts, IndentLevel, 4, " | ");
1339 }
1340
1341 Error DumpOutputStyle::dumpSectionHeaders() {
1342   dumpSectionHeaders("Section Headers", DbgHeaderType::SectionHdr);
1343   dumpSectionHeaders("Original Section Headers", DbgHeaderType::SectionHdrOrig);
1344   return Error::success();
1345 }
1346
1347 static Expected<std::pair<std::unique_ptr<MappedBlockStream>,
1348                           ArrayRef<llvm::object::coff_section>>>
1349 loadSectionHeaders(PDBFile &File, DbgHeaderType Type) {
1350   if (!File.hasPDBDbiStream())
1351     return make_error<StringError>(
1352         "Section headers require a DBI Stream, which could not be loaded",
1353         inconvertibleErrorCode());
1354
1355   auto &Dbi = cantFail(File.getPDBDbiStream());
1356   uint32_t SI = Dbi.getDebugStreamIndex(Type);
1357
1358   if (SI == kInvalidStreamIndex)
1359     return make_error<StringError>(
1360         "PDB does not contain the requested image section header type",
1361         inconvertibleErrorCode());
1362
1363   auto Stream = File.createIndexedStream(SI);
1364   if (!Stream)
1365     return make_error<StringError>("Could not load the required stream data",
1366                                    inconvertibleErrorCode());
1367
1368   ArrayRef<object::coff_section> Headers;
1369   if (Stream->getLength() % sizeof(object::coff_section) != 0)
1370     return make_error<StringError>(
1371         "Section header array size is not a multiple of section header size",
1372         inconvertibleErrorCode());
1373
1374   uint32_t NumHeaders = Stream->getLength() / sizeof(object::coff_section);
1375   BinaryStreamReader Reader(*Stream);
1376   cantFail(Reader.readArray(Headers, NumHeaders));
1377   return std::make_pair(std::move(Stream), Headers);
1378 }
1379
1380 void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) {
1381   printHeader(P, Label);
1382
1383   AutoIndent Indent(P);
1384   if (File.isObj()) {
1385     P.formatLine("Dumping Section Headers is not supported for object files");
1386     return;
1387   }
1388
1389   ExitOnError Err("Error dumping section headers: ");
1390   std::unique_ptr<MappedBlockStream> Stream;
1391   ArrayRef<object::coff_section> Headers;
1392   auto ExpectedHeaders = loadSectionHeaders(getPdb(), Type);
1393   if (!ExpectedHeaders) {
1394     P.printLine(toString(ExpectedHeaders.takeError()));
1395     return;
1396   }
1397   std::tie(Stream, Headers) = std::move(*ExpectedHeaders);
1398
1399   uint32_t I = 1;
1400   for (const auto &Header : Headers) {
1401     P.NewLine();
1402     P.formatLine("SECTION HEADER #{0}", I);
1403     P.formatLine("{0,8} name", Header.Name);
1404     P.formatLine("{0,8:X-} virtual size", uint32_t(Header.VirtualSize));
1405     P.formatLine("{0,8:X-} virtual address", uint32_t(Header.VirtualAddress));
1406     P.formatLine("{0,8:X-} size of raw data", uint32_t(Header.SizeOfRawData));
1407     P.formatLine("{0,8:X-} file pointer to raw data",
1408                  uint32_t(Header.PointerToRawData));
1409     P.formatLine("{0,8:X-} file pointer to relocation table",
1410                  uint32_t(Header.PointerToRelocations));
1411     P.formatLine("{0,8:X-} file pointer to line numbers",
1412                  uint32_t(Header.PointerToLinenumbers));
1413     P.formatLine("{0,8:X-} number of relocations",
1414                  uint32_t(Header.NumberOfRelocations));
1415     P.formatLine("{0,8:X-} number of line numbers",
1416                  uint32_t(Header.NumberOfLinenumbers));
1417     P.formatLine("{0,8:X-} flags", uint32_t(Header.Characteristics));
1418     AutoIndent IndentMore(P, 9);
1419     P.formatLine("{0}", formatSectionCharacteristics(
1420                             P.getIndentLevel(), Header.Characteristics, 1, ""));
1421     ++I;
1422   }
1423   return;
1424 }
1425
1426 std::vector<std::string> getSectionNames(PDBFile &File) {
1427   auto ExpectedHeaders = loadSectionHeaders(File, DbgHeaderType::SectionHdr);
1428   if (!ExpectedHeaders)
1429     return {};
1430
1431   std::unique_ptr<MappedBlockStream> Stream;
1432   ArrayRef<object::coff_section> Headers;
1433   std::tie(Stream, Headers) = std::move(*ExpectedHeaders);
1434   std::vector<std::string> Names;
1435   for (const auto &H : Headers)
1436     Names.push_back(H.Name);
1437   return Names;
1438 }
1439
1440 Error DumpOutputStyle::dumpSectionContribs() {
1441   printHeader(P, "Section Contributions");
1442
1443   AutoIndent Indent(P);
1444   if (File.isObj()) {
1445     P.formatLine(
1446         "Dumping section contributions is not supported for object files");
1447     return Error::success();
1448   }
1449
1450   ExitOnError Err("Error dumping section contributions: ");
1451   if (!getPdb().hasPDBDbiStream()) {
1452     P.formatLine(
1453         "Section contribs require a DBI Stream, which could not be loaded");
1454     return Error::success();
1455   }
1456
1457   auto &Dbi = Err(getPdb().getPDBDbiStream());
1458
1459   class Visitor : public ISectionContribVisitor {
1460   public:
1461     Visitor(LinePrinter &P, ArrayRef<std::string> Names) : P(P), Names(Names) {
1462       auto Max = std::max_element(
1463           Names.begin(), Names.end(),
1464           [](StringRef S1, StringRef S2) { return S1.size() < S2.size(); });
1465       MaxNameLen = (Max == Names.end() ? 0 : Max->size());
1466     }
1467     void visit(const SectionContrib &SC) override {
1468       assert(SC.ISect > 0);
1469       std::string NameInsert;
1470       if (SC.ISect < Names.size()) {
1471         StringRef SectionName = Names[SC.ISect - 1];
1472         NameInsert = formatv("[{0}]", SectionName).str();
1473       } else
1474         NameInsert = "[???]";
1475       P.formatLine("SC{5}  | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "
1476                    "crc = {4}",
1477                    formatSegmentOffset(SC.ISect, SC.Off), fmtle(SC.Size),
1478                    fmtle(SC.Imod), fmtle(SC.DataCrc), fmtle(SC.RelocCrc),
1479                    fmt_align(NameInsert, AlignStyle::Left, MaxNameLen + 2));
1480       AutoIndent Indent(P, MaxNameLen + 2);
1481       P.formatLine("      {0}",
1482                    formatSectionCharacteristics(P.getIndentLevel() + 6,
1483                                                 SC.Characteristics, 3, " | "));
1484     }
1485     void visit(const SectionContrib2 &SC) override {
1486       P.formatLine(
1487           "SC2[{6}] | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "
1488           "crc = {4}, coff section = {5}",
1489           formatSegmentOffset(SC.Base.ISect, SC.Base.Off), fmtle(SC.Base.Size),
1490           fmtle(SC.Base.Imod), fmtle(SC.Base.DataCrc), fmtle(SC.Base.RelocCrc),
1491           fmtle(SC.ISectCoff));
1492       P.formatLine("      {0}", formatSectionCharacteristics(
1493                                     P.getIndentLevel() + 6,
1494                                     SC.Base.Characteristics, 3, " | "));
1495     }
1496
1497   private:
1498     LinePrinter &P;
1499     uint32_t MaxNameLen;
1500     ArrayRef<std::string> Names;
1501   };
1502
1503   std::vector<std::string> Names = getSectionNames(getPdb());
1504   Visitor V(P, makeArrayRef(Names));
1505   Dbi.visitSectionContributions(V);
1506   return Error::success();
1507 }
1508
1509 Error DumpOutputStyle::dumpSectionMap() {
1510   printHeader(P, "Section Map");
1511   AutoIndent Indent(P);
1512
1513   if (File.isObj()) {
1514     P.formatLine("Dumping section map is not supported for object files");
1515     return Error::success();
1516   }
1517
1518   ExitOnError Err("Error dumping section map: ");
1519
1520   if (!getPdb().hasPDBDbiStream()) {
1521     P.formatLine("Dumping the section map requires a DBI Stream, which could "
1522                  "not be loaded");
1523     return Error::success();
1524   }
1525
1526   auto &Dbi = Err(getPdb().getPDBDbiStream());
1527
1528   uint32_t I = 0;
1529   for (auto &M : Dbi.getSectionMap()) {
1530     P.formatLine(
1531         "Section {0:4} | ovl = {1}, group = {2}, frame = {3}, name = {4}", I,
1532         fmtle(M.Ovl), fmtle(M.Group), fmtle(M.Frame), fmtle(M.SecName));
1533     P.formatLine("               class = {0}, offset = {1}, size = {2}",
1534                  fmtle(M.ClassName), fmtle(M.Offset), fmtle(M.SecByteLength));
1535     P.formatLine("               flags = {0}",
1536                  formatSegMapDescriptorFlag(
1537                      P.getIndentLevel() + 13,
1538                      static_cast<OMFSegDescFlags>(uint16_t(M.Flags))));
1539     ++I;
1540   }
1541   return Error::success();
1542 }