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