OSDN Git Service

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