]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r308421, and update
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / llvm-pdbutil / DumpOutputStyle.cpp
1 //===- DumpOutputStyle.cpp ------------------------------------ *- C++ --*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "DumpOutputStyle.h"
11
12 #include "FormatUtil.h"
13 #include "MinimalSymbolDumper.h"
14 #include "MinimalTypeDumper.h"
15 #include "StreamUtil.h"
16 #include "llvm-pdbutil.h"
17
18 #include "llvm/ADT/STLExtras.h"
19 #include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h"
20 #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
21 #include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h"
22 #include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h"
23 #include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h"
24 #include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.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/DebugSubsectionVisitor.h"
29 #include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h"
30 #include "llvm/DebugInfo/CodeView/DebugUnknownSubsection.h"
31 #include "llvm/DebugInfo/CodeView/EnumTables.h"
32 #include "llvm/DebugInfo/CodeView/Formatters.h"
33 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
34 #include "llvm/DebugInfo/CodeView/Line.h"
35 #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
36 #include "llvm/DebugInfo/CodeView/SymbolDumper.h"
37 #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h"
38 #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h"
39 #include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
40 #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
41 #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
42 #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
43 #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"
44 #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
45 #include "llvm/DebugInfo/PDB/Native/EnumTables.h"
46 #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
47 #include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h"
48 #include "llvm/DebugInfo/PDB/Native/InfoStream.h"
49 #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
50 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
51 #include "llvm/DebugInfo/PDB/Native/PublicsStream.h"
52 #include "llvm/DebugInfo/PDB/Native/RawError.h"
53 #include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
54 #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
55 #include "llvm/DebugInfo/PDB/PDBExtras.h"
56 #include "llvm/Object/COFF.h"
57 #include "llvm/Support/BinaryStreamReader.h"
58 #include "llvm/Support/FormatAdapters.h"
59 #include "llvm/Support/FormatVariadic.h"
60
61 #include <unordered_map>
62
63 using namespace llvm;
64 using namespace llvm::codeview;
65 using namespace llvm::msf;
66 using namespace llvm::pdb;
67
68 DumpOutputStyle::DumpOutputStyle(PDBFile &File)
69     : File(File), P(2, false, outs()) {}
70
71 Error DumpOutputStyle::dump() {
72   if (opts::dump::DumpSummary) {
73     if (auto EC = dumpFileSummary())
74       return EC;
75     P.NewLine();
76   }
77
78   if (opts::dump::DumpStreams) {
79     if (auto EC = dumpStreamSummary())
80       return EC;
81     P.NewLine();
82   }
83
84   if (opts::dump::DumpStringTable) {
85     if (auto EC = dumpStringTable())
86       return EC;
87     P.NewLine();
88   }
89
90   if (opts::dump::DumpModules) {
91     if (auto EC = dumpModules())
92       return EC;
93   }
94
95   if (opts::dump::DumpModuleFiles) {
96     if (auto EC = dumpModuleFiles())
97       return EC;
98   }
99
100   if (opts::dump::DumpLines) {
101     if (auto EC = dumpLines())
102       return EC;
103   }
104
105   if (opts::dump::DumpInlineeLines) {
106     if (auto EC = dumpInlineeLines())
107       return EC;
108   }
109
110   if (opts::dump::DumpXmi) {
111     if (auto EC = dumpXmi())
112       return EC;
113   }
114
115   if (opts::dump::DumpXme) {
116     if (auto EC = dumpXme())
117       return EC;
118   }
119
120   if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() ||
121       opts::dump::DumpTypeExtras) {
122     if (auto EC = dumpTpiStream(StreamTPI))
123       return EC;
124   }
125
126   if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() ||
127       opts::dump::DumpIdExtras) {
128     if (auto EC = dumpTpiStream(StreamIPI))
129       return EC;
130   }
131
132   if (opts::dump::DumpPublics) {
133     if (auto EC = dumpPublics())
134       return EC;
135   }
136
137   if (opts::dump::DumpSymbols) {
138     if (auto EC = dumpModuleSyms())
139       return EC;
140   }
141
142   if (opts::dump::DumpSectionContribs) {
143     if (auto EC = dumpSectionContribs())
144       return EC;
145   }
146
147   if (opts::dump::DumpSectionMap) {
148     if (auto EC = dumpSectionMap())
149       return EC;
150   }
151
152   return Error::success();
153 }
154
155 static void printHeader(LinePrinter &P, const Twine &S) {
156   P.NewLine();
157   P.formatLine("{0,=60}", S);
158   P.formatLine("{0}", fmt_repeat('=', 60));
159 }
160
161 Error DumpOutputStyle::dumpFileSummary() {
162   printHeader(P, "Summary");
163
164   ExitOnError Err("Invalid PDB Format");
165
166   AutoIndent Indent(P);
167   P.formatLine("Block Size: {0}", File.getBlockSize());
168   P.formatLine("Number of blocks: {0}", File.getBlockCount());
169   P.formatLine("Number of streams: {0}", File.getNumStreams());
170
171   auto &PS = Err(File.getPDBInfoStream());
172   P.formatLine("Signature: {0}", PS.getSignature());
173   P.formatLine("Age: {0}", PS.getAge());
174   P.formatLine("GUID: {0}", fmt_guid(PS.getGuid().Guid));
175   P.formatLine("Features: {0:x+}", static_cast<uint32_t>(PS.getFeatures()));
176   P.formatLine("Has Debug Info: {0}", File.hasPDBDbiStream());
177   P.formatLine("Has Types: {0}", File.hasPDBTpiStream());
178   P.formatLine("Has IDs: {0}", File.hasPDBIpiStream());
179   P.formatLine("Has Globals: {0}", File.hasPDBGlobalsStream());
180   P.formatLine("Has Publics: {0}", File.hasPDBPublicsStream());
181   if (File.hasPDBDbiStream()) {
182     auto &DBI = Err(File.getPDBDbiStream());
183     P.formatLine("Is incrementally linked: {0}", DBI.isIncrementallyLinked());
184     P.formatLine("Has conflicting types: {0}", DBI.hasCTypes());
185     P.formatLine("Is stripped: {0}", DBI.isStripped());
186   }
187
188   return Error::success();
189 }
190
191 Error DumpOutputStyle::dumpStreamSummary() {
192   printHeader(P, "Streams");
193
194   if (StreamPurposes.empty())
195     discoverStreamPurposes(File, StreamPurposes);
196
197   AutoIndent Indent(P);
198   uint32_t StreamCount = File.getNumStreams();
199
200   for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {
201     P.formatLine(
202         "Stream {0}: [{1}] ({2} bytes)",
203         fmt_align(StreamIdx, AlignStyle::Right, NumDigits(StreamCount)),
204         StreamPurposes[StreamIdx], File.getStreamByteSize(StreamIdx));
205     if (opts::dump::DumpStreamBlocks) {
206       auto Blocks = File.getStreamBlockList(StreamIdx);
207       std::vector<uint32_t> BV(Blocks.begin(), Blocks.end());
208       P.formatLine("       {0}  Blocks: [{1}]",
209                    fmt_repeat(' ', NumDigits(StreamCount)),
210                    make_range(BV.begin(), BV.end()));
211     }
212   }
213
214   return Error::success();
215 }
216
217 static Expected<ModuleDebugStreamRef> getModuleDebugStream(PDBFile &File,
218                                                            uint32_t Index) {
219   ExitOnError Err("Unexpected error");
220
221   auto &Dbi = Err(File.getPDBDbiStream());
222   const auto &Modules = Dbi.modules();
223   auto Modi = Modules.getModuleDescriptor(Index);
224
225   uint16_t ModiStream = Modi.getModuleStreamIndex();
226   if (ModiStream == kInvalidStreamIndex)
227     return make_error<RawError>(raw_error_code::no_stream,
228                                 "Module stream not present");
229
230   auto ModStreamData = MappedBlockStream::createIndexedStream(
231       File.getMsfLayout(), File.getMsfBuffer(), ModiStream,
232       File.getAllocator());
233
234   ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData));
235   if (auto EC = ModS.reload())
236     return make_error<RawError>(raw_error_code::corrupt_file,
237                                 "Invalid module stream");
238
239   return std::move(ModS);
240 }
241
242 static std::string formatChecksumKind(FileChecksumKind Kind) {
243   switch (Kind) {
244     RETURN_CASE(FileChecksumKind, None, "None");
245     RETURN_CASE(FileChecksumKind, MD5, "MD5");
246     RETURN_CASE(FileChecksumKind, SHA1, "SHA-1");
247     RETURN_CASE(FileChecksumKind, SHA256, "SHA-256");
248   }
249   return formatUnknownEnum(Kind);
250 }
251
252 namespace {
253 class StringsAndChecksumsPrinter {
254   const DebugStringTableSubsectionRef &extractStringTable(PDBFile &File) {
255     ExitOnError Err("Unexpected error processing modules");
256     return Err(File.getStringTable()).getStringTable();
257   }
258
259   template <typename... Args>
260   void formatInternal(LinePrinter &Printer, bool Append,
261                       Args &&... args) const {
262     if (Append)
263       Printer.format(std::forward<Args>(args)...);
264     else
265       Printer.formatLine(std::forward<Args>(args)...);
266   }
267
268 public:
269   StringsAndChecksumsPrinter(PDBFile &File, uint32_t Modi)
270       : Records(extractStringTable(File)) {
271     auto MDS = getModuleDebugStream(File, Modi);
272     if (!MDS) {
273       consumeError(MDS.takeError());
274       return;
275     }
276
277     DebugStream = llvm::make_unique<ModuleDebugStreamRef>(std::move(*MDS));
278     Records.initialize(MDS->subsections());
279     if (Records.hasChecksums()) {
280       for (const auto &Entry : Records.checksums()) {
281         auto S = Records.strings().getString(Entry.FileNameOffset);
282         if (!S)
283           continue;
284         ChecksumsByFile[*S] = Entry;
285       }
286     }
287   }
288
289   Expected<StringRef> getNameFromStringTable(uint32_t Offset) const {
290     return Records.strings().getString(Offset);
291   }
292
293   void formatFromFileName(LinePrinter &Printer, StringRef File,
294                           bool Append = false) const {
295     auto FC = ChecksumsByFile.find(File);
296     if (FC == ChecksumsByFile.end()) {
297       formatInternal(Printer, Append, "- (no checksum) {0}", File);
298       return;
299     }
300
301     formatInternal(Printer, Append, "- ({0}: {1}) {2}",
302                    formatChecksumKind(FC->getValue().Kind),
303                    toHex(FC->getValue().Checksum), File);
304   }
305
306   void formatFromChecksumsOffset(LinePrinter &Printer, uint32_t Offset,
307                                  bool Append = false) const {
308     if (!Records.hasChecksums()) {
309       formatInternal(Printer, Append, "(unknown file name offset {0})", Offset);
310       return;
311     }
312
313     auto Iter = Records.checksums().getArray().at(Offset);
314     if (Iter == Records.checksums().getArray().end()) {
315       formatInternal(Printer, Append, "(unknown file name offset {0})", Offset);
316       return;
317     }
318
319     uint32_t FO = Iter->FileNameOffset;
320     auto ExpectedFile = getNameFromStringTable(FO);
321     if (!ExpectedFile) {
322       formatInternal(Printer, Append, "(unknown file name offset {0})", Offset);
323       consumeError(ExpectedFile.takeError());
324       return;
325     }
326     if (Iter->Kind == FileChecksumKind::None) {
327       formatInternal(Printer, Append, "{0} (no checksum)", *ExpectedFile);
328     } else {
329       formatInternal(Printer, Append, "{0} ({1}: {2})", *ExpectedFile,
330                      formatChecksumKind(Iter->Kind), toHex(Iter->Checksum));
331     }
332   }
333
334   std::unique_ptr<ModuleDebugStreamRef> DebugStream;
335   StringsAndChecksumsRef Records;
336   StringMap<FileChecksumEntry> ChecksumsByFile;
337 };
338 } // namespace
339
340 template <typename CallbackT>
341 static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel,
342                            CallbackT Callback) {
343   AutoIndent Indent(P);
344   if (!File.hasPDBDbiStream()) {
345     P.formatLine("DBI Stream not present");
346     return;
347   }
348
349   ExitOnError Err("Unexpected error processing modules");
350
351   auto &Stream = Err(File.getPDBDbiStream());
352
353   const DbiModuleList &Modules = Stream.modules();
354   uint32_t Count = Modules.getModuleCount();
355   uint32_t Digits = NumDigits(Count);
356   for (uint32_t I = 0; I < Count; ++I) {
357     auto Modi = Modules.getModuleDescriptor(I);
358     P.formatLine("Mod {0:4} | `{1}`: ", fmt_align(I, AlignStyle::Right, Digits),
359                  Modi.getModuleName());
360
361     StringsAndChecksumsPrinter Strings(File, I);
362     AutoIndent Indent2(P, IndentLevel);
363     Callback(I, Strings);
364   }
365 }
366
367 template <typename SubsectionT>
368 static void iterateModuleSubsections(
369     PDBFile &File, LinePrinter &P, uint32_t IndentLevel,
370     llvm::function_ref<void(uint32_t, StringsAndChecksumsPrinter &,
371                             SubsectionT &)>
372         Callback) {
373
374   iterateModules(
375       File, P, IndentLevel,
376       [&File, &Callback](uint32_t Modi, StringsAndChecksumsPrinter &Strings) {
377         auto MDS = getModuleDebugStream(File, Modi);
378         if (!MDS) {
379           consumeError(MDS.takeError());
380           return;
381         }
382
383         for (const auto &SS : MDS->subsections()) {
384           SubsectionT Subsection;
385
386           if (SS.kind() != Subsection.kind())
387             continue;
388
389           BinaryStreamReader Reader(SS.getRecordData());
390           if (auto EC = Subsection.initialize(Reader))
391             continue;
392           Callback(Modi, Strings, Subsection);
393         }
394       });
395 }
396
397 Error DumpOutputStyle::dumpModules() {
398   printHeader(P, "Modules");
399
400   AutoIndent Indent(P);
401   if (!File.hasPDBDbiStream()) {
402     P.formatLine("DBI Stream not present");
403     return Error::success();
404   }
405
406   ExitOnError Err("Unexpected error processing modules");
407
408   auto &Stream = Err(File.getPDBDbiStream());
409
410   const DbiModuleList &Modules = Stream.modules();
411   uint32_t Count = Modules.getModuleCount();
412   uint32_t Digits = NumDigits(Count);
413   for (uint32_t I = 0; I < Count; ++I) {
414     auto Modi = Modules.getModuleDescriptor(I);
415     P.formatLine("Mod {0:4} | Name: `{1}`: ",
416                  fmt_align(I, AlignStyle::Right, Digits), Modi.getModuleName());
417     P.formatLine("           Obj: `{0}`: ", Modi.getObjFileName());
418     P.formatLine("           debug stream: {0}, # files: {1}, has ec info: {2}",
419                  Modi.getModuleStreamIndex(), Modi.getNumberOfFiles(),
420                  Modi.hasECInfo());
421     StringRef PdbFilePath =
422         Err(Stream.getECName(Modi.getPdbFilePathNameIndex()));
423     StringRef SrcFilePath =
424         Err(Stream.getECName(Modi.getSourceFileNameIndex()));
425     P.formatLine("           pdb file ni: {0} `{1}`, src file ni: {2} `{3}`",
426                  Modi.getPdbFilePathNameIndex(), PdbFilePath,
427                  Modi.getSourceFileNameIndex(), SrcFilePath);
428   }
429   return Error::success();
430 }
431
432 Error DumpOutputStyle::dumpModuleFiles() {
433   printHeader(P, "Files");
434
435   ExitOnError Err("Unexpected error processing modules");
436
437   iterateModules(
438       File, P, 11,
439       [this, &Err](uint32_t Modi, StringsAndChecksumsPrinter &Strings) {
440         auto &Stream = Err(File.getPDBDbiStream());
441
442         const DbiModuleList &Modules = Stream.modules();
443         for (const auto &F : Modules.source_files(Modi)) {
444           Strings.formatFromFileName(P, F);
445         }
446       });
447   return Error::success();
448 }
449
450 static void typesetLinesAndColumns(PDBFile &File, LinePrinter &P,
451                                    uint32_t Start, const LineColumnEntry &E) {
452   const uint32_t kMaxCharsPerLineNumber = 4; // 4 digit line number
453   uint32_t MinColumnWidth = kMaxCharsPerLineNumber + 5;
454
455   // Let's try to keep it under 100 characters
456   constexpr uint32_t kMaxRowLength = 100;
457   // At least 3 spaces between columns.
458   uint32_t ColumnsPerRow = kMaxRowLength / (MinColumnWidth + 3);
459   uint32_t ItemsLeft = E.LineNumbers.size();
460   auto LineIter = E.LineNumbers.begin();
461   while (ItemsLeft != 0) {
462     uint32_t RowColumns = std::min(ItemsLeft, ColumnsPerRow);
463     for (uint32_t I = 0; I < RowColumns; ++I) {
464       LineInfo Line(LineIter->Flags);
465       std::string LineStr;
466       if (Line.isAlwaysStepInto())
467         LineStr = "ASI";
468       else if (Line.isNeverStepInto())
469         LineStr = "NSI";
470       else
471         LineStr = utostr(Line.getStartLine());
472       char Statement = Line.isStatement() ? ' ' : '!';
473       P.format("{0} {1:X-} {2} ",
474                fmt_align(LineStr, AlignStyle::Right, kMaxCharsPerLineNumber),
475                fmt_align(Start + LineIter->Offset, AlignStyle::Right, 8, '0'),
476                Statement);
477       ++LineIter;
478       --ItemsLeft;
479     }
480     P.NewLine();
481   }
482 }
483
484 Error DumpOutputStyle::dumpLines() {
485   printHeader(P, "Lines");
486
487   uint32_t LastModi = UINT32_MAX;
488   uint32_t LastNameIndex = UINT32_MAX;
489   iterateModuleSubsections<DebugLinesSubsectionRef>(
490       File, P, 4,
491       [this, &LastModi, &LastNameIndex](uint32_t Modi,
492                                         StringsAndChecksumsPrinter &Strings,
493                                         DebugLinesSubsectionRef &Lines) {
494         uint16_t Segment = Lines.header()->RelocSegment;
495         uint32_t Begin = Lines.header()->RelocOffset;
496         uint32_t End = Begin + Lines.header()->CodeSize;
497         for (const auto &Block : Lines) {
498           if (LastModi != Modi || LastNameIndex != Block.NameIndex) {
499             LastModi = Modi;
500             LastNameIndex = Block.NameIndex;
501             Strings.formatFromChecksumsOffset(P, Block.NameIndex);
502           }
503
504           AutoIndent Indent(P, 2);
505           P.formatLine("{0:X-4}:{1:X-8}-{2:X-8}, ", Segment, Begin, End);
506           uint32_t Count = Block.LineNumbers.size();
507           if (Lines.hasColumnInfo())
508             P.format("line/column/addr entries = {0}", Count);
509           else
510             P.format("line/addr entries = {0}", Count);
511
512           P.NewLine();
513           typesetLinesAndColumns(File, P, Begin, Block);
514         }
515       });
516
517   return Error::success();
518 }
519
520 Error DumpOutputStyle::dumpInlineeLines() {
521   printHeader(P, "Inlinee Lines");
522
523   iterateModuleSubsections<DebugInlineeLinesSubsectionRef>(
524       File, P, 2,
525       [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings,
526              DebugInlineeLinesSubsectionRef &Lines) {
527         P.formatLine("{0,+8} | {1,+5} | {2}", "Inlinee", "Line", "Source File");
528         for (const auto &Entry : Lines) {
529           P.formatLine("{0,+8} | {1,+5} | ", Entry.Header->Inlinee,
530                        fmtle(Entry.Header->SourceLineNum));
531           Strings.formatFromChecksumsOffset(P, Entry.Header->FileID, true);
532         }
533         P.NewLine();
534       });
535
536   return Error::success();
537 }
538
539 Error DumpOutputStyle::dumpXmi() {
540   printHeader(P, "Cross Module Imports");
541   iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>(
542       File, P, 2,
543       [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings,
544              DebugCrossModuleImportsSubsectionRef &Imports) {
545         P.formatLine("{0,=32} | {1}", "Imported Module", "Type IDs");
546
547         for (const auto &Xmi : Imports) {
548           auto ExpectedModule =
549               Strings.getNameFromStringTable(Xmi.Header->ModuleNameOffset);
550           StringRef Module;
551           SmallString<32> ModuleStorage;
552           if (!ExpectedModule) {
553             Module = "(unknown module)";
554             consumeError(ExpectedModule.takeError());
555           } else
556             Module = *ExpectedModule;
557           if (Module.size() > 32) {
558             ModuleStorage = "...";
559             ModuleStorage += Module.take_back(32 - 3);
560             Module = ModuleStorage;
561           }
562           std::vector<std::string> TIs;
563           for (const auto I : Xmi.Imports)
564             TIs.push_back(formatv("{0,+10:X+}", fmtle(I)));
565           std::string Result =
566               typesetItemList(TIs, P.getIndentLevel() + 35, 12, " ");
567           P.formatLine("{0,+32} | {1}", Module, Result);
568         }
569       });
570
571   return Error::success();
572 }
573
574 Error DumpOutputStyle::dumpXme() {
575   printHeader(P, "Cross Module Exports");
576
577   iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>(
578       File, P, 2,
579       [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings,
580              DebugCrossModuleExportsSubsectionRef &Exports) {
581         P.formatLine("{0,-10} | {1}", "Local ID", "Global ID");
582         for (const auto &Export : Exports) {
583           P.formatLine("{0,+10:X+} | {1}", TypeIndex(Export.Local),
584                        TypeIndex(Export.Global));
585         }
586       });
587
588   return Error::success();
589 }
590
591 Error DumpOutputStyle::dumpStringTable() {
592   printHeader(P, "String Table");
593
594   AutoIndent Indent(P);
595   auto IS = File.getStringTable();
596   if (!IS) {
597     P.formatLine("Not present in file");
598     consumeError(IS.takeError());
599     return Error::success();
600   }
601
602   if (IS->name_ids().empty()) {
603     P.formatLine("Empty");
604     return Error::success();
605   }
606
607   auto MaxID = std::max_element(IS->name_ids().begin(), IS->name_ids().end());
608   uint32_t Digits = NumDigits(*MaxID);
609
610   P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits),
611                "String");
612
613   std::vector<uint32_t> SortedIDs(IS->name_ids().begin(), IS->name_ids().end());
614   std::sort(SortedIDs.begin(), SortedIDs.end());
615   for (uint32_t I : SortedIDs) {
616     auto ES = IS->getStringForID(I);
617     llvm::SmallString<32> Str;
618     if (!ES) {
619       consumeError(ES.takeError());
620       Str = "Error reading string";
621     } else if (!ES->empty()) {
622       Str.append("'");
623       Str.append(*ES);
624       Str.append("'");
625     }
626
627     if (!Str.empty())
628       P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits), Str);
629   }
630   return Error::success();
631 }
632
633 static void buildDepSet(LazyRandomTypeCollection &Types,
634                         ArrayRef<TypeIndex> Indices,
635                         std::map<TypeIndex, CVType> &DepSet) {
636   SmallVector<TypeIndex, 4> DepList;
637   for (const auto &I : Indices) {
638     TypeIndex TI(I);
639     if (DepSet.find(TI) != DepSet.end() || TI.isSimple() || TI.isNoneType())
640       continue;
641
642     CVType Type = Types.getType(TI);
643     DepSet[TI] = Type;
644     codeview::discoverTypeIndices(Type, DepList);
645     buildDepSet(Types, DepList, DepSet);
646   }
647 }
648
649 static void dumpFullTypeStream(LinePrinter &Printer,
650                                LazyRandomTypeCollection &Types,
651                                TpiStream &Stream, bool Bytes, bool Extras) {
652   Printer.formatLine("Showing {0:N} records", Stream.getNumTypeRecords());
653   uint32_t Width =
654       NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords());
655
656   MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types,
657                            Stream.getNumHashBuckets(), Stream.getHashValues());
658
659   if (auto EC = codeview::visitTypeStream(Types, V)) {
660     Printer.formatLine("An error occurred dumping type records: {0}",
661                        toString(std::move(EC)));
662   }
663 }
664
665 static void dumpPartialTypeStream(LinePrinter &Printer,
666                                   LazyRandomTypeCollection &Types,
667                                   TpiStream &Stream, ArrayRef<TypeIndex> TiList,
668                                   bool Bytes, bool Extras, bool Deps) {
669   uint32_t Width =
670       NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords());
671
672   MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types,
673                            Stream.getNumHashBuckets(), Stream.getHashValues());
674
675   if (opts::dump::DumpTypeDependents) {
676     // If we need to dump all dependents, then iterate each index and find
677     // all dependents, adding them to a map ordered by TypeIndex.
678     std::map<TypeIndex, CVType> DepSet;
679     buildDepSet(Types, TiList, DepSet);
680
681     Printer.formatLine(
682         "Showing {0:N} records and their dependents ({1:N} records total)",
683         TiList.size(), DepSet.size());
684
685     for (auto &Dep : DepSet) {
686       if (auto EC = codeview::visitTypeRecord(Dep.second, Dep.first, V))
687         Printer.formatLine("An error occurred dumping type record {0}: {1}",
688                            Dep.first, toString(std::move(EC)));
689     }
690   } else {
691     Printer.formatLine("Showing {0:N} records.", TiList.size());
692
693     for (const auto &I : TiList) {
694       TypeIndex TI(I);
695       CVType Type = Types.getType(TI);
696       if (auto EC = codeview::visitTypeRecord(Type, TI, V))
697         Printer.formatLine("An error occurred dumping type record {0}: {1}", TI,
698                            toString(std::move(EC)));
699     }
700   }
701 }
702
703 Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
704   assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);
705
706   bool Present = false;
707   bool DumpTypes = false;
708   bool DumpBytes = false;
709   bool DumpExtras = false;
710   std::vector<uint32_t> Indices;
711   if (StreamIdx == StreamTPI) {
712     printHeader(P, "Types (TPI Stream)");
713     Present = File.hasPDBTpiStream();
714     DumpTypes = opts::dump::DumpTypes;
715     DumpBytes = opts::dump::DumpTypeData;
716     DumpExtras = opts::dump::DumpTypeExtras;
717     Indices.assign(opts::dump::DumpTypeIndex.begin(),
718                    opts::dump::DumpTypeIndex.end());
719   } else if (StreamIdx == StreamIPI) {
720     printHeader(P, "Types (IPI Stream)");
721     Present = File.hasPDBIpiStream();
722     DumpTypes = opts::dump::DumpIds;
723     DumpBytes = opts::dump::DumpIdData;
724     DumpExtras = opts::dump::DumpIdExtras;
725     Indices.assign(opts::dump::DumpIdIndex.begin(),
726                    opts::dump::DumpIdIndex.end());
727   }
728
729   AutoIndent Indent(P);
730   if (!Present) {
731     P.formatLine("Stream not present");
732     return Error::success();
733   }
734
735   ExitOnError Err("Unexpected error processing types");
736
737   auto &Stream = Err((StreamIdx == StreamTPI) ? File.getPDBTpiStream()
738                                               : File.getPDBIpiStream());
739
740   auto &Types = Err(initializeTypes(StreamIdx));
741
742   if (DumpTypes || !Indices.empty()) {
743     if (Indices.empty())
744       dumpFullTypeStream(P, Types, Stream, DumpBytes, DumpExtras);
745     else {
746       std::vector<TypeIndex> TiList(Indices.begin(), Indices.end());
747       dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras,
748                             opts::dump::DumpTypeDependents);
749     }
750   }
751
752   if (DumpExtras) {
753     P.NewLine();
754     auto IndexOffsets = Stream.getTypeIndexOffsets();
755     P.formatLine("Type Index Offsets:");
756     for (const auto &IO : IndexOffsets) {
757       AutoIndent Indent2(P);
758       P.formatLine("TI: {0}, Offset: {1}", IO.Type, fmtle(IO.Offset));
759     }
760
761     P.NewLine();
762     P.formatLine("Hash Adjusters:");
763     auto &Adjusters = Stream.getHashAdjusters();
764     auto &Strings = Err(File.getStringTable());
765     for (const auto &A : Adjusters) {
766       AutoIndent Indent2(P);
767       auto ExpectedStr = Strings.getStringForID(A.first);
768       TypeIndex TI(A.second);
769       if (ExpectedStr)
770         P.formatLine("`{0}` -> {1}", *ExpectedStr, TI);
771       else {
772         P.formatLine("unknown str id ({0}) -> {1}", A.first, TI);
773         consumeError(ExpectedStr.takeError());
774       }
775     }
776   }
777   return Error::success();
778 }
779
780 Expected<codeview::LazyRandomTypeCollection &>
781 DumpOutputStyle::initializeTypes(uint32_t SN) {
782   auto &TypeCollection = (SN == StreamTPI) ? TpiTypes : IpiTypes;
783   auto Tpi =
784       (SN == StreamTPI) ? File.getPDBTpiStream() : File.getPDBIpiStream();
785   if (!Tpi)
786     return Tpi.takeError();
787
788   if (!TypeCollection) {
789     auto &Types = Tpi->typeArray();
790     uint32_t Count = Tpi->getNumTypeRecords();
791     auto Offsets = Tpi->getTypeIndexOffsets();
792     TypeCollection =
793         llvm::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets);
794   }
795
796   return *TypeCollection;
797 }
798
799 Error DumpOutputStyle::dumpModuleSyms() {
800   printHeader(P, "Symbols");
801
802   AutoIndent Indent(P);
803   if (!File.hasPDBDbiStream()) {
804     P.formatLine("DBI Stream not present");
805     return Error::success();
806   }
807
808   ExitOnError Err("Unexpected error processing symbols");
809
810   auto &Stream = Err(File.getPDBDbiStream());
811
812   auto &Types = Err(initializeTypes(StreamTPI));
813
814   const DbiModuleList &Modules = Stream.modules();
815   uint32_t Count = Modules.getModuleCount();
816   uint32_t Digits = NumDigits(Count);
817   for (uint32_t I = 0; I < Count; ++I) {
818     auto Modi = Modules.getModuleDescriptor(I);
819     P.formatLine("Mod {0:4} | `{1}`: ", fmt_align(I, AlignStyle::Right, Digits),
820                  Modi.getModuleName());
821     uint16_t ModiStream = Modi.getModuleStreamIndex();
822     if (ModiStream == kInvalidStreamIndex) {
823       P.formatLine("           <symbols not present>");
824       continue;
825     }
826     auto ModStreamData = MappedBlockStream::createIndexedStream(
827         File.getMsfLayout(), File.getMsfBuffer(), ModiStream,
828         File.getAllocator());
829
830     ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData));
831     if (auto EC = ModS.reload()) {
832       P.formatLine("Error loading module stream {0}.  {1}", I,
833                    toString(std::move(EC)));
834       continue;
835     }
836
837     SymbolVisitorCallbackPipeline Pipeline;
838     SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
839     MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types);
840
841     Pipeline.addCallbackToPipeline(Deserializer);
842     Pipeline.addCallbackToPipeline(Dumper);
843     CVSymbolVisitor Visitor(Pipeline);
844     auto SS = ModS.getSymbolsSubstream();
845     if (auto EC = Visitor.visitSymbolStream(ModS.getSymbolArray(), SS.Offset)) {
846       P.formatLine("Error while processing symbol records.  {0}",
847                    toString(std::move(EC)));
848       continue;
849     }
850   }
851   return Error::success();
852 }
853
854 Error DumpOutputStyle::dumpPublics() {
855   printHeader(P, "Public Symbols");
856
857   AutoIndent Indent(P);
858   if (!File.hasPDBPublicsStream()) {
859     P.formatLine("Publics stream not present");
860     return Error::success();
861   }
862
863   ExitOnError Err("Error dumping publics stream");
864
865   auto &Types = Err(initializeTypes(StreamTPI));
866   auto &Publics = Err(File.getPDBPublicsStream());
867   SymbolVisitorCallbackPipeline Pipeline;
868   SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
869   MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types);
870
871   Pipeline.addCallbackToPipeline(Deserializer);
872   Pipeline.addCallbackToPipeline(Dumper);
873   CVSymbolVisitor Visitor(Pipeline);
874
875   auto ExpectedSymbols = Publics.getSymbolArray();
876   if (!ExpectedSymbols) {
877     P.formatLine("Could not read public symbol record stream");
878     return Error::success();
879   }
880
881   if (auto EC = Visitor.visitSymbolStream(*ExpectedSymbols, 0))
882     P.formatLine("Error while processing public symbol records.  {0}",
883                  toString(std::move(EC)));
884
885   return Error::success();
886 }
887
888 static std::string formatSectionCharacteristics(uint32_t IndentLevel,
889                                                 uint32_t C) {
890   using SC = COFF::SectionCharacteristics;
891   std::vector<std::string> Opts;
892   if (C == COFF::SC_Invalid)
893     return "invalid";
894   if (C == 0)
895     return "none";
896
897   PUSH_FLAG(SC, IMAGE_SCN_TYPE_NOLOAD, C, "IMAGE_SCN_TYPE_NOLOAD");
898   PUSH_FLAG(SC, IMAGE_SCN_TYPE_NO_PAD, C, "IMAGE_SCN_TYPE_NO_PAD");
899   PUSH_FLAG(SC, IMAGE_SCN_CNT_CODE, C, "IMAGE_SCN_CNT_CODE");
900   PUSH_FLAG(SC, IMAGE_SCN_CNT_INITIALIZED_DATA, C,
901             "IMAGE_SCN_CNT_INITIALIZED_DATA");
902   PUSH_FLAG(SC, IMAGE_SCN_CNT_UNINITIALIZED_DATA, C,
903             "IMAGE_SCN_CNT_UNINITIALIZED_DATA");
904   PUSH_FLAG(SC, IMAGE_SCN_LNK_OTHER, C, "IMAGE_SCN_LNK_OTHER");
905   PUSH_FLAG(SC, IMAGE_SCN_LNK_INFO, C, "IMAGE_SCN_LNK_INFO");
906   PUSH_FLAG(SC, IMAGE_SCN_LNK_REMOVE, C, "IMAGE_SCN_LNK_REMOVE");
907   PUSH_FLAG(SC, IMAGE_SCN_LNK_COMDAT, C, "IMAGE_SCN_LNK_COMDAT");
908   PUSH_FLAG(SC, IMAGE_SCN_GPREL, C, "IMAGE_SCN_GPREL");
909   PUSH_FLAG(SC, IMAGE_SCN_MEM_PURGEABLE, C, "IMAGE_SCN_MEM_PURGEABLE");
910   PUSH_FLAG(SC, IMAGE_SCN_MEM_16BIT, C, "IMAGE_SCN_MEM_16BIT");
911   PUSH_FLAG(SC, IMAGE_SCN_MEM_LOCKED, C, "IMAGE_SCN_MEM_LOCKED");
912   PUSH_FLAG(SC, IMAGE_SCN_MEM_PRELOAD, C, "IMAGE_SCN_MEM_PRELOAD");
913   PUSH_FLAG(SC, IMAGE_SCN_GPREL, C, "IMAGE_SCN_GPREL");
914   PUSH_FLAG(SC, IMAGE_SCN_GPREL, C, "IMAGE_SCN_GPREL");
915   PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_1BYTES, C,
916                    "IMAGE_SCN_ALIGN_1BYTES");
917   PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_2BYTES, C,
918                    "IMAGE_SCN_ALIGN_2BYTES");
919   PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_4BYTES, C,
920                    "IMAGE_SCN_ALIGN_4BYTES");
921   PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_8BYTES, C,
922                    "IMAGE_SCN_ALIGN_8BYTES");
923   PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_16BYTES, C,
924                    "IMAGE_SCN_ALIGN_16BYTES");
925   PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_32BYTES, C,
926                    "IMAGE_SCN_ALIGN_32BYTES");
927   PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_64BYTES, C,
928                    "IMAGE_SCN_ALIGN_64BYTES");
929   PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_128BYTES, C,
930                    "IMAGE_SCN_ALIGN_128BYTES");
931   PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_256BYTES, C,
932                    "IMAGE_SCN_ALIGN_256BYTES");
933   PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_512BYTES, C,
934                    "IMAGE_SCN_ALIGN_512BYTES");
935   PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_1024BYTES, C,
936                    "IMAGE_SCN_ALIGN_1024BYTES");
937   PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_2048BYTES, C,
938                    "IMAGE_SCN_ALIGN_2048BYTES");
939   PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_4096BYTES, C,
940                    "IMAGE_SCN_ALIGN_4096BYTES");
941   PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_8192BYTES, C,
942                    "IMAGE_SCN_ALIGN_8192BYTES");
943   PUSH_FLAG(SC, IMAGE_SCN_LNK_NRELOC_OVFL, C, "IMAGE_SCN_LNK_NRELOC_OVFL");
944   PUSH_FLAG(SC, IMAGE_SCN_MEM_DISCARDABLE, C, "IMAGE_SCN_MEM_DISCARDABLE");
945   PUSH_FLAG(SC, IMAGE_SCN_MEM_NOT_CACHED, C, "IMAGE_SCN_MEM_NOT_CACHED");
946   PUSH_FLAG(SC, IMAGE_SCN_MEM_NOT_PAGED, C, "IMAGE_SCN_MEM_NOT_PAGED");
947   PUSH_FLAG(SC, IMAGE_SCN_MEM_SHARED, C, "IMAGE_SCN_MEM_SHARED");
948   PUSH_FLAG(SC, IMAGE_SCN_MEM_EXECUTE, C, "IMAGE_SCN_MEM_EXECUTE");
949   PUSH_FLAG(SC, IMAGE_SCN_MEM_READ, C, "IMAGE_SCN_MEM_READ");
950   PUSH_FLAG(SC, IMAGE_SCN_MEM_WRITE, C, "IMAGE_SCN_MEM_WRITE");
951   return typesetItemList(Opts, IndentLevel, 3, " | ");
952 }
953
954 static std::string formatSegMapDescriptorFlag(uint32_t IndentLevel,
955                                               OMFSegDescFlags Flags) {
956   std::vector<std::string> Opts;
957   if (Flags == OMFSegDescFlags::None)
958     return "none";
959
960   PUSH_FLAG(OMFSegDescFlags, Read, Flags, "read");
961   PUSH_FLAG(OMFSegDescFlags, Write, Flags, "write");
962   PUSH_FLAG(OMFSegDescFlags, Execute, Flags, "execute");
963   PUSH_FLAG(OMFSegDescFlags, AddressIs32Bit, Flags, "32 bit addr");
964   PUSH_FLAG(OMFSegDescFlags, IsSelector, Flags, "selector");
965   PUSH_FLAG(OMFSegDescFlags, IsAbsoluteAddress, Flags, "absolute addr");
966   PUSH_FLAG(OMFSegDescFlags, IsGroup, Flags, "group");
967   return typesetItemList(Opts, IndentLevel, 4, " | ");
968 }
969
970 Error DumpOutputStyle::dumpSectionContribs() {
971   printHeader(P, "Section Contributions");
972   ExitOnError Err("Error dumping publics stream");
973
974   AutoIndent Indent(P);
975   if (!File.hasPDBDbiStream()) {
976     P.formatLine(
977         "Section contribs require a DBI Stream, which could not be loaded");
978     return Error::success();
979   }
980
981   auto &Dbi = Err(File.getPDBDbiStream());
982
983   class Visitor : public ISectionContribVisitor {
984   public:
985     Visitor(LinePrinter &P) : P(P) {}
986     void visit(const SectionContrib &SC) override {
987       P.formatLine(
988           "SC  | mod = {2}, {0}, size = {1}, data crc = {3}, reloc crc = {4}",
989           formatSegmentOffset(SC.ISect, SC.Off), fmtle(SC.Size), fmtle(SC.Imod),
990           fmtle(SC.DataCrc), fmtle(SC.RelocCrc));
991       P.formatLine("      {0}",
992                    formatSectionCharacteristics(P.getIndentLevel() + 6,
993                                                 SC.Characteristics));
994     }
995     void visit(const SectionContrib2 &SC) override {
996       P.formatLine("SC2 | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "
997                    "crc = {4}, coff section = {5}",
998                    formatSegmentOffset(SC.Base.ISect, SC.Base.Off),
999                    fmtle(SC.Base.Size), fmtle(SC.Base.Imod),
1000                    fmtle(SC.Base.DataCrc), fmtle(SC.Base.RelocCrc),
1001                    fmtle(SC.ISectCoff));
1002       P.formatLine("      {0}",
1003                    formatSectionCharacteristics(P.getIndentLevel() + 6,
1004                                                 SC.Base.Characteristics));
1005     }
1006
1007   private:
1008     LinePrinter &P;
1009   };
1010
1011   Visitor V(P);
1012   Dbi.visitSectionContributions(V);
1013   return Error::success();
1014 }
1015
1016 Error DumpOutputStyle::dumpSectionMap() {
1017   printHeader(P, "Section Map");
1018   ExitOnError Err("Error dumping section map");
1019
1020   AutoIndent Indent(P);
1021   if (!File.hasPDBDbiStream()) {
1022     P.formatLine("Dumping the section map requires a DBI Stream, which could "
1023                  "not be loaded");
1024     return Error::success();
1025   }
1026
1027   auto &Dbi = Err(File.getPDBDbiStream());
1028
1029   uint32_t I = 0;
1030   for (auto &M : Dbi.getSectionMap()) {
1031     P.formatLine(
1032         "Section {0:4} | ovl = {0}, group = {1}, frame = {2}, name = {3}", I,
1033         fmtle(M.Ovl), fmtle(M.Group), fmtle(M.Frame), fmtle(M.SecName));
1034     P.formatLine("               class = {0}, offset = {1}, size = {2}",
1035                  fmtle(M.ClassName), fmtle(M.Offset), fmtle(M.SecByteLength));
1036     P.formatLine("               flags = {0}",
1037                  formatSegMapDescriptorFlag(
1038                      P.getIndentLevel() + 13,
1039                      static_cast<OMFSegDescFlags>(uint16_t(M.Flags))));
1040     ++I;
1041   }
1042   return Error::success();
1043 }