1//===- BytesOutputStyle.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 "BytesOutputStyle.h"
10
11#include "StreamUtil.h"
12#include "llvm-pdbutil.h"
13
14#include "llvm/DebugInfo/CodeView/Formatters.h"
15#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
16#include "llvm/DebugInfo/MSF/MSFCommon.h"
17#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
18#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
19#include "llvm/DebugInfo/PDB/Native/FormatUtil.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"
28
29using namespace llvm;
30using namespace llvm::codeview;
31using namespace llvm::msf;
32using namespace llvm::pdb;
33
34namespace {
35struct StreamSpec {
36 uint32_t SI = 0;
37 uint32_t Begin = 0;
38 uint32_t Size = 0;
39};
40} // namespace
41
42static Expected<StreamSpec> parseStreamSpec(StringRef Str) {
43 StreamSpec Result;
44 if (Str.consumeInteger(Radix: 0, Result&: Result.SI))
45 return make_error<RawError>(Args: raw_error_code::invalid_format,
46 Args: "Invalid Stream Specification");
47 if (Str.consume_front(Prefix: ":")) {
48 if (Str.consumeInteger(Radix: 0, Result&: Result.Begin))
49 return make_error<RawError>(Args: raw_error_code::invalid_format,
50 Args: "Invalid Stream Specification");
51 }
52 if (Str.consume_front(Prefix: "@")) {
53 if (Str.consumeInteger(Radix: 0, Result&: Result.Size))
54 return make_error<RawError>(Args: raw_error_code::invalid_format,
55 Args: "Invalid Stream Specification");
56 }
57
58 if (!Str.empty())
59 return make_error<RawError>(Args: raw_error_code::invalid_format,
60 Args: "Invalid Stream Specification");
61 return Result;
62}
63
64static SmallVector<StreamSpec, 2> parseStreamSpecs(LinePrinter &P) {
65 SmallVector<StreamSpec, 2> Result;
66
67 for (auto &Str : opts::bytes::DumpStreamData) {
68 auto ESS = parseStreamSpec(Str);
69 if (!ESS) {
70 P.formatLine(Fmt: "Error parsing stream spec {0}: {1}", Items&: Str,
71 Items: toString(E: ESS.takeError()));
72 continue;
73 }
74 Result.push_back(Elt: *ESS);
75 }
76 return Result;
77}
78
79static void printHeader(LinePrinter &P, const Twine &S) {
80 P.NewLine();
81 P.formatLine(Fmt: "{0,=60}", Items: S);
82 P.formatLine(Fmt: "{0}", Items: fmt_repeat(Item: '=', Count: 60));
83}
84
85BytesOutputStyle::BytesOutputStyle(PDBFile &File)
86 : File(File), P(2, false, outs(), opts::Filters) {}
87
88Error BytesOutputStyle::dump() {
89
90 if (opts::bytes::DumpBlockRange) {
91 auto &R = *opts::bytes::DumpBlockRange;
92 uint32_t Max = R.Max.value_or(u&: R.Min);
93
94 if (Max < R.Min)
95 return make_error<StringError>(
96 Args: "Invalid block range specified. Max < Min",
97 Args: inconvertibleErrorCode());
98 if (Max >= File.getBlockCount())
99 return make_error<StringError>(
100 Args: "Invalid block range specified. Requested block out of bounds",
101 Args: inconvertibleErrorCode());
102
103 dumpBlockRanges(Min: R.Min, Max);
104 P.NewLine();
105 }
106
107 if (opts::bytes::DumpByteRange) {
108 auto &R = *opts::bytes::DumpByteRange;
109 uint32_t Max = R.Max.value_or(u: File.getFileSize());
110
111 if (Max < R.Min)
112 return make_error<StringError>(Args: "Invalid byte range specified. Max < Min",
113 Args: inconvertibleErrorCode());
114 if (Max >= File.getFileSize())
115 return make_error<StringError>(
116 Args: "Invalid byte range specified. Requested byte larger than file size",
117 Args: inconvertibleErrorCode());
118
119 dumpByteRanges(Min: R.Min, Max);
120 P.NewLine();
121 }
122
123 if (opts::bytes::Fpm) {
124 dumpFpm();
125 P.NewLine();
126 }
127
128 if (!opts::bytes::DumpStreamData.empty()) {
129 dumpStreamBytes();
130 P.NewLine();
131 }
132
133 if (opts::bytes::NameMap) {
134 dumpNameMap();
135 P.NewLine();
136 }
137
138 if (opts::bytes::SectionContributions) {
139 dumpSectionContributions();
140 P.NewLine();
141 }
142
143 if (opts::bytes::SectionMap) {
144 dumpSectionMap();
145 P.NewLine();
146 }
147
148 if (opts::bytes::ModuleInfos) {
149 dumpModuleInfos();
150 P.NewLine();
151 }
152
153 if (opts::bytes::FileInfo) {
154 dumpFileInfo();
155 P.NewLine();
156 }
157
158 if (opts::bytes::TypeServerMap) {
159 dumpTypeServerMap();
160 P.NewLine();
161 }
162
163 if (opts::bytes::ECData) {
164 dumpECData();
165 P.NewLine();
166 }
167
168 if (!opts::bytes::TypeIndex.empty()) {
169 dumpTypeIndex(StreamIdx: StreamTPI, Indices: opts::bytes::TypeIndex);
170 P.NewLine();
171 }
172
173 if (!opts::bytes::IdIndex.empty()) {
174 dumpTypeIndex(StreamIdx: StreamIPI, Indices: opts::bytes::IdIndex);
175 P.NewLine();
176 }
177
178 if (opts::bytes::ModuleSyms) {
179 dumpModuleSyms();
180 P.NewLine();
181 }
182
183 if (opts::bytes::ModuleC11) {
184 dumpModuleC11();
185 P.NewLine();
186 }
187
188 if (opts::bytes::ModuleC13) {
189 dumpModuleC13();
190 P.NewLine();
191 }
192
193 return Error::success();
194}
195
196void BytesOutputStyle::dumpNameMap() {
197 printHeader(P, S: "Named Stream Map");
198
199 AutoIndent Indent(P);
200
201 auto &InfoS = Err(File.getPDBInfoStream());
202 BinarySubstreamRef NS = InfoS.getNamedStreamsBuffer();
203 auto Layout = File.getStreamLayout(StreamIdx: StreamPDB);
204 P.formatMsfStreamData(Label: "Named Stream Map", File, Stream: Layout, Substream: NS);
205}
206
207void BytesOutputStyle::dumpBlockRanges(uint32_t Min, uint32_t Max) {
208 printHeader(P, S: "MSF Blocks");
209
210 AutoIndent Indent(P);
211 for (uint32_t I = Min; I <= Max; ++I) {
212 uint64_t Base = I;
213 Base *= File.getBlockSize();
214
215 auto ExpectedData = File.getBlockData(BlockIndex: I, NumBytes: File.getBlockSize());
216 if (!ExpectedData) {
217 P.formatLine(Fmt: "Could not get block {0}. Reason = {1}", Items&: I,
218 Items: toString(E: ExpectedData.takeError()));
219 continue;
220 }
221 std::string Label = formatv(Fmt: "Block {0}", Vals&: I).str();
222 P.formatBinary(Label, Data: *ExpectedData, BaseAddr: Base, StartOffset: 0);
223 }
224}
225
226void BytesOutputStyle::dumpSectionContributions() {
227 printHeader(P, S: "Section Contributions");
228
229 AutoIndent Indent(P);
230
231 auto &DbiS = Err(File.getPDBDbiStream());
232 BinarySubstreamRef NS = DbiS.getSectionContributionData();
233 auto Layout = File.getStreamLayout(StreamIdx: StreamDBI);
234 P.formatMsfStreamData(Label: "Section Contributions", File, Stream: Layout, Substream: NS);
235}
236
237void BytesOutputStyle::dumpSectionMap() {
238 printHeader(P, S: "Section Map");
239
240 AutoIndent Indent(P);
241
242 auto &DbiS = Err(File.getPDBDbiStream());
243 BinarySubstreamRef NS = DbiS.getSecMapSubstreamData();
244 auto Layout = File.getStreamLayout(StreamIdx: StreamDBI);
245 P.formatMsfStreamData(Label: "Section Map", File, Stream: Layout, Substream: NS);
246}
247
248void BytesOutputStyle::dumpModuleInfos() {
249 printHeader(P, S: "Module Infos");
250
251 AutoIndent Indent(P);
252
253 auto &DbiS = Err(File.getPDBDbiStream());
254 BinarySubstreamRef NS = DbiS.getModiSubstreamData();
255 auto Layout = File.getStreamLayout(StreamIdx: StreamDBI);
256 P.formatMsfStreamData(Label: "Module Infos", File, Stream: Layout, Substream: NS);
257}
258
259void BytesOutputStyle::dumpFileInfo() {
260 printHeader(P, S: "File Info");
261
262 AutoIndent Indent(P);
263
264 auto &DbiS = Err(File.getPDBDbiStream());
265 BinarySubstreamRef NS = DbiS.getFileInfoSubstreamData();
266 auto Layout = File.getStreamLayout(StreamIdx: StreamDBI);
267 P.formatMsfStreamData(Label: "File Info", File, Stream: Layout, Substream: NS);
268}
269
270void BytesOutputStyle::dumpTypeServerMap() {
271 printHeader(P, S: "Type Server Map");
272
273 AutoIndent Indent(P);
274
275 auto &DbiS = Err(File.getPDBDbiStream());
276 BinarySubstreamRef NS = DbiS.getTypeServerMapSubstreamData();
277 auto Layout = File.getStreamLayout(StreamIdx: StreamDBI);
278 P.formatMsfStreamData(Label: "Type Server Map", File, Stream: Layout, Substream: NS);
279}
280
281void BytesOutputStyle::dumpECData() {
282 printHeader(P, S: "Edit and Continue Data");
283
284 AutoIndent Indent(P);
285
286 auto &DbiS = Err(File.getPDBDbiStream());
287 BinarySubstreamRef NS = DbiS.getECSubstreamData();
288 auto Layout = File.getStreamLayout(StreamIdx: StreamDBI);
289 P.formatMsfStreamData(Label: "Edit and Continue Data", File, Stream: Layout, Substream: NS);
290}
291
292void BytesOutputStyle::dumpTypeIndex(uint32_t StreamIdx,
293 ArrayRef<uint32_t> Indices) {
294 assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);
295 assert(!Indices.empty());
296
297 bool IsTpi = (StreamIdx == StreamTPI);
298
299 StringRef Label = IsTpi ? "Type (TPI) Records" : "Index (IPI) Records";
300 printHeader(P, S: Label);
301 auto &Stream = Err(IsTpi ? File.getPDBTpiStream() : File.getPDBIpiStream());
302
303 AutoIndent Indent(P);
304
305 auto Substream = Stream.getTypeRecordsSubstream();
306 auto &Types = Err(initializeTypes(StreamIdx));
307 auto Layout = File.getStreamLayout(StreamIdx);
308 for (const auto &Id : Indices) {
309 TypeIndex TI(Id);
310 if (TI.toArrayIndex() >= Types.capacity()) {
311 P.formatLine(Fmt: "Error: TypeIndex {0} does not exist", Items&: TI);
312 continue;
313 }
314
315 auto Type = Types.getType(Index: TI);
316 uint32_t Offset = Types.getOffsetOfType(Index: TI);
317 auto OneType = Substream.slice(Off: Offset, Size: Type.length());
318 P.formatMsfStreamData(Label: formatv(Fmt: "Type {0}", Vals&: TI).str(), File, Stream: Layout, Substream: OneType);
319 }
320}
321
322template <typename CallbackT>
323static 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(Fmt: "Mod {0:4} | Invalid module index ",
329 Items: fmt_align(Item&: I, Where: AlignStyle::Right, Amount: std::max(a: Digits, b: 4U)));
330 return;
331 }
332
333 auto Modi = Modules.getModuleDescriptor(Modi: I);
334 P.formatLine(Fmt: "Mod {0:4} | `{1}`: ",
335 Items: fmt_align(Item&: I, Where: AlignStyle::Right, Amount: std::max(a: Digits, b: 4U)),
336 Items: Modi.getModuleName());
337
338 uint16_t ModiStream = Modi.getModuleStreamIndex();
339 AutoIndent Indent2(P, IndentLevel);
340 if (ModiStream == kInvalidStreamIndex)
341 return;
342
343 auto ModStreamData = File.createIndexedStream(SN: ModiStream);
344 ModuleDebugStreamRef ModStream(Modi, std::move(ModStreamData));
345 if (auto EC = ModStream.reload()) {
346 P.formatLine(Fmt: "Could not parse debug information.");
347 return;
348 }
349 auto Layout = File.getStreamLayout(StreamIdx: ModiStream);
350 Callback(I, ModStream, Layout);
351}
352
353template <typename CallbackT>
354static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel,
355 CallbackT Callback) {
356 AutoIndent Indent(P);
357 if (!File.hasPDBDbiStream()) {
358 P.formatLine(Fmt: "DBI Stream not present");
359 return;
360 }
361
362 ExitOnError Err("Unexpected error processing modules");
363
364 auto &Stream = Err(File.getPDBDbiStream());
365
366 const DbiModuleList &Modules = Stream.modules();
367
368 if (opts::bytes::ModuleIndex.getNumOccurrences() > 0) {
369 iterateOneModule(File, P, Modules, opts::bytes::ModuleIndex, 1, IndentLevel,
370 Callback);
371 } else {
372 uint32_t Count = Modules.getModuleCount();
373 uint32_t Digits = NumDigits(N: Count);
374 for (uint32_t I = 0; I < Count; ++I) {
375 iterateOneModule(File, P, Modules, I, Digits, IndentLevel, Callback);
376 }
377 }
378}
379
380void BytesOutputStyle::dumpModuleSyms() {
381 printHeader(P, S: "Module Symbols");
382
383 AutoIndent Indent(P);
384
385 iterateModules(File, P, IndentLevel: 2,
386 Callback: [this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
387 const MSFStreamLayout &Layout) {
388 auto Symbols = Stream.getSymbolsSubstream();
389 P.formatMsfStreamData(Label: "Symbols", File, Stream: Layout, Substream: Symbols);
390 });
391}
392
393void BytesOutputStyle::dumpModuleC11() {
394 printHeader(P, S: "C11 Debug Chunks");
395
396 AutoIndent Indent(P);
397
398 iterateModules(File, P, IndentLevel: 2,
399 Callback: [this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
400 const MSFStreamLayout &Layout) {
401 auto Chunks = Stream.getC11LinesSubstream();
402 P.formatMsfStreamData(Label: "C11 Debug Chunks", File, Stream: Layout,
403 Substream: Chunks);
404 });
405}
406
407void BytesOutputStyle::dumpModuleC13() {
408 printHeader(P, S: "Debug Chunks");
409
410 AutoIndent Indent(P);
411
412 iterateModules(
413 File, P, IndentLevel: 2,
414 Callback: [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(args&: ThisChunk, args&: Chunks) = Chunks.split(Off: SS.getRecordLength());
421 P.formatMsfStreamData(Label: formatChunkKind(Kind: SS.kind()), File, Stream: Layout,
422 Substream: ThisChunk);
423 }
424 } else {
425 P.formatMsfStreamData(Label: "Debug Chunks", File, Stream: Layout, Substream: Chunks);
426 }
427 });
428}
429
430void BytesOutputStyle::dumpByteRanges(uint32_t Min, uint32_t Max) {
431 printHeader(P, S: "MSF Bytes");
432
433 AutoIndent Indent(P);
434
435 BinaryStreamReader Reader(File.getMsfBuffer());
436 ArrayRef<uint8_t> Data;
437 consumeError(Err: Reader.skip(Amount: Min));
438 uint32_t Size = Max - Min + 1;
439 auto EC = Reader.readBytes(Buffer&: Data, Size);
440 assert(!EC);
441 consumeError(Err: std::move(EC));
442 P.formatBinary(Label: "Bytes", Data, StartOffset: Min);
443}
444
445Expected<codeview::LazyRandomTypeCollection &>
446BytesOutputStyle::initializeTypes(uint32_t StreamIdx) {
447 auto &TypeCollection = (StreamIdx == StreamTPI) ? TpiTypes : IpiTypes;
448 if (TypeCollection)
449 return *TypeCollection;
450
451 auto Tpi = (StreamIdx == StreamTPI) ? File.getPDBTpiStream()
452 : File.getPDBIpiStream();
453 if (!Tpi)
454 return Tpi.takeError();
455
456 auto &Types = Tpi->typeArray();
457 uint32_t Count = Tpi->getNumTypeRecords();
458 auto Offsets = Tpi->getTypeIndexOffsets();
459 TypeCollection =
460 std::make_unique<LazyRandomTypeCollection>(args: Types, args&: Count, args&: Offsets);
461
462 return *TypeCollection;
463}
464
465void BytesOutputStyle::dumpFpm() {
466 printHeader(P, S: "Free Page Map");
467
468 msf::MSFStreamLayout FpmLayout = File.getFpmStreamLayout();
469 P.formatMsfStreamBlocks(File, Stream: FpmLayout);
470}
471
472void BytesOutputStyle::dumpStreamBytes() {
473 if (StreamPurposes.empty())
474 discoverStreamPurposes(File, Streams&: StreamPurposes);
475
476 printHeader(P, S: "Stream Data");
477 ExitOnError Err("Unexpected error reading stream data");
478
479 auto Specs = parseStreamSpecs(P);
480
481 for (const auto &Spec : Specs) {
482 AutoIndent Indent(P);
483 if (Spec.SI >= StreamPurposes.size()) {
484 P.formatLine(Fmt: "Stream {0}: Not present", Items: Spec.SI);
485 continue;
486 }
487 P.formatMsfStreamData(Label: "Data", File, StreamIdx: Spec.SI,
488 StreamPurpose: StreamPurposes[Spec.SI].getShortName(), Offset: Spec.Begin,
489 Size: Spec.Size);
490 }
491}
492