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() {}
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::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(StreamIdx: StreamTPI))
177 return EC;
178 }
179
180 if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() ||
181 opts::dump::DumpIdExtras) {
182 if (auto EC = dumpTpiStream(StreamIdx: 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
232static void printHeader(LinePrinter &P, const Twine &S) {
233 P.NewLine();
234 P.formatLine(Fmt: "{0,=60}", Items: S);
235 P.formatLine(Fmt: "{0}", Items: fmt_repeat(Item: '=', Count: 60));
236}
237
238Error DumpOutputStyle::dumpFileSummary() {
239 printHeader(P, S: "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(Fmt: "Block Size: {0}", Items: getPdb().getBlockSize());
250 P.formatLine(Fmt: "Number of blocks: {0}", Items: getPdb().getBlockCount());
251 P.formatLine(Fmt: "Number of streams: {0}", Items: getPdb().getNumStreams());
252
253 auto &PS = Err(getPdb().getPDBInfoStream());
254 P.formatLine(Fmt: "Signature: {0}", Items: PS.getSignature());
255 P.formatLine(Fmt: "Age: {0}", Items: PS.getAge());
256 P.formatLine(Fmt: "GUID: {0}", Items: fmt_guid(Item: PS.getGuid().Guid));
257 P.formatLine(Fmt: "Features: {0:x+}", Items: static_cast<uint32_t>(PS.getFeatures()));
258 P.formatLine(Fmt: "Has Debug Info: {0}", Items: getPdb().hasPDBDbiStream());
259 P.formatLine(Fmt: "Has Types: {0}", Items: getPdb().hasPDBTpiStream());
260 P.formatLine(Fmt: "Has IDs: {0}", Items: getPdb().hasPDBIpiStream());
261 P.formatLine(Fmt: "Has Globals: {0}", Items: getPdb().hasPDBGlobalsStream());
262 P.formatLine(Fmt: "Has Publics: {0}", Items: getPdb().hasPDBPublicsStream());
263 if (getPdb().hasPDBDbiStream()) {
264 DbiStream &DBI = Err(getPdb().getPDBDbiStream());
265 P.formatLine(Fmt: "Is incrementally linked: {0}", Items: DBI.isIncrementallyLinked());
266 P.formatLine(Fmt: "Has conflicting types: {0}", Items: DBI.hasCTypes());
267 P.formatLine(Fmt: "Is stripped: {0}", Items: DBI.isStripped());
268 }
269
270 return Error::success();
271}
272
273static 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(HadError: nullptr)) {
279 Stats.update(Kind: S.kind(), RecordSize: S.length());
280 CumulativeStats.update(Kind: S.kind(), RecordSize: 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(Err: Symbols.initialize(Reader));
293 for (const auto &S : Symbols) {
294 Stats.update(Kind: S.kind(), RecordSize: S.length());
295 CumulativeStats.update(Kind: S.kind(), RecordSize: S.length());
296 }
297 }
298 return Stats;
299}
300
301static StatCollection getChunkStats(const SymbolGroup &SG,
302 StatCollection &CumulativeStats) {
303 StatCollection Stats;
304 for (const auto &Chunk : SG.getDebugSubsections()) {
305 Stats.update(Kind: uint32_t(Chunk.kind()), RecordSize: Chunk.getRecordLength());
306 CumulativeStats.update(Kind: uint32_t(Chunk.kind()), RecordSize: Chunk.getRecordLength());
307 }
308 return Stats;
309}
310
311static inline std::string formatModuleDetailKind(DebugSubsectionKind K) {
312 return formatChunkKind(Kind: K, Friendly: false);
313}
314
315static inline std::string formatModuleDetailKind(SymbolKind K) {
316 return formatSymbolKind(K);
317}
318
319// Get the stats sorted by size, descending.
320std::vector<StatCollection::KindAndStat>
321StatCollection::getStatsSortedBySize() const {
322 std::vector<KindAndStat> SortedStats(Individual.begin(), Individual.end());
323 llvm::stable_sort(Range&: SortedStats,
324 C: [](const KindAndStat &LHS, const KindAndStat &RHS) {
325 return LHS.second.Size > RHS.second.Size;
326 });
327 return SortedStats;
328}
329
330template <typename Kind>
331static void printModuleDetailStats(LinePrinter &P, StringRef Label,
332 const StatCollection &Stats) {
333 P.NewLine();
334 P.formatLine(Fmt: " {0}", Items&: Label);
335 AutoIndent Indent(P);
336 P.formatLine(Fmt: "{0,40}: {1,7} entries ({2,12:N} bytes)", Items: "Total",
337 Items: Stats.Totals.Count, Items: Stats.Totals.Size);
338 P.formatLine(Fmt: "{0}", Items: fmt_repeat(Item: '-', Count: 74));
339
340 for (const auto &K : Stats.getStatsSortedBySize()) {
341 std::string KindName = formatModuleDetailKind(Kind(K.first));
342 P.formatLine(Fmt: "{0,40}: {1,7} entries ({2,12:N} bytes)", Items&: KindName,
343 Items: K.second.Count, Items: K.second.Size);
344 }
345}
346
347Error DumpOutputStyle::dumpStreamSummary() {
348 printHeader(P, S: "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(File&: getPdb(), Streams&: 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 Fmt: "Stream {0} ({1} bytes): [{2}]",
366 Items: fmt_align(Item&: StreamIdx, Where: AlignStyle::Right, Amount: NumDigits(N: StreamCount)),
367 Items: fmt_align(Item: getPdb().getStreamByteSize(StreamIndex: StreamIdx), Where: AlignStyle::Right,
368 Amount: NumDigits(N: MaxStreamSize)),
369 Items: StreamPurposes[StreamIdx].getLongName());
370
371 if (opts::dump::DumpStreamBlocks) {
372 auto Blocks = getPdb().getStreamBlockList(StreamIndex: StreamIdx);
373 std::vector<uint32_t> BV(Blocks.begin(), Blocks.end());
374 P.formatLine(Fmt: " {0} Blocks: [{1}]",
375 Items: fmt_repeat(Item: ' ', Count: NumDigits(N: StreamCount)),
376 Items: make_range(x: BV.begin(), y: BV.end()));
377 }
378 }
379
380 return Error::success();
381}
382
383static Expected<std::pair<std::unique_ptr<MappedBlockStream>,
384 ArrayRef<llvm::object::coff_section>>>
385loadSectionHeaders(PDBFile &File, DbgHeaderType Type) {
386 if (!File.hasPDBDbiStream())
387 return make_error<StringError>(
388 Args: "Section headers require a DBI Stream, which could not be loaded",
389 Args: inconvertibleErrorCode());
390
391 DbiStream &Dbi = cantFail(ValOrErr: File.getPDBDbiStream());
392 uint32_t SI = Dbi.getDebugStreamIndex(Type);
393
394 if (SI == kInvalidStreamIndex)
395 return make_error<StringError>(
396 Args: "PDB does not contain the requested image section header type",
397 Args: inconvertibleErrorCode());
398
399 auto Stream = File.createIndexedStream(SN: SI);
400 if (!Stream)
401 return make_error<StringError>(Args: "Could not load the required stream data",
402 Args: inconvertibleErrorCode());
403
404 ArrayRef<object::coff_section> Headers;
405 if (Stream->getLength() % sizeof(object::coff_section) != 0)
406 return make_error<StringError>(
407 Args: "Section header array size is not a multiple of section header size",
408 Args: inconvertibleErrorCode());
409
410 uint32_t NumHeaders = Stream->getLength() / sizeof(object::coff_section);
411 BinaryStreamReader Reader(*Stream);
412 cantFail(Err: Reader.readArray(Array&: Headers, NumElements: NumHeaders));
413 return std::make_pair(x: std::move(Stream), y&: Headers);
414}
415
416static Expected<std::vector<std::string>> getSectionNames(PDBFile &File) {
417 auto ExpectedHeaders = loadSectionHeaders(File, Type: 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(args&: Stream, args&: Headers) = std::move(*ExpectedHeaders);
424 std::vector<std::string> Names;
425 for (const auto &H : Headers)
426 Names.push_back(x: H.Name);
427 return Names;
428}
429
430static 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(Fmt: "[{0}]", Vals&: SectionName).str();
437 } else
438 NameInsert = "[???]";
439 P.formatLine(Fmt: "SC{5} | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "
440 "crc = {4}",
441 Items: formatSegmentOffset(Segment: SC.ISect, Offset: SC.Off), Items: fmtle(Value: SC.Size),
442 Items: fmtle(Value: SC.Imod), Items: fmtle(Value: SC.DataCrc), Items: fmtle(Value: SC.RelocCrc),
443 Items: fmt_align(Item&: NameInsert, Where: AlignStyle::Left, Amount: FieldWidth + 2));
444 AutoIndent Indent(P, FieldWidth + 2);
445 P.formatLine(Fmt: " {0}",
446 Items: formatSectionCharacteristics(IndentLevel: P.getIndentLevel() + 6,
447 C: SC.Characteristics, FlagsPerLine: 3, Separator: " | "));
448}
449
450static void dumpSectionContrib(LinePrinter &P, const SectionContrib2 &SC,
451 ArrayRef<std::string> SectionNames,
452 uint32_t FieldWidth) {
453 P.formatLine(Fmt: "SC2[{6}] | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "
454 "crc = {4}, coff section = {5}",
455 Items: formatSegmentOffset(Segment: SC.Base.ISect, Offset: SC.Base.Off),
456 Items: fmtle(Value: SC.Base.Size), Items: fmtle(Value: SC.Base.Imod), Items: fmtle(Value: SC.Base.DataCrc),
457 Items: fmtle(Value: SC.Base.RelocCrc), Items: fmtle(Value: SC.ISectCoff));
458 P.formatLine(Fmt: " {0}",
459 Items: formatSectionCharacteristics(IndentLevel: P.getIndentLevel() + 6,
460 C: SC.Base.Characteristics, FlagsPerLine: 3, Separator: " | "));
461}
462
463Error DumpOutputStyle::dumpModules() {
464 printHeader(P, S: "Modules");
465
466 if (File.isObj()) {
467 printStreamNotValidForObj();
468 return Error::success();
469 }
470
471 if (!getPdb().hasPDBDbiStream()) {
472 printStreamNotPresent(StreamName: "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 Input&: File, HeaderScope: PrintScope{P, 11},
486 Callback: [&](uint32_t Modi, const SymbolGroup &Strings) -> Error {
487 auto Desc = Modules.getModuleDescriptor(Modi);
488 if (opts::dump::DumpSectionContribs) {
489 auto SectionsOrErr = getSectionNames(File&: getPdb());
490 if (!SectionsOrErr)
491 return SectionsOrErr.takeError();
492 ArrayRef<std::string> Sections = *SectionsOrErr;
493 dumpSectionContrib(P, SC: Desc.getSectionContrib(), SectionNames: Sections, FieldWidth: 0);
494 }
495 P.formatLine(Fmt: "Obj: `{0}`: ", Items: Desc.getObjFileName());
496 P.formatLine(Fmt: "debug stream: {0}, # files: {1}, has ec info: {2}",
497 Items: Desc.getModuleStreamIndex(), Items: Desc.getNumberOfFiles(),
498 Items: Desc.hasECInfo());
499
500 auto PdbPathOrErr = Stream.getECName(NI: Desc.getPdbFilePathNameIndex());
501 if (!PdbPathOrErr)
502 return PdbPathOrErr.takeError();
503 StringRef PdbFilePath = *PdbPathOrErr;
504
505 auto SrcPathOrErr = Stream.getECName(NI: Desc.getSourceFileNameIndex());
506 if (!SrcPathOrErr)
507 return SrcPathOrErr.takeError();
508 StringRef SrcFilePath = *SrcPathOrErr;
509
510 P.formatLine(Fmt: "pdb file ni: {0} `{1}`, src file ni: {2} `{3}`",
511 Items: Desc.getPdbFilePathNameIndex(), Items&: PdbFilePath,
512 Items: Desc.getSourceFileNameIndex(), Items&: SrcFilePath);
513 return Error::success();
514 });
515}
516
517Error DumpOutputStyle::dumpModuleFiles() {
518 printHeader(P, S: "Files");
519
520 if (File.isObj()) {
521 printStreamNotValidForObj();
522 return Error::success();
523 }
524
525 if (!getPdb().hasPDBDbiStream()) {
526 printStreamNotPresent(StreamName: "DBI");
527 return Error::success();
528 }
529
530 return iterateSymbolGroups(
531 Input&: File, HeaderScope: PrintScope{P, 11},
532 Callback: [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(Printer&: P, File: F);
541 }
542 return Error::success();
543 });
544}
545
546Error DumpOutputStyle::dumpSymbolStats() {
547 printHeader(P, S: "Module Stats");
548
549 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
550 printStreamNotPresent(StreamName: "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 Input&: File, HeaderScope: Scope, Callback: [&](uint32_t Modi, const SymbolGroup &SG) -> Error {
560 StatCollection SS = getSymbolStats(SG, CumulativeStats&: SymStats);
561 StatCollection CS = getChunkStats(SG, CumulativeStats&: ChunkStats);
562
563 if (!SG.getFile().isPdb())
564 return Error::success();
565
566 AutoIndent Indent(P);
567 auto Modules = cantFail(ValOrErr: 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 Fmt: "Mod {0} (debug info not present): [{1}]",
575 Items: fmt_align(Item&: Modi, Where: AlignStyle::Right, Amount: NumDigits(N: ModCount)),
576 Items: Desc.getModuleName());
577 return Error::success();
578 }
579 P.formatLine(Fmt: "Stream {0}, {1} bytes", Items&: StreamIdx,
580 Items: getPdb().getStreamByteSize(StreamIndex: StreamIdx));
581
582 printModuleDetailStats<SymbolKind>(P, Label: "Symbols", Stats: SS);
583 printModuleDetailStats<DebugSubsectionKind>(P, Label: "Chunks", Stats: CS);
584
585 return Error::success();
586 }))
587 return Err;
588
589 if (SymStats.Totals.Count > 0) {
590 P.printLine(T: " Summary |");
591 AutoIndent Indent(P, 4);
592 printModuleDetailStats<SymbolKind>(P, Label: "Symbols", Stats: SymStats);
593 printModuleDetailStats<DebugSubsectionKind>(P, Label: "Chunks", Stats: ChunkStats);
594 }
595
596 return Error::success();
597}
598
599Error DumpOutputStyle::dumpTypeStats() {
600 printHeader(P, S: "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(Prev: *TI)) {
608 CVType Type = Types.getType(Index: *TI);
609 TypeStats.update(Kind: uint32_t(Type.kind()), RecordSize: Type.length());
610 }
611
612 P.NewLine();
613 P.formatLine(Fmt: " Types");
614 AutoIndent Indent(P);
615 P.formatLine(Fmt: "{0,16}: {1,7} entries ({2,12:N} bytes, {3,7} avg)", Items: "Total",
616 Items&: TypeStats.Totals.Count, Items&: TypeStats.Totals.Size,
617 Items: (double)TypeStats.Totals.Size / TypeStats.Totals.Count);
618 P.formatLine(Fmt: "{0}", Items: fmt_repeat(Item: '-', Count: 74));
619
620 for (const auto &K : TypeStats.getStatsSortedBySize()) {
621 P.formatLine(Fmt: "{0,16}: {1,7} entries ({2,12:N} bytes, {3,7} avg)",
622 Items: formatTypeLeafKind(K: TypeLeafKind(K.first)), Items: K.second.Count,
623 Items: K.second.Size, Items: (double)K.second.Size / K.second.Count);
624 }
625 return Error::success();
626}
627
628static 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(Range&: S, P: [](char C) { return std::isalnum(C); });
636}
637
638namespace {
639constexpr uint32_t kNoneUdtKind = 0;
640constexpr uint32_t kSimpleUdtKind = 1;
641constexpr uint32_t kUnknownUdtKind = 2;
642} // namespace
643
644static 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(K: static_cast<TypeLeafKind>(Kind));
655}
656
657static uint32_t getLongestTypeLeafName(const StatCollection &Stats) {
658 size_t L = 0;
659 for (const auto &Stat : Stats.Individual) {
660 std::string Label = getUdtStatLabel(Kind: Stat.first);
661 L = std::max(a: L, b: Label.size());
662 }
663 return static_cast<uint32_t>(L);
664}
665
666Error DumpOutputStyle::dumpUdtStats() {
667 printHeader(P, S: "S_UDT Record Stats");
668
669 if (File.isPdb() && !getPdb().hasPDBGlobalsStream()) {
670 printStreamNotPresent(StreamName: "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(Kind: SymbolKind::S_UDT, RecordSize: Sym.length());
687
688 UDTSym UDT = cantFail(ValOrErr: SymbolDeserializer::deserializeAs<UDTSym>(Symbol: 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(Index: 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(Str: "::");
706 if (Pos == StringRef::npos)
707 return;
708
709 StringRef Scope = UDT.Name.take_front(N: Pos);
710 if (Scope.empty() || !isValidNamespaceIdentifier(S: Scope))
711 return;
712
713 LongestNamespace = std::max(a: LongestNamespace, b: Scope.size());
714 NamespacedStats[Scope].update(RecordSize);
715 };
716
717 P.NewLine();
718
719 if (File.isPdb()) {
720 auto &SymbolRecords = cantFail(ValOrErr: 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(Offset: 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(Err: 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(Stats: UdtTargetStats);
746 size_t FieldWidth = std::max(a: LongestNamespace, b: 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(N: UdtStats.Totals.Count);
753 CD += (CD - 1) / 3;
754 CD = std::max(a: CD, b: CountHeader.size());
755
756 size_t SD = NumDigits(N: UdtStats.Totals.Size);
757 SD += (SD - 1) / 3;
758 SD = std::max(a: SD, b: SizeHeader.size());
759
760 uint32_t TableWidth = FieldWidth + 3 + CD + 2 + SD + 1;
761
762 P.formatLine(Fmt: "{0} | {1} {2}",
763 Items: fmt_align(Item: "Record Kind", Where: AlignStyle::Right, Amount: FieldWidth),
764 Items: fmt_align(Item&: CountHeader, Where: AlignStyle::Right, Amount: CD),
765 Items: fmt_align(Item&: SizeHeader, Where: AlignStyle::Right, Amount: SD));
766
767 P.formatLine(Fmt: "{0}", Items: fmt_repeat(Item: '-', Count: TableWidth));
768 for (const auto &Stat : UdtTargetStats.getStatsSortedBySize()) {
769 std::string Label = getUdtStatLabel(Kind: Stat.first);
770 P.formatLine(Fmt: "{0} | {1:N} {2:N}",
771 Items: fmt_align(Item&: Label, Where: AlignStyle::Right, Amount: FieldWidth),
772 Items: fmt_align(Item: Stat.second.Count, Where: AlignStyle::Right, Amount: CD),
773 Items: fmt_align(Item: Stat.second.Size, Where: AlignStyle::Right, Amount: SD));
774 }
775 P.formatLine(Fmt: "{0}", Items: fmt_repeat(Item: '-', Count: TableWidth));
776 P.formatLine(Fmt: "{0} | {1:N} {2:N}",
777 Items: fmt_align(Item: "Total (S_UDT)", Where: AlignStyle::Right, Amount: FieldWidth),
778 Items: fmt_align(Item&: UdtStats.Totals.Count, Where: AlignStyle::Right, Amount: CD),
779 Items: fmt_align(Item&: UdtStats.Totals.Size, Where: AlignStyle::Right, Amount: SD));
780 P.formatLine(Fmt: "{0}", Items: fmt_repeat(Item: '-', Count: 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(x: {.Key: Stat.getKey(), .Stat: Stat.second});
790 llvm::stable_sort(Range&: NamespacedStatsSorted,
791 C: [](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(Fmt: "namespace '{0}'", Vals: Stat.Key));
796 P.formatLine(Fmt: "{0} | {1:N} {2:N}",
797 Items: fmt_align(Item&: Label, Where: AlignStyle::Right, Amount: FieldWidth),
798 Items: fmt_align(Item: Stat.Stat.Count, Where: AlignStyle::Right, Amount: CD),
799 Items: fmt_align(Item: Stat.Stat.Size, Where: AlignStyle::Right, Amount: SD));
800 }
801 return Error::success();
802}
803
804static 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(a: ItemsLeft, b: 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(X: Line.getStartLine());
826 char Statement = Line.isStatement() ? ' ' : '!';
827 P.format(Fmt: "{0} {1:X-} {2} ",
828 Items: fmt_align(Item&: LineStr, Where: AlignStyle::Right, Amount: kMaxCharsPerLineNumber),
829 Items: fmt_align(Item: Start + LineIter->Offset, Where: AlignStyle::Right, Amount: 8, Fill: '0'),
830 Items&: Statement);
831 ++LineIter;
832 --ItemsLeft;
833 }
834 P.NewLine();
835 }
836}
837
838Error DumpOutputStyle::dumpLines() {
839 printHeader(P, S: "Lines");
840
841 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
842 printStreamNotPresent(StreamName: "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, HeaderScope: PrintScope{P, 4},
850 Callback: [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(Printer&: P, Offset: Block.NameIndex);
861 }
862
863 AutoIndent Indent(P, 2);
864 P.formatLine(Fmt: "{0:X-4}:{1:X-8}-{2:X-8}, ", Items&: Segment, Items&: Begin, Items&: End);
865 uint32_t Count = Block.LineNumbers.size();
866 if (Lines.hasColumnInfo())
867 P.format(Fmt: "line/column/addr entries = {0}", Items&: Count);
868 else
869 P.format(Fmt: "line/addr entries = {0}", Items&: Count);
870
871 P.NewLine();
872 typesetLinesAndColumns(P, Start: Begin, E: Block);
873 }
874 return Error::success();
875 });
876}
877
878Error DumpOutputStyle::dumpInlineeLines() {
879 printHeader(P, S: "Inlinee Lines");
880
881 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
882 printStreamNotPresent(StreamName: "DBI");
883 return Error::success();
884 }
885
886 return iterateModuleSubsections<DebugInlineeLinesSubsectionRef>(
887 File, HeaderScope: PrintScope{P, 2},
888 Callback: [this](uint32_t Modi, const SymbolGroup &Strings,
889 DebugInlineeLinesSubsectionRef &Lines) -> Error {
890 P.formatLine(Fmt: "{0,+8} | {1,+5} | {2}", Items: "Inlinee", Items: "Line", Items: "Source File");
891 for (const auto &Entry : Lines) {
892 P.formatLine(Fmt: "{0,+8} | {1,+5} | ", Items: Entry.Header->Inlinee,
893 Items: fmtle(Value: Entry.Header->SourceLineNum));
894 Strings.formatFromChecksumsOffset(Printer&: P, Offset: Entry.Header->FileID, Append: true);
895 for (const auto &ExtraFileID : Entry.ExtraFiles) {
896 P.formatLine(Fmt: " ");
897 Strings.formatFromChecksumsOffset(Printer&: P, Offset: ExtraFileID, Append: true);
898 }
899 }
900 P.NewLine();
901 return Error::success();
902 });
903}
904
905Error DumpOutputStyle::dumpXmi() {
906 printHeader(P, S: "Cross Module Imports");
907
908 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
909 printStreamNotPresent(StreamName: "DBI");
910 return Error::success();
911 }
912
913 return iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>(
914 File, HeaderScope: PrintScope{P, 2},
915 Callback: [this](uint32_t Modi, const SymbolGroup &Strings,
916 DebugCrossModuleImportsSubsectionRef &Imports) -> Error {
917 P.formatLine(Fmt: "{0,=32} | {1}", Items: "Imported Module", Items: "Type IDs");
918
919 for (const auto &Xmi : Imports) {
920 auto ExpectedModule =
921 Strings.getNameFromStringTable(Offset: Xmi.Header->ModuleNameOffset);
922 StringRef Module;
923 SmallString<32> ModuleStorage;
924 if (!ExpectedModule) {
925 Module = "(unknown module)";
926 consumeError(Err: ExpectedModule.takeError());
927 } else
928 Module = *ExpectedModule;
929 if (Module.size() > 32) {
930 ModuleStorage = "...";
931 ModuleStorage += Module.take_back(N: 32 - 3);
932 Module = ModuleStorage;
933 }
934 std::vector<std::string> TIs;
935 for (const auto I : Xmi.Imports)
936 TIs.push_back(x: std::string(formatv(Fmt: "{0,+10:X+}", Vals: fmtle(Value: I))));
937 std::string Result =
938 typesetItemList(Opts: TIs, IndentLevel: P.getIndentLevel() + 35, GroupSize: 12, Sep: " ");
939 P.formatLine(Fmt: "{0,+32} | {1}", Items&: Module, Items&: Result);
940 }
941 return Error::success();
942 });
943}
944
945Error DumpOutputStyle::dumpXme() {
946 printHeader(P, S: "Cross Module Exports");
947
948 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
949 printStreamNotPresent(StreamName: "DBI");
950 return Error::success();
951 }
952
953 return iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>(
954 File, HeaderScope: PrintScope{P, 2},
955 Callback: [this](uint32_t Modi, const SymbolGroup &Strings,
956 DebugCrossModuleExportsSubsectionRef &Exports) -> Error {
957 P.formatLine(Fmt: "{0,-10} | {1}", Items: "Local ID", Items: "Global ID");
958 for (const auto &Export : Exports) {
959 P.formatLine(Fmt: "{0,+10:X+} | {1}", Items: TypeIndex(Export.Local),
960 Items: TypeIndex(Export.Global));
961 }
962 return Error::success();
963 });
964}
965
966std::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
980Error DumpOutputStyle::dumpOldFpo(PDBFile &File) {
981 printHeader(P, S: "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(StreamName: "FPO");
988 return Error::success();
989 }
990
991 const FixedStreamArray<object::FpoData>& Records = Dbi.getOldFpoRecords();
992
993 P.printLine(T: " 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(Fmt: "{0:X-8} | {1,4} | {2,6} | {3,6} | {4,6} | {5,10} | {6,6} | "
998 "{7,7} | {8,9}",
999 Items: uint32_t(FD.Offset), Items: uint32_t(FD.Size), Items: uint32_t(FD.NumLocals),
1000 Items: uint32_t(FD.NumParams), Items: FD.getPrologSize(),
1001 Items: FD.getNumSavedRegs(), Items: FD.useBP(), Items: FD.hasSEH(),
1002 Items: formatFrameType(FT: FD.getFP()));
1003 }
1004 return Error::success();
1005}
1006
1007Error DumpOutputStyle::dumpNewFpo(PDBFile &File) {
1008 printHeader(P, S: "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(StreamName: "New FPO");
1015 return Error::success();
1016 }
1017
1018 const DebugFrameDataSubsectionRef& FDS = Dbi.getNewFpoRecords();
1019
1020 P.printLine(T: " 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(ID: FD.FrameFunc));
1030 P.formatLine(Fmt: "{0:X-8} | {1,4} | {2,6} | {3,6} | {4,5} | {5,6} | {6,10} | "
1031 "{7,7} | {8,9} | {9,5} | {10}",
1032 Items: uint32_t(FD.RvaStart), Items: uint32_t(FD.CodeSize),
1033 Items: uint32_t(FD.LocalSize), Items: uint32_t(FD.ParamsSize),
1034 Items: uint32_t(FD.MaxStackSize), Items: uint16_t(FD.PrologSize),
1035 Items: uint16_t(FD.SavedRegsSize), Items&: HasSEH, Items&: HasEH, Items&: IsFuncStart,
1036 Items&: Program);
1037 }
1038 return Error::success();
1039}
1040
1041Error DumpOutputStyle::dumpFpo() {
1042 if (!File.isPdb()) {
1043 printStreamNotValidForObj();
1044 return Error::success();
1045 }
1046
1047 PDBFile &File = getPdb();
1048 if (!File.hasPDBDbiStream()) {
1049 printStreamNotPresent(StreamName: "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
1060Error DumpOutputStyle::dumpStringTableFromPdb() {
1061 AutoIndent Indent(P);
1062 auto IS = getPdb().getStringTable();
1063 if (!IS) {
1064 P.formatLine(Fmt: "Not present in file");
1065 consumeError(Err: IS.takeError());
1066 return Error::success();
1067 }
1068
1069 if (opts::dump::DumpStringTable) {
1070 if (IS->name_ids().empty())
1071 P.formatLine(Fmt: "Empty");
1072 else {
1073 auto MaxID = llvm::max_element(Range: IS->name_ids());
1074 uint32_t Digits = NumDigits(N: *MaxID);
1075
1076 P.formatLine(Fmt: "{0} | {1}", Items: fmt_align(Item: "ID", Where: AlignStyle::Right, Amount: Digits),
1077 Items: "String");
1078
1079 std::vector<uint32_t> SortedIDs(IS->name_ids().begin(),
1080 IS->name_ids().end());
1081 llvm::sort(C&: SortedIDs);
1082 for (uint32_t I : SortedIDs) {
1083 auto ES = IS->getStringForID(ID: I);
1084 llvm::SmallString<32> Str;
1085 if (!ES) {
1086 consumeError(Err: ES.takeError());
1087 Str = "Error reading string";
1088 } else if (!ES->empty()) {
1089 Str.append(RHS: "'");
1090 Str.append(RHS: *ES);
1091 Str.append(RHS: "'");
1092 }
1093
1094 if (!Str.empty())
1095 P.formatLine(Fmt: "{0} | {1}", Items: fmt_align(Item&: I, Where: AlignStyle::Right, Amount: Digits),
1096 Items&: Str);
1097 }
1098 }
1099 }
1100
1101 if (opts::dump::DumpStringTableDetails) {
1102 P.NewLine();
1103 {
1104 P.printLine(T: "String Table Header:");
1105 AutoIndent Indent(P);
1106 P.formatLine(Fmt: "Signature: {0}", Items: IS->getSignature());
1107 P.formatLine(Fmt: "Hash Version: {0}", Items: IS->getHashVersion());
1108 P.formatLine(Fmt: "Name Buffer Size: {0}", Items: IS->getByteSize());
1109 P.NewLine();
1110 }
1111
1112 BinaryStreamRef NameBuffer = IS->getStringTable().getBuffer();
1113 ArrayRef<uint8_t> Contents;
1114 cantFail(Err: NameBuffer.readBytes(Offset: 0, Size: NameBuffer.getLength(), Buffer&: Contents));
1115 P.formatBinary(Label: "Name Buffer", Data: Contents, StartOffset: 0);
1116 P.NewLine();
1117 {
1118 P.printLine(T: "Hash Table:");
1119 AutoIndent Indent(P);
1120 P.formatLine(Fmt: "Bucket Count: {0}", Items: IS->name_ids().size());
1121 for (const auto &Entry : enumerate(First: IS->name_ids()))
1122 P.formatLine(Fmt: "Bucket[{0}] : {1}", Items: Entry.index(),
1123 Items: uint32_t(Entry.value()));
1124 P.formatLine(Fmt: "Name Count: {0}", Items: IS->getNameCount());
1125 }
1126 }
1127 return Error::success();
1128}
1129
1130Error DumpOutputStyle::dumpStringTableFromObj() {
1131 return iterateModuleSubsections<DebugStringTableSubsectionRef>(
1132 File, HeaderScope: PrintScope{P, 4},
1133 Callback: [&](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(Err: Reader.readCString(Dest&: Str));
1141 if (Str.empty())
1142 continue;
1143
1144 P.formatLine(Fmt: "{0} | {1}", Items: fmt_align(Item&: Offset, Where: AlignStyle::Right, Amount: 4),
1145 Items&: Str);
1146 }
1147 return Error::success();
1148 });
1149}
1150
1151Error DumpOutputStyle::dumpNamedStreams() {
1152 printHeader(P, S: "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(T: Entry.getKey());
1166 AutoIndent Indent2(P, 2);
1167 P.formatLine(Fmt: "Index: {0}", Items: Entry.getValue());
1168 P.formatLine(Fmt: "Size in bytes: {0}",
1169 Items: File.pdb().getStreamByteSize(StreamIndex: Entry.getValue()));
1170 }
1171
1172 return Error::success();
1173}
1174
1175Error DumpOutputStyle::dumpStringTable() {
1176 printHeader(P, S: "String Table");
1177
1178 if (File.isPdb())
1179 return dumpStringTableFromPdb();
1180
1181 return dumpStringTableFromObj();
1182}
1183
1184static 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(x: TI) != DepSet.end() || TI.isSimple() || TI.isNoneType())
1191 continue;
1192
1193 CVType Type = Types.getType(Index: TI);
1194 DepSet[TI] = Type;
1195 codeview::discoverTypeIndices(Type, Indices&: DepList);
1196 buildDepSet(Types, Indices: DepList, DepSet);
1197 }
1198}
1199
1200static void
1201dumpFullTypeStream(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(Fmt: "Showing {0:N} records", Items&: NumTypeRecords);
1208 uint32_t Width = NumDigits(N: 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, Callbacks&: V)) {
1214 Printer.formatLine(Fmt: "An error occurred dumping type records: {0}",
1215 Items: toString(E: std::move(EC)));
1216 }
1217}
1218
1219static 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(N: 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, Indices: TiList, DepSet);
1236
1237 Printer.formatLine(
1238 Fmt: "Showing {0:N} records and their dependents ({1:N} records total)",
1239 Items: TiList.size(), Items: DepSet.size());
1240
1241 for (auto &Dep : DepSet) {
1242 if (auto EC = codeview::visitTypeRecord(Record&: Dep.second, Index: Dep.first, Callbacks&: V))
1243 Printer.formatLine(Fmt: "An error occurred dumping type record {0}: {1}",
1244 Items: Dep.first, Items: toString(E: std::move(EC)));
1245 }
1246 } else {
1247 Printer.formatLine(Fmt: "Showing {0:N} records.", Items: TiList.size());
1248
1249 for (const auto &I : TiList) {
1250 TypeIndex TI(I);
1251 if (TI.isSimple()) {
1252 Printer.formatLine(Fmt: "{0} | {1}", Items: fmt_align(Item: I, Where: AlignStyle::Right, Amount: Width),
1253 Items: Types.getTypeName(Index: TI));
1254 } else if (std::optional<CVType> Type = Types.tryGetType(Index: TI)) {
1255 if (auto EC = codeview::visitTypeRecord(Record&: *Type, Index: TI, Callbacks&: V))
1256 Printer.formatLine(Fmt: "An error occurred dumping type record {0}: {1}",
1257 Items&: TI, Items: toString(E: std::move(EC)));
1258 } else {
1259 Printer.formatLine(Fmt: "Type {0} doesn't exist in TPI stream", Items&: TI);
1260 }
1261 }
1262 }
1263}
1264
1265Error 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, S: "Types (.debug$T)");
1278 else if (SectionName == ".debug$P")
1279 printHeader(P, S: "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(Dest&: Magic))
1290 return EC;
1291 if (Magic != COFF::DEBUG_SECTION_MAGIC)
1292 return make_error<StringError>(Args: "Invalid CodeView debug section.",
1293 Args: inconvertibleErrorCode());
1294
1295 Types.reset(Reader, RecordCountHint: 100);
1296
1297 if (opts::dump::DumpTypes) {
1298 dumpFullTypeStream(Printer&: P, Types, RefTracker: RefTracker.get(), NumTypeRecords: 0, NumHashBuckets: 0, HashValues: {}, Stream: nullptr,
1299 Bytes: opts::dump::DumpTypeData, Extras: 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(Fmt: "Local / Global hashes:");
1306 TypeIndex TI(TypeIndex::FirstNonSimpleIndex);
1307 for (auto H : zip(t&: LocalHashes, u&: GlobalHashes)) {
1308 AutoIndent Indent2(P);
1309 LocallyHashedType &L = std::get<0>(t&: H);
1310 GloballyHashedType &G = std::get<1>(t&: H);
1311
1312 P.formatLine(Fmt: "TI: {0}, LocalHash: {1:X}, GlobalHash: {2}", Items&: TI, Items&: L, Items&: G);
1313
1314 ++TI;
1315 }
1316 P.NewLine();
1317 }
1318 }
1319
1320 return Error::success();
1321}
1322
1323Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
1324 assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);
1325
1326 if (StreamIdx == StreamTPI) {
1327 printHeader(P, S: "Types (TPI Stream)");
1328 } else if (StreamIdx == StreamIPI) {
1329 printHeader(P, S: "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(first: opts::dump::DumpTypeIndex.begin(),
1345 last: 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(first: opts::dump::DumpIdIndex.begin(),
1352 last: opts::dump::DumpIdIndex.end());
1353 }
1354
1355 if (!Present) {
1356 printStreamNotPresent(StreamName: 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(Printer&: P, Types, RefTracker: MaybeTracker, NumTypeRecords: Stream.getNumTypeRecords(),
1378 NumHashBuckets: Stream.getNumHashBuckets(), HashValues: Stream.getHashValues(),
1379 Stream: &Stream, Bytes: DumpBytes, Extras: DumpExtras);
1380 else {
1381 std::vector<TypeIndex> TiList(Indices.begin(), Indices.end());
1382 dumpPartialTypeStream(Printer&: P, Types, RefTracker: MaybeTracker, Stream, TiList, Bytes: DumpBytes,
1383 Extras: DumpExtras, Deps: opts::dump::DumpTypeDependents);
1384 }
1385 }
1386
1387 if (DumpExtras) {
1388 P.NewLine();
1389
1390 P.formatLine(Fmt: "Header Version: {0}",
1391 Items: static_cast<uint32_t>(Stream.getTpiVersion()));
1392 P.formatLine(Fmt: "Hash Stream Index: {0}", Items: Stream.getTypeHashStreamIndex());
1393 P.formatLine(Fmt: "Aux Hash Stream Index: {0}",
1394 Items: Stream.getTypeHashStreamAuxIndex());
1395 P.formatLine(Fmt: "Hash Key Size: {0}", Items: Stream.getHashKeySize());
1396 P.formatLine(Fmt: "Num Hash Buckets: {0}", Items: Stream.getNumHashBuckets());
1397
1398 auto IndexOffsets = Stream.getTypeIndexOffsets();
1399 P.formatLine(Fmt: "Type Index Offsets:");
1400 for (const auto &IO : IndexOffsets) {
1401 AutoIndent Indent2(P);
1402 P.formatLine(Fmt: "TI: {0}, Offset: {1}", Items: IO.Type, Items: fmtle(Value: IO.Offset));
1403 }
1404
1405 if (getPdb().hasPDBStringTable()) {
1406 P.NewLine();
1407 P.formatLine(Fmt: "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(ID: A.first);
1413 TypeIndex TI(A.second);
1414 if (ExpectedStr)
1415 P.formatLine(Fmt: "`{0}` -> {1}", Items&: *ExpectedStr, Items&: TI);
1416 else {
1417 P.formatLine(Fmt: "unknown str id ({0}) -> {1}", Items: A.first, Items&: TI);
1418 consumeError(Err: ExpectedStr.takeError());
1419 }
1420 }
1421 }
1422 }
1423 return Error::success();
1424}
1425
1426Error DumpOutputStyle::dumpModuleSymsForObj() {
1427 printHeader(P, S: "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(Callbacks&: Deserializer);
1438 Pipeline.addCallbackToPipeline(Callbacks&: Dumper);
1439 CVSymbolVisitor Visitor(Pipeline);
1440
1441 return iterateModuleSubsections<DebugSymbolsSubsectionRef>(
1442 File, HeaderScope: PrintScope{P, 2},
1443 Callback: [&](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(Record&: Symbol)) {
1448 return EC;
1449 }
1450 }
1451 return Error::success();
1452 });
1453}
1454
1455Error DumpOutputStyle::dumpModuleSymsForPdb() {
1456 printHeader(P, S: "Symbols");
1457
1458 if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
1459 printStreamNotPresent(StreamName: "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 Input&: File, HeaderScope: PrintScope{P, 2},
1470 Callback: [&](uint32_t I, const SymbolGroup &Strings) -> Error {
1471 auto ExpectedModS = getModuleDebugStream(File&: File.pdb(), Index: I);
1472 if (!ExpectedModS) {
1473 P.formatLine(Fmt: "Error loading module stream {0}. {1}", Items&: I,
1474 Items: toString(E: 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(Callbacks&: Deserializer);
1486 Pipeline.addCallbackToPipeline(Callbacks&: 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(Symbols: ModS.getSymbolArray(),
1495 Filter)) {
1496 P.formatLine(Fmt: "Error while processing symbol records. {0}",
1497 Items: toString(E: std::move(EC)));
1498 return EC;
1499 }
1500 } else if (auto EC = Visitor.visitSymbolStream(Symbols: ModS.getSymbolArray(),
1501 InitialOffset: SS.Offset)) {
1502 P.formatLine(Fmt: "Error while processing symbol records. {0}",
1503 Items: toString(E: std::move(EC)));
1504 return EC;
1505 }
1506 return Error::success();
1507 });
1508}
1509
1510Error DumpOutputStyle::dumpTypeRefStats() {
1511 printHeader(P, S: "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(Prev: *TI)) {
1523 CVType Type = File.types().getType(Index: *TI);
1524 TotalBytes += Type.length();
1525 if (RefTracker->isTypeReferenced(TI: *TI)) {
1526 ++RefRecs;
1527 RefBytes += Type.length();
1528 }
1529 }
1530
1531 P.formatLine(Fmt: "Records referenced: {0:N} / {1:N} {2:P}", Items&: RefRecs, Items&: TotalRecs,
1532 Items: (double)RefRecs / TotalRecs);
1533 P.formatLine(Fmt: "Bytes referenced: {0:N} / {1:N} {2:P}", Items&: RefBytes, Items&: TotalBytes,
1534 Items: (double)RefBytes / TotalBytes);
1535
1536 return Error::success();
1537}
1538
1539Error DumpOutputStyle::dumpGSIRecords() {
1540 printHeader(P, S: "GSI Records");
1541
1542 if (File.isObj()) {
1543 printStreamNotValidForObj();
1544 return Error::success();
1545 }
1546
1547 if (!getPdb().hasPDBSymbolStream()) {
1548 printStreamNotPresent(StreamName: "GSI Common Symbol");
1549 return Error::success();
1550 }
1551
1552 AutoIndent Indent(P);
1553
1554 auto &Records = cantFail(ValOrErr: getPdb().getPDBSymbolStream());
1555 auto &Types = File.types();
1556 auto &Ids = File.ids();
1557
1558 P.printLine(T: "Records");
1559 SymbolVisitorCallbackPipeline Pipeline;
1560 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
1561 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);
1562
1563 Pipeline.addCallbackToPipeline(Callbacks&: Deserializer);
1564 Pipeline.addCallbackToPipeline(Callbacks&: Dumper);
1565 CVSymbolVisitor Visitor(Pipeline);
1566
1567 BinaryStreamRef SymStream = Records.getSymbolArray().getUnderlyingStream();
1568 if (auto E = Visitor.visitSymbolStream(Symbols: Records.getSymbolArray(), InitialOffset: 0))
1569 return E;
1570 return Error::success();
1571}
1572
1573Error DumpOutputStyle::dumpGlobals() {
1574 printHeader(P, S: "Global Symbols");
1575
1576 if (File.isObj()) {
1577 printStreamNotValidForObj();
1578 return Error::success();
1579 }
1580
1581 if (!getPdb().hasPDBGlobalsStream()) {
1582 printStreamNotPresent(StreamName: "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, HashExtras: opts::dump::DumpGlobalExtras));
1593 } else {
1594 SymbolStream &SymRecords = cantFail(ValOrErr: 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(Callbacks&: Deserializer);
1603 Pipeline.addCallbackToPipeline(Callbacks&: 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(Fmt: "Global Name `{0}`", Items&: Name);
1610 std::vector<ResultEntryType> Results =
1611 Globals.findRecordsByName(Name, Symbols: SymRecords);
1612 if (Results.empty()) {
1613 AutoIndent Indent(P);
1614 P.printLine(T: "(no matching records found)");
1615 continue;
1616 }
1617
1618 for (ResultEntryType Result : Results) {
1619 if (auto E = Visitor.visitSymbolRecord(Record&: Result.second, Offset: Result.first))
1620 return E;
1621 }
1622 }
1623 }
1624 return Error::success();
1625}
1626
1627Error DumpOutputStyle::dumpPublics() {
1628 printHeader(P, S: "Public Symbols");
1629
1630 if (File.isObj()) {
1631 printStreamNotValidForObj();
1632 return Error::success();
1633 }
1634
1635 if (!getPdb().hasPDBPublicsStream()) {
1636 printStreamNotPresent(StreamName: "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(T: "Publics Header");
1647 AutoIndent Indent(P);
1648 P.formatLine(Fmt: "sym hash = {0}, thunk table addr = {1}", Items: Publics.getSymHash(),
1649 Items: formatSegmentOffset(Segment: Publics.getThunkTableSection(),
1650 Offset: Publics.getThunkTableOffset()));
1651 }
1652 Err(dumpSymbolsFromGSI(Table: PublicsTable, HashExtras: 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(Fmt: "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(Fmt: "off = {0}", Items&: Addr);
1664 }
1665
1666 // The thunk map is optional debug info used for ILT thunks.
1667 if (!Publics.getThunkMap().empty()) {
1668 P.formatLine(Fmt: "Thunk Map");
1669 AutoIndent Indent2(P);
1670 for (uint32_t Addr : Publics.getThunkMap())
1671 P.formatLine(Fmt: "{0:x8}", Items&: 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(Fmt: "Section Offsets");
1678 AutoIndent Indent2(P);
1679 for (const SectionOffset &SO : Publics.getSectionOffsets())
1680 P.formatLine(Fmt: "{0:x4}:{1:x8}", Items: uint16_t(SO.Isect), Items: uint32_t(SO.Off));
1681 }
1682
1683 return Error::success();
1684}
1685
1686Error 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(T: "GSI Header");
1696 AutoIndent Indent(P);
1697 P.formatLine(Fmt: "sig = {0:X}, hdr = {1:X}, hr size = {2}, num buckets = {3}",
1698 Items: Table.getVerSignature(), Items: Table.getVerHeader(),
1699 Items: Table.getHashRecordSize(), Items: Table.getNumBuckets());
1700 }
1701
1702 {
1703 P.printLine(T: "Records");
1704 SymbolVisitorCallbackPipeline Pipeline;
1705 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
1706 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);
1707
1708 Pipeline.addCallbackToPipeline(Callbacks&: Deserializer);
1709 Pipeline.addCallbackToPipeline(Callbacks&: 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(Stream: SymStream, Offset: PubSymOff);
1717 if (!Sym)
1718 return Sym.takeError();
1719 if (auto E = Visitor.visitSymbolRecord(Record&: *Sym, Offset: 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(Fmt: "Hash Entries");
1727 {
1728 AutoIndent Indent2(P);
1729 for (const PSHashRecord &HR : Table.HashRecords)
1730 P.formatLine(Fmt: "off = {0}, refcnt = {1}", Items: uint32_t(HR.Off),
1731 Items: uint32_t(HR.CRef));
1732 }
1733
1734 P.formatLine(Fmt: "Hash Buckets");
1735 {
1736 AutoIndent Indent2(P);
1737 for (uint32_t Hash : Table.HashBuckets)
1738 P.formatLine(Fmt: "{0:x8}", Items&: Hash);
1739 }
1740 }
1741
1742 return Error::success();
1743}
1744
1745static 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, GroupSize: 4, Sep: " | ");
1759}
1760
1761Error DumpOutputStyle::dumpSectionHeaders() {
1762 dumpSectionHeaders(Label: "Section Headers", Type: DbgHeaderType::SectionHdr);
1763 dumpSectionHeaders(Label: "Original Section Headers", Type: DbgHeaderType::SectionHdrOrig);
1764 return Error::success();
1765}
1766
1767void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) {
1768 printHeader(P, S: Label);
1769
1770 if (File.isObj()) {
1771 printStreamNotValidForObj();
1772 return;
1773 }
1774
1775 if (!getPdb().hasPDBDbiStream()) {
1776 printStreamNotPresent(StreamName: "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(File&: getPdb(), Type);
1785 if (!ExpectedHeaders) {
1786 P.printLine(T: toString(E: ExpectedHeaders.takeError()));
1787 return;
1788 }
1789 std::tie(args&: Stream, args&: Headers) = std::move(*ExpectedHeaders);
1790
1791 uint32_t I = 1;
1792 for (const auto &Header : Headers) {
1793 P.NewLine();
1794 P.formatLine(Fmt: "SECTION HEADER #{0}", Items&: I);
1795 P.formatLine(Fmt: "{0,8} name", Items: Header.Name);
1796 P.formatLine(Fmt: "{0,8:X-} virtual size", Items: uint32_t(Header.VirtualSize));
1797 P.formatLine(Fmt: "{0,8:X-} virtual address", Items: uint32_t(Header.VirtualAddress));
1798 P.formatLine(Fmt: "{0,8:X-} size of raw data", Items: uint32_t(Header.SizeOfRawData));
1799 P.formatLine(Fmt: "{0,8:X-} file pointer to raw data",
1800 Items: uint32_t(Header.PointerToRawData));
1801 P.formatLine(Fmt: "{0,8:X-} file pointer to relocation table",
1802 Items: uint32_t(Header.PointerToRelocations));
1803 P.formatLine(Fmt: "{0,8:X-} file pointer to line numbers",
1804 Items: uint32_t(Header.PointerToLinenumbers));
1805 P.formatLine(Fmt: "{0,8:X-} number of relocations",
1806 Items: uint32_t(Header.NumberOfRelocations));
1807 P.formatLine(Fmt: "{0,8:X-} number of line numbers",
1808 Items: uint32_t(Header.NumberOfLinenumbers));
1809 P.formatLine(Fmt: "{0,8:X-} flags", Items: uint32_t(Header.Characteristics));
1810 AutoIndent IndentMore(P, 9);
1811 P.formatLine(Fmt: "{0}", Items: formatSectionCharacteristics(
1812 IndentLevel: P.getIndentLevel(), C: Header.Characteristics, FlagsPerLine: 1, Separator: ""));
1813 ++I;
1814 }
1815}
1816
1817Error DumpOutputStyle::dumpSectionContribs() {
1818 printHeader(P, S: "Section Contributions");
1819
1820 if (File.isObj()) {
1821 printStreamNotValidForObj();
1822 return Error::success();
1823 }
1824
1825 if (!getPdb().hasPDBDbiStream()) {
1826 printStreamNotPresent(StreamName: "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(Range&: Names, C: [](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, SectionNames: Names, FieldWidth: MaxNameLen);
1845 }
1846 void visit(const SectionContrib2 &SC) override {
1847 dumpSectionContrib(P, SC, SectionNames: Names, FieldWidth: MaxNameLen);
1848 }
1849
1850 private:
1851 LinePrinter &P;
1852 uint32_t MaxNameLen;
1853 ArrayRef<std::string> Names;
1854 };
1855
1856 auto NamesOrErr = getSectionNames(File&: getPdb());
1857 if (!NamesOrErr)
1858 return NamesOrErr.takeError();
1859 ArrayRef<std::string> Names = *NamesOrErr;
1860 Visitor V(P, Names);
1861 Dbi.visitSectionContributions(Visitor&: V);
1862 return Error::success();
1863}
1864
1865Error DumpOutputStyle::dumpSectionMap() {
1866 printHeader(P, S: "Section Map");
1867
1868 if (File.isObj()) {
1869 printStreamNotValidForObj();
1870 return Error::success();
1871 }
1872
1873 if (!getPdb().hasPDBDbiStream()) {
1874 printStreamNotPresent(StreamName: "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 Fmt: "Section {0:4} | ovl = {1}, group = {2}, frame = {3}, name = {4}", Items&: I,
1887 Items: fmtle(Value: M.Ovl), Items: fmtle(Value: M.Group), Items: fmtle(Value: M.Frame), Items: fmtle(Value: M.SecName));
1888 P.formatLine(Fmt: " class = {0}, offset = {1}, size = {2}",
1889 Items: fmtle(Value: M.ClassName), Items: fmtle(Value: M.Offset), Items: fmtle(Value: M.SecByteLength));
1890 P.formatLine(Fmt: " flags = {0}",
1891 Items: formatSegMapDescriptorFlag(
1892 IndentLevel: P.getIndentLevel() + 13,
1893 Flags: static_cast<OMFSegDescFlags>(uint16_t(M.Flags))));
1894 ++I;
1895 }
1896 return Error::success();
1897}
1898

source code of llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp