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