1 //===- DumpOutputStyle.cpp ------------------------------------ *- C++ --*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "DumpOutputStyle.h"
12 #include "FormatUtil.h"
13 #include "MinimalSymbolDumper.h"
14 #include "MinimalTypeDumper.h"
15 #include "StreamUtil.h"
16 #include "llvm-pdbutil.h"
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"
61 #include <unordered_map>
64 using namespace llvm::codeview;
65 using namespace llvm::msf;
66 using namespace llvm::pdb;
68 DumpOutputStyle::DumpOutputStyle(PDBFile &File)
69 : File(File), P(2, false, outs()) {}
71 Error DumpOutputStyle::dump() {
72 if (opts::dump::DumpSummary) {
73 if (auto EC = dumpFileSummary())
78 if (opts::dump::DumpStreams) {
79 if (auto EC = dumpStreamSummary())
84 if (opts::dump::DumpStringTable) {
85 if (auto EC = dumpStringTable())
90 if (opts::dump::DumpModules) {
91 if (auto EC = dumpModules())
95 if (opts::dump::DumpModuleFiles) {
96 if (auto EC = dumpModuleFiles())
100 if (opts::dump::DumpLines) {
101 if (auto EC = dumpLines())
105 if (opts::dump::DumpInlineeLines) {
106 if (auto EC = dumpInlineeLines())
110 if (opts::dump::DumpXmi) {
111 if (auto EC = dumpXmi())
115 if (opts::dump::DumpXme) {
116 if (auto EC = dumpXme())
120 if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() ||
121 opts::dump::DumpTypeExtras) {
122 if (auto EC = dumpTpiStream(StreamTPI))
126 if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() ||
127 opts::dump::DumpIdExtras) {
128 if (auto EC = dumpTpiStream(StreamIPI))
132 if (opts::dump::DumpPublics) {
133 if (auto EC = dumpPublics())
137 if (opts::dump::DumpSymbols) {
138 if (auto EC = dumpModuleSyms())
142 if (opts::dump::DumpSectionContribs) {
143 if (auto EC = dumpSectionContribs())
147 if (opts::dump::DumpSectionMap) {
148 if (auto EC = dumpSectionMap())
152 return Error::success();
155 static void printHeader(LinePrinter &P, const Twine &S) {
157 P.formatLine("{0,=60}", S);
158 P.formatLine("{0}", fmt_repeat('=', 60));
161 Error DumpOutputStyle::dumpFileSummary() {
162 printHeader(P, "Summary");
164 ExitOnError Err("Invalid PDB Format");
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());
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());
188 return Error::success();
191 Error DumpOutputStyle::dumpStreamSummary() {
192 printHeader(P, "Streams");
194 if (StreamPurposes.empty())
195 discoverStreamPurposes(File, StreamPurposes);
197 AutoIndent Indent(P);
198 uint32_t StreamCount = File.getNumStreams();
200 for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {
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()));
214 return Error::success();
217 static Expected<ModuleDebugStreamRef> getModuleDebugStream(PDBFile &File,
219 ExitOnError Err("Unexpected error");
221 auto &Dbi = Err(File.getPDBDbiStream());
222 const auto &Modules = Dbi.modules();
223 auto Modi = Modules.getModuleDescriptor(Index);
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");
230 auto ModStreamData = MappedBlockStream::createIndexedStream(
231 File.getMsfLayout(), File.getMsfBuffer(), ModiStream,
232 File.getAllocator());
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");
239 return std::move(ModS);
242 static std::string formatChecksumKind(FileChecksumKind 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");
249 return formatUnknownEnum(Kind);
253 class StringsAndChecksumsPrinter {
254 const DebugStringTableSubsectionRef &extractStringTable(PDBFile &File) {
255 ExitOnError Err("Unexpected error processing modules");
256 return Err(File.getStringTable()).getStringTable();
259 template <typename... Args>
260 void formatInternal(LinePrinter &Printer, bool Append,
261 Args &&... args) const {
263 Printer.format(std::forward<Args>(args)...);
265 Printer.formatLine(std::forward<Args>(args)...);
269 StringsAndChecksumsPrinter(PDBFile &File, uint32_t Modi)
270 : Records(extractStringTable(File)) {
271 auto MDS = getModuleDebugStream(File, Modi);
273 consumeError(MDS.takeError());
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);
284 ChecksumsByFile[*S] = Entry;
289 Expected<StringRef> getNameFromStringTable(uint32_t Offset) const {
290 return Records.strings().getString(Offset);
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);
301 formatInternal(Printer, Append, "- ({0}: {1}) {2}",
302 formatChecksumKind(FC->getValue().Kind),
303 toHex(FC->getValue().Checksum), File);
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);
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);
319 uint32_t FO = Iter->FileNameOffset;
320 auto ExpectedFile = getNameFromStringTable(FO);
322 formatInternal(Printer, Append, "(unknown file name offset {0})", Offset);
323 consumeError(ExpectedFile.takeError());
326 if (Iter->Kind == FileChecksumKind::None) {
327 formatInternal(Printer, Append, "{0} (no checksum)", *ExpectedFile);
329 formatInternal(Printer, Append, "{0} ({1}: {2})", *ExpectedFile,
330 formatChecksumKind(Iter->Kind), toHex(Iter->Checksum));
334 std::unique_ptr<ModuleDebugStreamRef> DebugStream;
335 StringsAndChecksumsRef Records;
336 StringMap<FileChecksumEntry> ChecksumsByFile;
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");
349 ExitOnError Err("Unexpected error processing modules");
351 auto &Stream = Err(File.getPDBDbiStream());
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());
361 StringsAndChecksumsPrinter Strings(File, I);
362 AutoIndent Indent2(P, IndentLevel);
363 Callback(I, Strings);
367 template <typename SubsectionT>
368 static void iterateModuleSubsections(
369 PDBFile &File, LinePrinter &P, uint32_t IndentLevel,
370 llvm::function_ref<void(uint32_t, StringsAndChecksumsPrinter &,
375 File, P, IndentLevel,
376 [&File, &Callback](uint32_t Modi, StringsAndChecksumsPrinter &Strings) {
377 auto MDS = getModuleDebugStream(File, Modi);
379 consumeError(MDS.takeError());
383 for (const auto &SS : MDS->subsections()) {
384 SubsectionT Subsection;
386 if (SS.kind() != Subsection.kind())
389 BinaryStreamReader Reader(SS.getRecordData());
390 if (auto EC = Subsection.initialize(Reader))
392 Callback(Modi, Strings, Subsection);
397 Error DumpOutputStyle::dumpModules() {
398 printHeader(P, "Modules");
400 AutoIndent Indent(P);
401 if (!File.hasPDBDbiStream()) {
402 P.formatLine("DBI Stream not present");
403 return Error::success();
406 ExitOnError Err("Unexpected error processing modules");
408 auto &Stream = Err(File.getPDBDbiStream());
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(),
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);
429 return Error::success();
432 Error DumpOutputStyle::dumpModuleFiles() {
433 printHeader(P, "Files");
435 ExitOnError Err("Unexpected error processing modules");
439 [this, &Err](uint32_t Modi, StringsAndChecksumsPrinter &Strings) {
440 auto &Stream = Err(File.getPDBDbiStream());
442 const DbiModuleList &Modules = Stream.modules();
443 for (const auto &F : Modules.source_files(Modi)) {
444 Strings.formatFromFileName(P, F);
447 return Error::success();
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;
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);
466 if (Line.isAlwaysStepInto())
468 else if (Line.isNeverStepInto())
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'),
484 Error DumpOutputStyle::dumpLines() {
485 printHeader(P, "Lines");
487 uint32_t LastModi = UINT32_MAX;
488 uint32_t LastNameIndex = UINT32_MAX;
489 iterateModuleSubsections<DebugLinesSubsectionRef>(
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) {
500 LastNameIndex = Block.NameIndex;
501 Strings.formatFromChecksumsOffset(P, Block.NameIndex);
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);
510 P.format("line/addr entries = {0}", Count);
513 typesetLinesAndColumns(File, P, Begin, Block);
517 return Error::success();
520 Error DumpOutputStyle::dumpInlineeLines() {
521 printHeader(P, "Inlinee Lines");
523 iterateModuleSubsections<DebugInlineeLinesSubsectionRef>(
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);
536 return Error::success();
539 Error DumpOutputStyle::dumpXmi() {
540 printHeader(P, "Cross Module Imports");
541 iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>(
543 [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings,
544 DebugCrossModuleImportsSubsectionRef &Imports) {
545 P.formatLine("{0,=32} | {1}", "Imported Module", "Type IDs");
547 for (const auto &Xmi : Imports) {
548 auto ExpectedModule =
549 Strings.getNameFromStringTable(Xmi.Header->ModuleNameOffset);
551 SmallString<32> ModuleStorage;
552 if (!ExpectedModule) {
553 Module = "(unknown module)";
554 consumeError(ExpectedModule.takeError());
556 Module = *ExpectedModule;
557 if (Module.size() > 32) {
558 ModuleStorage = "...";
559 ModuleStorage += Module.take_back(32 - 3);
560 Module = ModuleStorage;
562 std::vector<std::string> TIs;
563 for (const auto I : Xmi.Imports)
564 TIs.push_back(formatv("{0,+10:X+}", fmtle(I)));
566 typesetItemList(TIs, P.getIndentLevel() + 35, 12, " ");
567 P.formatLine("{0,+32} | {1}", Module, Result);
571 return Error::success();
574 Error DumpOutputStyle::dumpXme() {
575 printHeader(P, "Cross Module Exports");
577 iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>(
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));
588 return Error::success();
591 Error DumpOutputStyle::dumpStringTable() {
592 printHeader(P, "String Table");
594 AutoIndent Indent(P);
595 auto IS = File.getStringTable();
597 P.formatLine("Not present in file");
598 consumeError(IS.takeError());
599 return Error::success();
602 if (IS->name_ids().empty()) {
603 P.formatLine("Empty");
604 return Error::success();
607 auto MaxID = std::max_element(IS->name_ids().begin(), IS->name_ids().end());
608 uint32_t Digits = NumDigits(*MaxID);
610 P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits),
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;
619 consumeError(ES.takeError());
620 Str = "Error reading string";
621 } else if (!ES->empty()) {
628 P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits), Str);
630 return Error::success();
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) {
639 if (DepSet.find(TI) != DepSet.end() || TI.isSimple() || TI.isNoneType())
642 CVType Type = Types.getType(TI);
644 codeview::discoverTypeIndices(Type, DepList);
645 buildDepSet(Types, DepList, DepSet);
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());
654 NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords());
656 MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types,
657 Stream.getNumHashBuckets(), Stream.getHashValues());
659 if (auto EC = codeview::visitTypeStream(Types, V)) {
660 Printer.formatLine("An error occurred dumping type records: {0}",
661 toString(std::move(EC)));
665 static void dumpPartialTypeStream(LinePrinter &Printer,
666 LazyRandomTypeCollection &Types,
667 TpiStream &Stream, ArrayRef<TypeIndex> TiList,
668 bool Bytes, bool Extras, bool Deps) {
670 NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords());
672 MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types,
673 Stream.getNumHashBuckets(), Stream.getHashValues());
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);
682 "Showing {0:N} records and their dependents ({1:N} records total)",
683 TiList.size(), DepSet.size());
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)));
691 Printer.formatLine("Showing {0:N} records.", TiList.size());
693 for (const auto &I : TiList) {
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)));
703 Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
704 assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);
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());
729 AutoIndent Indent(P);
731 P.formatLine("Stream not present");
732 return Error::success();
735 ExitOnError Err("Unexpected error processing types");
737 auto &Stream = Err((StreamIdx == StreamTPI) ? File.getPDBTpiStream()
738 : File.getPDBIpiStream());
740 auto &Types = Err(initializeTypes(StreamIdx));
742 if (DumpTypes || !Indices.empty()) {
744 dumpFullTypeStream(P, Types, Stream, DumpBytes, DumpExtras);
746 std::vector<TypeIndex> TiList(Indices.begin(), Indices.end());
747 dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras,
748 opts::dump::DumpTypeDependents);
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));
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);
770 P.formatLine("`{0}` -> {1}", *ExpectedStr, TI);
772 P.formatLine("unknown str id ({0}) -> {1}", A.first, TI);
773 consumeError(ExpectedStr.takeError());
777 return Error::success();
780 Expected<codeview::LazyRandomTypeCollection &>
781 DumpOutputStyle::initializeTypes(uint32_t SN) {
782 auto &TypeCollection = (SN == StreamTPI) ? TpiTypes : IpiTypes;
784 (SN == StreamTPI) ? File.getPDBTpiStream() : File.getPDBIpiStream();
786 return Tpi.takeError();
788 if (!TypeCollection) {
789 auto &Types = Tpi->typeArray();
790 uint32_t Count = Tpi->getNumTypeRecords();
791 auto Offsets = Tpi->getTypeIndexOffsets();
793 llvm::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets);
796 return *TypeCollection;
799 Error DumpOutputStyle::dumpModuleSyms() {
800 printHeader(P, "Symbols");
802 AutoIndent Indent(P);
803 if (!File.hasPDBDbiStream()) {
804 P.formatLine("DBI Stream not present");
805 return Error::success();
808 ExitOnError Err("Unexpected error processing symbols");
810 auto &Stream = Err(File.getPDBDbiStream());
812 auto &Types = Err(initializeTypes(StreamTPI));
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>");
826 auto ModStreamData = MappedBlockStream::createIndexedStream(
827 File.getMsfLayout(), File.getMsfBuffer(), ModiStream,
828 File.getAllocator());
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)));
837 SymbolVisitorCallbackPipeline Pipeline;
838 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
839 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types);
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)));
851 return Error::success();
854 Error DumpOutputStyle::dumpPublics() {
855 printHeader(P, "Public Symbols");
857 AutoIndent Indent(P);
858 if (!File.hasPDBPublicsStream()) {
859 P.formatLine("Publics stream not present");
860 return Error::success();
863 ExitOnError Err("Error dumping publics stream");
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);
871 Pipeline.addCallbackToPipeline(Deserializer);
872 Pipeline.addCallbackToPipeline(Dumper);
873 CVSymbolVisitor Visitor(Pipeline);
875 auto ExpectedSymbols = Publics.getSymbolArray();
876 if (!ExpectedSymbols) {
877 P.formatLine("Could not read public symbol record stream");
878 return Error::success();
881 if (auto EC = Visitor.visitSymbolStream(*ExpectedSymbols, 0))
882 P.formatLine("Error while processing public symbol records. {0}",
883 toString(std::move(EC)));
885 return Error::success();
888 static std::string formatSectionCharacteristics(uint32_t IndentLevel,
890 using SC = COFF::SectionCharacteristics;
891 std::vector<std::string> Opts;
892 if (C == COFF::SC_Invalid)
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, " | ");
954 static std::string formatSegMapDescriptorFlag(uint32_t IndentLevel,
955 OMFSegDescFlags Flags) {
956 std::vector<std::string> Opts;
957 if (Flags == OMFSegDescFlags::None)
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, " | ");
970 Error DumpOutputStyle::dumpSectionContribs() {
971 printHeader(P, "Section Contributions");
972 ExitOnError Err("Error dumping publics stream");
974 AutoIndent Indent(P);
975 if (!File.hasPDBDbiStream()) {
977 "Section contribs require a DBI Stream, which could not be loaded");
978 return Error::success();
981 auto &Dbi = Err(File.getPDBDbiStream());
983 class Visitor : public ISectionContribVisitor {
985 Visitor(LinePrinter &P) : P(P) {}
986 void visit(const SectionContrib &SC) override {
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));
992 formatSectionCharacteristics(P.getIndentLevel() + 6,
993 SC.Characteristics));
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));
1012 Dbi.visitSectionContributions(V);
1013 return Error::success();
1016 Error DumpOutputStyle::dumpSectionMap() {
1017 printHeader(P, "Section Map");
1018 ExitOnError Err("Error dumping section map");
1020 AutoIndent Indent(P);
1021 if (!File.hasPDBDbiStream()) {
1022 P.formatLine("Dumping the section map requires a DBI Stream, which could "
1024 return Error::success();
1027 auto &Dbi = Err(File.getPDBDbiStream());
1030 for (auto &M : Dbi.getSectionMap()) {
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))));
1042 return Error::success();