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 | |
29 | using namespace llvm; |
30 | using namespace llvm::codeview; |
31 | using namespace llvm::msf; |
32 | using namespace llvm::pdb; |
33 | |
34 | namespace { |
35 | struct StreamSpec { |
36 | uint32_t SI = 0; |
37 | uint32_t Begin = 0; |
38 | uint32_t Size = 0; |
39 | }; |
40 | } // namespace |
41 | |
42 | static 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 | |
64 | static 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 | |
79 | static void (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 | |
85 | BytesOutputStyle::BytesOutputStyle(PDBFile &File) |
86 | : File(File), P(2, false, outs(), opts::Filters) {} |
87 | |
88 | Error 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 | |
196 | void 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 | |
207 | void 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 | |
226 | void 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 | |
237 | void 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 | |
248 | void 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 | |
259 | void 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 | |
270 | void 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 | |
281 | void 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 | |
292 | void 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 | |
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(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 | |
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(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 | |
380 | void 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 | |
393 | void 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 | |
407 | void 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 | |
430 | void 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 | |
445 | Expected<codeview::LazyRandomTypeCollection &> |
446 | BytesOutputStyle::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 | |
465 | void BytesOutputStyle::dumpFpm() { |
466 | printHeader(P, S: "Free Page Map" ); |
467 | |
468 | msf::MSFStreamLayout FpmLayout = File.getFpmStreamLayout(); |
469 | P.formatMsfStreamBlocks(File, Stream: FpmLayout); |
470 | } |
471 | |
472 | void 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 | |