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