1 //===- BytesOutputStyle.cpp ----------------------------------- *- C++ --*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "BytesOutputStyle.h"
11 #include "FormatUtil.h"
12 #include "StreamUtil.h"
13 #include "llvm-pdbutil.h"
15 #include "llvm/DebugInfo/CodeView/Formatters.h"
16 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
17 #include "llvm/DebugInfo/MSF/MSFCommon.h"
18 #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
19 #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
20 #include "llvm/DebugInfo/PDB/Native/InfoStream.h"
21 #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
22 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
23 #include "llvm/DebugInfo/PDB/Native/RawError.h"
24 #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
25 #include "llvm/Support/BinaryStreamReader.h"
26 #include "llvm/Support/FormatAdapters.h"
27 #include "llvm/Support/FormatVariadic.h"
30 using namespace llvm::codeview
;
31 using namespace llvm::msf
;
32 using namespace llvm::pdb
;
42 static Expected
<StreamSpec
> parseStreamSpec(StringRef Str
) {
44 if (Str
.consumeInteger(0, Result
.SI
))
45 return make_error
<RawError
>(raw_error_code::invalid_format
,
46 "Invalid Stream Specification");
47 if (Str
.consume_front(":")) {
48 if (Str
.consumeInteger(0, Result
.Begin
))
49 return make_error
<RawError
>(raw_error_code::invalid_format
,
50 "Invalid Stream Specification");
52 if (Str
.consume_front("@")) {
53 if (Str
.consumeInteger(0, Result
.Size
))
54 return make_error
<RawError
>(raw_error_code::invalid_format
,
55 "Invalid Stream Specification");
59 return make_error
<RawError
>(raw_error_code::invalid_format
,
60 "Invalid Stream Specification");
64 static SmallVector
<StreamSpec
, 2> parseStreamSpecs(LinePrinter
&P
) {
65 SmallVector
<StreamSpec
, 2> Result
;
67 for (auto &Str
: opts::bytes::DumpStreamData
) {
68 auto ESS
= parseStreamSpec(Str
);
70 P
.formatLine("Error parsing stream spec {0}: {1}", Str
,
71 toString(ESS
.takeError()));
74 Result
.push_back(*ESS
);
79 static void printHeader(LinePrinter
&P
, const Twine
&S
) {
81 P
.formatLine("{0,=60}", S
);
82 P
.formatLine("{0}", fmt_repeat('=', 60));
85 BytesOutputStyle::BytesOutputStyle(PDBFile
&File
)
86 : File(File
), P(2, false, outs()) {}
88 Error
BytesOutputStyle::dump() {
90 if (opts::bytes::DumpBlockRange
.hasValue()) {
91 auto &R
= *opts::bytes::DumpBlockRange
;
92 uint32_t Max
= R
.Max
.getValueOr(R
.Min
);
95 return make_error
<StringError
>(
96 "Invalid block range specified. Max < Min",
97 inconvertibleErrorCode());
98 if (Max
>= File
.getBlockCount())
99 return make_error
<StringError
>(
100 "Invalid block range specified. Requested block out of bounds",
101 inconvertibleErrorCode());
103 dumpBlockRanges(R
.Min
, Max
);
107 if (opts::bytes::DumpByteRange
.hasValue()) {
108 auto &R
= *opts::bytes::DumpByteRange
;
109 uint32_t Max
= R
.Max
.getValueOr(File
.getFileSize());
112 return make_error
<StringError
>("Invalid byte range specified. Max < Min",
113 inconvertibleErrorCode());
114 if (Max
>= File
.getFileSize())
115 return make_error
<StringError
>(
116 "Invalid byte range specified. Requested byte larger than file size",
117 inconvertibleErrorCode());
119 dumpByteRanges(R
.Min
, Max
);
123 if (opts::bytes::Fpm
) {
128 if (!opts::bytes::DumpStreamData
.empty()) {
133 if (opts::bytes::NameMap
) {
138 if (opts::bytes::SectionContributions
) {
139 dumpSectionContributions();
143 if (opts::bytes::SectionMap
) {
148 if (opts::bytes::ModuleInfos
) {
153 if (opts::bytes::FileInfo
) {
158 if (opts::bytes::TypeServerMap
) {
163 if (opts::bytes::ECData
) {
168 if (!opts::bytes::TypeIndex
.empty()) {
169 dumpTypeIndex(StreamTPI
, opts::bytes::TypeIndex
);
173 if (!opts::bytes::IdIndex
.empty()) {
174 dumpTypeIndex(StreamIPI
, opts::bytes::IdIndex
);
178 if (opts::bytes::ModuleSyms
) {
183 if (opts::bytes::ModuleC11
) {
188 if (opts::bytes::ModuleC13
) {
193 return Error::success();
196 void BytesOutputStyle::dumpNameMap() {
197 printHeader(P
, "Named Stream Map");
199 AutoIndent
Indent(P
);
201 auto &InfoS
= Err(File
.getPDBInfoStream());
202 BinarySubstreamRef NS
= InfoS
.getNamedStreamsBuffer();
203 auto Layout
= File
.getStreamLayout(StreamPDB
);
204 P
.formatMsfStreamData("Named Stream Map", File
, Layout
, NS
);
207 void BytesOutputStyle::dumpBlockRanges(uint32_t Min
, uint32_t Max
) {
208 printHeader(P
, "MSF Blocks");
210 AutoIndent
Indent(P
);
211 for (uint32_t I
= Min
; I
<= Max
; ++I
) {
213 Base
*= File
.getBlockSize();
215 auto ExpectedData
= File
.getBlockData(I
, File
.getBlockSize());
217 P
.formatLine("Could not get block {0}. Reason = {1}", I
,
218 toString(ExpectedData
.takeError()));
221 std::string Label
= formatv("Block {0}", I
).str();
222 P
.formatBinary(Label
, *ExpectedData
, Base
, 0);
226 void BytesOutputStyle::dumpSectionContributions() {
227 printHeader(P
, "Section Contributions");
229 AutoIndent
Indent(P
);
231 auto &DbiS
= Err(File
.getPDBDbiStream());
232 BinarySubstreamRef NS
= DbiS
.getSectionContributionData();
233 auto Layout
= File
.getStreamLayout(StreamDBI
);
234 P
.formatMsfStreamData("Section Contributions", File
, Layout
, NS
);
237 void BytesOutputStyle::dumpSectionMap() {
238 printHeader(P
, "Section Map");
240 AutoIndent
Indent(P
);
242 auto &DbiS
= Err(File
.getPDBDbiStream());
243 BinarySubstreamRef NS
= DbiS
.getSecMapSubstreamData();
244 auto Layout
= File
.getStreamLayout(StreamDBI
);
245 P
.formatMsfStreamData("Section Map", File
, Layout
, NS
);
248 void BytesOutputStyle::dumpModuleInfos() {
249 printHeader(P
, "Module Infos");
251 AutoIndent
Indent(P
);
253 auto &DbiS
= Err(File
.getPDBDbiStream());
254 BinarySubstreamRef NS
= DbiS
.getModiSubstreamData();
255 auto Layout
= File
.getStreamLayout(StreamDBI
);
256 P
.formatMsfStreamData("Module Infos", File
, Layout
, NS
);
259 void BytesOutputStyle::dumpFileInfo() {
260 printHeader(P
, "File Info");
262 AutoIndent
Indent(P
);
264 auto &DbiS
= Err(File
.getPDBDbiStream());
265 BinarySubstreamRef NS
= DbiS
.getFileInfoSubstreamData();
266 auto Layout
= File
.getStreamLayout(StreamDBI
);
267 P
.formatMsfStreamData("File Info", File
, Layout
, NS
);
270 void BytesOutputStyle::dumpTypeServerMap() {
271 printHeader(P
, "Type Server Map");
273 AutoIndent
Indent(P
);
275 auto &DbiS
= Err(File
.getPDBDbiStream());
276 BinarySubstreamRef NS
= DbiS
.getTypeServerMapSubstreamData();
277 auto Layout
= File
.getStreamLayout(StreamDBI
);
278 P
.formatMsfStreamData("Type Server Map", File
, Layout
, NS
);
281 void BytesOutputStyle::dumpECData() {
282 printHeader(P
, "Edit and Continue Data");
284 AutoIndent
Indent(P
);
286 auto &DbiS
= Err(File
.getPDBDbiStream());
287 BinarySubstreamRef NS
= DbiS
.getECSubstreamData();
288 auto Layout
= File
.getStreamLayout(StreamDBI
);
289 P
.formatMsfStreamData("Edit and Continue Data", File
, Layout
, NS
);
292 void BytesOutputStyle::dumpTypeIndex(uint32_t StreamIdx
,
293 ArrayRef
<uint32_t> Indices
) {
294 assert(StreamIdx
== StreamTPI
|| StreamIdx
== StreamIPI
);
295 assert(!Indices
.empty());
297 bool IsTpi
= (StreamIdx
== StreamTPI
);
299 StringRef Label
= IsTpi
? "Type (TPI) Records" : "Index (IPI) Records";
300 printHeader(P
, Label
);
301 auto &Stream
= Err(IsTpi
? File
.getPDBTpiStream() : File
.getPDBIpiStream());
303 AutoIndent
Indent(P
);
305 auto Substream
= Stream
.getTypeRecordsSubstream();
306 auto &Types
= Err(initializeTypes(StreamIdx
));
307 auto Layout
= File
.getStreamLayout(StreamIdx
);
308 for (const auto &Id
: Indices
) {
310 if (TI
.toArrayIndex() >= Types
.capacity()) {
311 P
.formatLine("Error: TypeIndex {0} does not exist", TI
);
315 auto Type
= Types
.getType(TI
);
316 uint32_t Offset
= Types
.getOffsetOfType(TI
);
317 auto OneType
= Substream
.slice(Offset
, Type
.length());
318 P
.formatMsfStreamData(formatv("Type {0}", TI
).str(), File
, Layout
, OneType
);
322 template <typename CallbackT
>
323 static void iterateOneModule(PDBFile
&File
, LinePrinter
&P
,
324 const DbiModuleList
&Modules
, uint32_t I
,
325 uint32_t Digits
, uint32_t IndentLevel
,
326 CallbackT Callback
) {
327 if (I
>= Modules
.getModuleCount()) {
328 P
.formatLine("Mod {0:4} | Invalid module index ",
329 fmt_align(I
, AlignStyle::Right
, std::max(Digits
, 4U)));
333 auto Modi
= Modules
.getModuleDescriptor(I
);
334 P
.formatLine("Mod {0:4} | `{1}`: ",
335 fmt_align(I
, AlignStyle::Right
, std::max(Digits
, 4U)),
336 Modi
.getModuleName());
338 uint16_t ModiStream
= Modi
.getModuleStreamIndex();
339 AutoIndent
Indent2(P
, IndentLevel
);
340 if (ModiStream
== kInvalidStreamIndex
)
343 auto ModStreamData
= File
.createIndexedStream(ModiStream
);
344 ModuleDebugStreamRef
ModStream(Modi
, std::move(ModStreamData
));
345 if (auto EC
= ModStream
.reload()) {
346 P
.formatLine("Could not parse debug information.");
349 auto Layout
= File
.getStreamLayout(ModiStream
);
350 Callback(I
, ModStream
, Layout
);
353 template <typename CallbackT
>
354 static void iterateModules(PDBFile
&File
, LinePrinter
&P
, uint32_t IndentLevel
,
355 CallbackT Callback
) {
356 AutoIndent
Indent(P
);
357 if (!File
.hasPDBDbiStream()) {
358 P
.formatLine("DBI Stream not present");
362 ExitOnError
Err("Unexpected error processing modules");
364 auto &Stream
= Err(File
.getPDBDbiStream());
366 const DbiModuleList
&Modules
= Stream
.modules();
368 if (opts::bytes::ModuleIndex
.getNumOccurrences() > 0) {
369 iterateOneModule(File
, P
, Modules
, opts::bytes::ModuleIndex
, 1, IndentLevel
,
372 uint32_t Count
= Modules
.getModuleCount();
373 uint32_t Digits
= NumDigits(Count
);
374 for (uint32_t I
= 0; I
< Count
; ++I
) {
375 iterateOneModule(File
, P
, Modules
, I
, Digits
, IndentLevel
, Callback
);
380 void BytesOutputStyle::dumpModuleSyms() {
381 printHeader(P
, "Module Symbols");
383 AutoIndent
Indent(P
);
385 iterateModules(File
, P
, 2,
386 [this](uint32_t Modi
, const ModuleDebugStreamRef
&Stream
,
387 const MSFStreamLayout
&Layout
) {
388 auto Symbols
= Stream
.getSymbolsSubstream();
389 P
.formatMsfStreamData("Symbols", File
, Layout
, Symbols
);
393 void BytesOutputStyle::dumpModuleC11() {
394 printHeader(P
, "C11 Debug Chunks");
396 AutoIndent
Indent(P
);
398 iterateModules(File
, P
, 2,
399 [this](uint32_t Modi
, const ModuleDebugStreamRef
&Stream
,
400 const MSFStreamLayout
&Layout
) {
401 auto Chunks
= Stream
.getC11LinesSubstream();
402 P
.formatMsfStreamData("C11 Debug Chunks", File
, Layout
,
407 void BytesOutputStyle::dumpModuleC13() {
408 printHeader(P
, "Debug Chunks");
410 AutoIndent
Indent(P
);
414 [this](uint32_t Modi
, const ModuleDebugStreamRef
&Stream
,
415 const MSFStreamLayout
&Layout
) {
416 auto Chunks
= Stream
.getC13LinesSubstream();
417 if (opts::bytes::SplitChunks
) {
418 for (const auto &SS
: Stream
.subsections()) {
419 BinarySubstreamRef ThisChunk
;
420 std::tie(ThisChunk
, Chunks
) = Chunks
.split(SS
.getRecordLength());
421 P
.formatMsfStreamData(formatChunkKind(SS
.kind()), File
, Layout
,
425 P
.formatMsfStreamData("Debug Chunks", File
, Layout
, Chunks
);
430 void BytesOutputStyle::dumpByteRanges(uint32_t Min
, uint32_t Max
) {
431 printHeader(P
, "MSF Bytes");
433 AutoIndent
Indent(P
);
435 BinaryStreamReader
Reader(File
.getMsfBuffer());
436 ArrayRef
<uint8_t> Data
;
437 consumeError(Reader
.skip(Min
));
438 uint32_t Size
= Max
- Min
+ 1;
439 auto EC
= Reader
.readBytes(Data
, Size
);
441 consumeError(std::move(EC
));
442 P
.formatBinary("Bytes", Data
, Min
);
445 Expected
<codeview::LazyRandomTypeCollection
&>
446 BytesOutputStyle::initializeTypes(uint32_t StreamIdx
) {
447 auto &TypeCollection
= (StreamIdx
== StreamTPI
) ? TpiTypes
: IpiTypes
;
449 return *TypeCollection
;
451 auto Tpi
= (StreamIdx
== StreamTPI
) ? File
.getPDBTpiStream()
452 : File
.getPDBIpiStream();
454 return Tpi
.takeError();
456 auto &Types
= Tpi
->typeArray();
457 uint32_t Count
= Tpi
->getNumTypeRecords();
458 auto Offsets
= Tpi
->getTypeIndexOffsets();
460 std::make_unique
<LazyRandomTypeCollection
>(Types
, Count
, Offsets
);
462 return *TypeCollection
;
465 void BytesOutputStyle::dumpFpm() {
466 printHeader(P
, "Free Page Map");
468 msf::MSFStreamLayout FpmLayout
= File
.getFpmStreamLayout();
469 P
.formatMsfStreamBlocks(File
, FpmLayout
);
472 void BytesOutputStyle::dumpStreamBytes() {
473 if (StreamPurposes
.empty())
474 discoverStreamPurposes(File
, StreamPurposes
);
476 printHeader(P
, "Stream Data");
477 ExitOnError
Err("Unexpected error reading stream data");
479 auto Specs
= parseStreamSpecs(P
);
481 for (const auto &Spec
: Specs
) {
482 AutoIndent
Indent(P
);
483 if (Spec
.SI
>= StreamPurposes
.size()) {
484 P
.formatLine("Stream {0}: Not present", Spec
.SI
);
487 P
.formatMsfStreamData("Data", File
, Spec
.SI
,
488 StreamPurposes
[Spec
.SI
].getShortName(), Spec
.Begin
,