1 | //===- ExplainOutputStyle.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 "ExplainOutputStyle.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/MappedBlockStream.h" |
17 | #include "llvm/DebugInfo/PDB/Native/DbiStream.h" |
18 | #include "llvm/DebugInfo/PDB/Native/FormatUtil.h" |
19 | #include "llvm/DebugInfo/PDB/Native/InfoStream.h" |
20 | #include "llvm/DebugInfo/PDB/Native/InputFile.h" |
21 | #include "llvm/DebugInfo/PDB/Native/NativeSession.h" |
22 | #include "llvm/DebugInfo/PDB/Native/PDBFile.h" |
23 | #include "llvm/DebugInfo/PDB/Native/RawTypes.h" |
24 | #include "llvm/Object/COFF.h" |
25 | #include "llvm/Support/BinaryByteStream.h" |
26 | #include "llvm/Support/BinaryStreamArray.h" |
27 | #include "llvm/Support/Error.h" |
28 | |
29 | using namespace llvm; |
30 | using namespace llvm::codeview; |
31 | using namespace llvm::msf; |
32 | using namespace llvm::pdb; |
33 | |
34 | ExplainOutputStyle::ExplainOutputStyle(InputFile &File, uint64_t FileOffset) |
35 | : File(File), FileOffset(FileOffset), P(2, false, outs(), opts::Filters) {} |
36 | |
37 | Error ExplainOutputStyle::dump() { |
38 | P.formatLine(Fmt: "Explaining file offset {0} of file '{1}'." , Items: FileOffset, |
39 | Items: File.getFilePath()); |
40 | |
41 | if (File.isPdb()) |
42 | return explainPdbFile(); |
43 | |
44 | return explainBinaryFile(); |
45 | } |
46 | |
47 | Error ExplainOutputStyle::explainPdbFile() { |
48 | bool IsAllocated = explainPdbBlockStatus(); |
49 | if (!IsAllocated) |
50 | return Error::success(); |
51 | |
52 | AutoIndent Indent(P); |
53 | if (isPdbSuperBlock()) |
54 | explainPdbSuperBlockOffset(); |
55 | else if (isPdbFpmBlock()) |
56 | explainPdbFpmBlockOffset(); |
57 | else if (isPdbBlockMapBlock()) |
58 | explainPdbBlockMapOffset(); |
59 | else if (isPdbStreamDirectoryBlock()) |
60 | explainPdbStreamDirectoryOffset(); |
61 | else if (auto Index = getPdbBlockStreamIndex()) |
62 | explainPdbStreamOffset(Stream: *Index); |
63 | else |
64 | explainPdbUnknownBlock(); |
65 | return Error::success(); |
66 | } |
67 | |
68 | Error ExplainOutputStyle::explainBinaryFile() { |
69 | std::unique_ptr<BinaryByteStream> Stream = std::make_unique<BinaryByteStream>( |
70 | args: File.unknown().getBuffer(), args: llvm::endianness::little); |
71 | switch (opts::explain::InputType) { |
72 | case opts::explain::InputFileType::DBIStream: { |
73 | DbiStream Dbi(std::move(Stream)); |
74 | if (auto EC = Dbi.reload(Pdb: nullptr)) |
75 | return EC; |
76 | explainStreamOffset(Stream&: Dbi, OffsetInStream: FileOffset); |
77 | break; |
78 | } |
79 | case opts::explain::InputFileType::PDBStream: { |
80 | InfoStream Info(std::move(Stream)); |
81 | if (auto EC = Info.reload()) |
82 | return EC; |
83 | explainStreamOffset(Stream&: Info, OffsetInStream: FileOffset); |
84 | break; |
85 | } |
86 | default: |
87 | llvm_unreachable("Invalid input file type!" ); |
88 | } |
89 | return Error::success(); |
90 | } |
91 | |
92 | uint32_t ExplainOutputStyle::pdbBlockIndex() const { |
93 | return FileOffset / File.pdb().getBlockSize(); |
94 | } |
95 | |
96 | uint32_t ExplainOutputStyle::pdbBlockOffset() const { |
97 | uint64_t BlockStart = pdbBlockIndex() * File.pdb().getBlockSize(); |
98 | assert(FileOffset >= BlockStart); |
99 | return FileOffset - BlockStart; |
100 | } |
101 | |
102 | bool ExplainOutputStyle::isPdbSuperBlock() const { |
103 | return pdbBlockIndex() == 0; |
104 | } |
105 | |
106 | bool ExplainOutputStyle::isPdbFpm1() const { |
107 | return ((pdbBlockIndex() - 1) % File.pdb().getBlockSize() == 0); |
108 | } |
109 | bool ExplainOutputStyle::isPdbFpm2() const { |
110 | return ((pdbBlockIndex() - 2) % File.pdb().getBlockSize() == 0); |
111 | } |
112 | |
113 | bool ExplainOutputStyle::isPdbFpmBlock() const { |
114 | return isPdbFpm1() || isPdbFpm2(); |
115 | } |
116 | |
117 | bool ExplainOutputStyle::isPdbBlockMapBlock() const { |
118 | return pdbBlockIndex() == File.pdb().getBlockMapIndex(); |
119 | } |
120 | |
121 | bool ExplainOutputStyle::isPdbStreamDirectoryBlock() const { |
122 | const auto &Layout = File.pdb().getMsfLayout(); |
123 | return llvm::is_contained(Range: Layout.DirectoryBlocks, Element: pdbBlockIndex()); |
124 | } |
125 | |
126 | std::optional<uint32_t> ExplainOutputStyle::getPdbBlockStreamIndex() const { |
127 | const auto &Layout = File.pdb().getMsfLayout(); |
128 | for (const auto &Entry : enumerate(First: Layout.StreamMap)) { |
129 | if (!llvm::is_contained(Range: Entry.value(), Element: pdbBlockIndex())) |
130 | continue; |
131 | return Entry.index(); |
132 | } |
133 | return std::nullopt; |
134 | } |
135 | |
136 | bool ExplainOutputStyle::explainPdbBlockStatus() { |
137 | if (FileOffset >= File.pdb().getFileSize()) { |
138 | P.formatLine(Fmt: "Address {0} is not in the file (file size = {1})." , |
139 | Items: FileOffset, Items: File.pdb().getFileSize()); |
140 | return false; |
141 | } |
142 | P.formatLine(Fmt: "Block:Offset = {2:X-}:{1:X-4}." , Items: FileOffset, Items: pdbBlockOffset(), |
143 | Items: pdbBlockIndex()); |
144 | |
145 | bool IsFree = File.pdb().getMsfLayout().FreePageMap[pdbBlockIndex()]; |
146 | P.formatLine(Fmt: "Address is in block {0} ({1}allocated)." , Items: pdbBlockIndex(), |
147 | Items: IsFree ? "un" : "" ); |
148 | return !IsFree; |
149 | } |
150 | |
151 | #define endof(Class, Field) (offsetof(Class, Field) + sizeof(Class::Field)) |
152 | |
153 | void ExplainOutputStyle::explainPdbSuperBlockOffset() { |
154 | P.formatLine(Fmt: "This corresponds to offset {0} of the MSF super block, " , |
155 | Items: pdbBlockOffset()); |
156 | if (pdbBlockOffset() < endof(SuperBlock, MagicBytes)) |
157 | P.printLine(T: "which is part of the MSF file magic." ); |
158 | else if (pdbBlockOffset() < endof(SuperBlock, BlockSize)) { |
159 | P.printLine(T: "which contains the block size of the file." ); |
160 | P.formatLine(Fmt: "The current value is {0}." , |
161 | Items: uint32_t(File.pdb().getMsfLayout().SB->BlockSize)); |
162 | } else if (pdbBlockOffset() < endof(SuperBlock, FreeBlockMapBlock)) { |
163 | P.printLine(T: "which contains the index of the FPM block (e.g. 1 or 2)." ); |
164 | P.formatLine(Fmt: "The current value is {0}." , |
165 | Items: uint32_t(File.pdb().getMsfLayout().SB->FreeBlockMapBlock)); |
166 | } else if (pdbBlockOffset() < endof(SuperBlock, NumBlocks)) { |
167 | P.printLine(T: "which contains the number of blocks in the file." ); |
168 | P.formatLine(Fmt: "The current value is {0}." , |
169 | Items: uint32_t(File.pdb().getMsfLayout().SB->NumBlocks)); |
170 | } else if (pdbBlockOffset() < endof(SuperBlock, NumDirectoryBytes)) { |
171 | P.printLine(T: "which contains the number of bytes in the stream directory." ); |
172 | P.formatLine(Fmt: "The current value is {0}." , |
173 | Items: uint32_t(File.pdb().getMsfLayout().SB->NumDirectoryBytes)); |
174 | } else if (pdbBlockOffset() < endof(SuperBlock, Unknown1)) { |
175 | P.printLine(T: "whose purpose is unknown." ); |
176 | P.formatLine(Fmt: "The current value is {0}." , |
177 | Items: uint32_t(File.pdb().getMsfLayout().SB->Unknown1)); |
178 | } else if (pdbBlockOffset() < endof(SuperBlock, BlockMapAddr)) { |
179 | P.printLine(T: "which contains the file offset of the block map." ); |
180 | P.formatLine(Fmt: "The current value is {0}." , |
181 | Items: uint32_t(File.pdb().getMsfLayout().SB->BlockMapAddr)); |
182 | } else { |
183 | assert(pdbBlockOffset() > sizeof(SuperBlock)); |
184 | P.printLine( |
185 | T: "which is outside the range of valid data for the super block." ); |
186 | } |
187 | } |
188 | |
189 | static std::string toBinaryString(uint8_t Byte) { |
190 | char Result[9] = {0}; |
191 | for (int I = 0; I < 8; ++I) { |
192 | char C = (Byte & 1) ? '1' : '0'; |
193 | Result[I] = C; |
194 | Byte >>= 1; |
195 | } |
196 | return std::string(Result); |
197 | } |
198 | |
199 | void ExplainOutputStyle::explainPdbFpmBlockOffset() { |
200 | const MSFLayout &Layout = File.pdb().getMsfLayout(); |
201 | uint32_t MainFpm = Layout.mainFpmBlock(); |
202 | uint32_t AltFpm = Layout.alternateFpmBlock(); |
203 | |
204 | assert(isPdbFpmBlock()); |
205 | uint32_t Fpm = isPdbFpm1() ? 1 : 2; |
206 | uint32_t FpmChunk = pdbBlockIndex() / File.pdb().getBlockSize(); |
207 | assert((Fpm == MainFpm) || (Fpm == AltFpm)); |
208 | (void)AltFpm; |
209 | bool IsMain = (Fpm == MainFpm); |
210 | P.formatLine(Fmt: "Address is in FPM{0} ({1} FPM)" , Items&: Fpm, Items: IsMain ? "Main" : "Alt" ); |
211 | uint32_t DescribedBlockStart = |
212 | 8 * (FpmChunk * File.pdb().getBlockSize() + pdbBlockOffset()); |
213 | if (DescribedBlockStart > File.pdb().getBlockCount()) { |
214 | P.printLine(T: "Address is in extraneous FPM space." ); |
215 | return; |
216 | } |
217 | |
218 | P.formatLine(Fmt: "Address describes the allocation status of blocks [{0},{1})" , |
219 | Items&: DescribedBlockStart, Items: DescribedBlockStart + 8); |
220 | ArrayRef<uint8_t> Bytes; |
221 | cantFail(Err: File.pdb().getMsfBuffer().readBytes(Offset: FileOffset, Size: 1, Buffer&: Bytes)); |
222 | P.formatLine(Fmt: "Status = {0} (Note: 0 = allocated, 1 = free)" , |
223 | Items: toBinaryString(Byte: Bytes[0])); |
224 | } |
225 | |
226 | void ExplainOutputStyle::explainPdbBlockMapOffset() { |
227 | uint64_t BlockMapOffset = File.pdb().getBlockMapOffset(); |
228 | uint32_t OffsetInBlock = FileOffset - BlockMapOffset; |
229 | P.formatLine(Fmt: "Address is at offset {0} of the directory block list" , |
230 | Items&: OffsetInBlock); |
231 | } |
232 | |
233 | static uint32_t getOffsetInStream(ArrayRef<support::ulittle32_t> StreamBlocks, |
234 | uint64_t FileOffset, uint32_t BlockSize) { |
235 | uint32_t BlockIndex = FileOffset / BlockSize; |
236 | uint32_t OffsetInBlock = FileOffset - BlockIndex * BlockSize; |
237 | |
238 | auto Iter = llvm::find(Range&: StreamBlocks, Val: BlockIndex); |
239 | assert(Iter != StreamBlocks.end()); |
240 | uint32_t StreamBlockIndex = std::distance(first: StreamBlocks.begin(), last: Iter); |
241 | return StreamBlockIndex * BlockSize + OffsetInBlock; |
242 | } |
243 | |
244 | void ExplainOutputStyle::explainPdbStreamOffset(uint32_t Stream) { |
245 | SmallVector<StreamInfo, 12> Streams; |
246 | discoverStreamPurposes(File&: File.pdb(), Streams); |
247 | |
248 | assert(Stream <= Streams.size()); |
249 | const StreamInfo &S = Streams[Stream]; |
250 | const auto &Layout = File.pdb().getStreamLayout(StreamIdx: Stream); |
251 | uint32_t StreamOff = |
252 | getOffsetInStream(StreamBlocks: Layout.Blocks, FileOffset, BlockSize: File.pdb().getBlockSize()); |
253 | P.formatLine(Fmt: "Address is at offset {0}/{1} of Stream {2} ({3}){4}." , |
254 | Items&: StreamOff, Items: Layout.Length, Items&: Stream, Items: S.getLongName(), |
255 | Items: (StreamOff > Layout.Length) ? " in unused space" : "" ); |
256 | switch (S.getPurpose()) { |
257 | case StreamPurpose::DBI: { |
258 | DbiStream &Dbi = cantFail(ValOrErr: File.pdb().getPDBDbiStream()); |
259 | explainStreamOffset(Stream&: Dbi, OffsetInStream: StreamOff); |
260 | break; |
261 | } |
262 | case StreamPurpose::PDB: { |
263 | InfoStream &Info = cantFail(ValOrErr: File.pdb().getPDBInfoStream()); |
264 | explainStreamOffset(Stream&: Info, OffsetInStream: StreamOff); |
265 | break; |
266 | } |
267 | case StreamPurpose::IPI: |
268 | case StreamPurpose::TPI: |
269 | case StreamPurpose::ModuleStream: |
270 | case StreamPurpose::NamedStream: |
271 | default: |
272 | break; |
273 | } |
274 | } |
275 | |
276 | void ExplainOutputStyle::explainPdbStreamDirectoryOffset() { |
277 | auto DirectoryBlocks = File.pdb().getDirectoryBlockArray(); |
278 | const auto &Layout = File.pdb().getMsfLayout(); |
279 | uint32_t StreamOff = |
280 | getOffsetInStream(StreamBlocks: DirectoryBlocks, FileOffset, BlockSize: File.pdb().getBlockSize()); |
281 | P.formatLine(Fmt: "Address is at offset {0}/{1} of Stream Directory{2}." , |
282 | Items&: StreamOff, Items: uint32_t(Layout.SB->NumDirectoryBytes), |
283 | Items: uint32_t(StreamOff > Layout.SB->NumDirectoryBytes) |
284 | ? " in unused space" |
285 | : "" ); |
286 | } |
287 | |
288 | void ExplainOutputStyle::explainPdbUnknownBlock() { |
289 | P.formatLine(Fmt: "Address has unknown purpose." ); |
290 | } |
291 | |
292 | template <typename T> |
293 | static void printStructField(LinePrinter &P, StringRef Label, T Value) { |
294 | P.formatLine(Fmt: "which contains {0}." , Items&: Label); |
295 | P.formatLine("The current value is {0}." , Value); |
296 | } |
297 | |
298 | static void (LinePrinter &P, DbiStream &Dbi, |
299 | uint32_t Offset) { |
300 | const DbiStreamHeader * = Dbi.getHeader(); |
301 | assert(Header != nullptr); |
302 | |
303 | if (Offset < endof(DbiStreamHeader, VersionSignature)) |
304 | printStructField(P, Label: "the DBI Stream Version Signature" , |
305 | Value: int32_t(Header->VersionSignature)); |
306 | else if (Offset < endof(DbiStreamHeader, VersionHeader)) |
307 | printStructField(P, Label: "the DBI Stream Version Header" , |
308 | Value: uint32_t(Header->VersionHeader)); |
309 | else if (Offset < endof(DbiStreamHeader, Age)) |
310 | printStructField(P, Label: "the age of the DBI Stream" , Value: uint32_t(Header->Age)); |
311 | else if (Offset < endof(DbiStreamHeader, GlobalSymbolStreamIndex)) |
312 | printStructField(P, Label: "the index of the Global Symbol Stream" , |
313 | Value: uint16_t(Header->GlobalSymbolStreamIndex)); |
314 | else if (Offset < endof(DbiStreamHeader, BuildNumber)) |
315 | printStructField(P, Label: "the build number" , Value: uint16_t(Header->BuildNumber)); |
316 | else if (Offset < endof(DbiStreamHeader, PublicSymbolStreamIndex)) |
317 | printStructField(P, Label: "the index of the Public Symbol Stream" , |
318 | Value: uint16_t(Header->PublicSymbolStreamIndex)); |
319 | else if (Offset < endof(DbiStreamHeader, PdbDllVersion)) |
320 | printStructField(P, Label: "the version of mspdb.dll" , |
321 | Value: uint16_t(Header->PdbDllVersion)); |
322 | else if (Offset < endof(DbiStreamHeader, SymRecordStreamIndex)) |
323 | printStructField(P, Label: "the index of the Symbol Record Stream" , |
324 | Value: uint16_t(Header->SymRecordStreamIndex)); |
325 | else if (Offset < endof(DbiStreamHeader, PdbDllRbld)) |
326 | printStructField(P, Label: "the rbld of mspdb.dll" , Value: uint16_t(Header->PdbDllRbld)); |
327 | else if (Offset < endof(DbiStreamHeader, ModiSubstreamSize)) |
328 | printStructField(P, Label: "the size of the Module Info Substream" , |
329 | Value: int32_t(Header->ModiSubstreamSize)); |
330 | else if (Offset < endof(DbiStreamHeader, SecContrSubstreamSize)) |
331 | printStructField(P, Label: "the size of the Section Contribution Substream" , |
332 | Value: int32_t(Header->SecContrSubstreamSize)); |
333 | else if (Offset < endof(DbiStreamHeader, SectionMapSize)) |
334 | printStructField(P, Label: "the size of the Section Map Substream" , |
335 | Value: int32_t(Header->SectionMapSize)); |
336 | else if (Offset < endof(DbiStreamHeader, FileInfoSize)) |
337 | printStructField(P, Label: "the size of the File Info Substream" , |
338 | Value: int32_t(Header->FileInfoSize)); |
339 | else if (Offset < endof(DbiStreamHeader, TypeServerSize)) |
340 | printStructField(P, Label: "the size of the Type Server Map" , |
341 | Value: int32_t(Header->TypeServerSize)); |
342 | else if (Offset < endof(DbiStreamHeader, MFCTypeServerIndex)) |
343 | printStructField(P, Label: "the index of the MFC Type Server stream" , |
344 | Value: uint32_t(Header->MFCTypeServerIndex)); |
345 | else if (Offset < endof(DbiStreamHeader, OptionalDbgHdrSize)) |
346 | printStructField(P, Label: "the size of the Optional Debug Stream array" , |
347 | Value: int32_t(Header->OptionalDbgHdrSize)); |
348 | else if (Offset < endof(DbiStreamHeader, ECSubstreamSize)) |
349 | printStructField(P, Label: "the size of the Edit & Continue Substream" , |
350 | Value: int32_t(Header->ECSubstreamSize)); |
351 | else if (Offset < endof(DbiStreamHeader, Flags)) |
352 | printStructField(P, Label: "the DBI Stream flags" , Value: uint16_t(Header->Flags)); |
353 | else if (Offset < endof(DbiStreamHeader, MachineType)) |
354 | printStructField(P, Label: "the machine type" , Value: uint16_t(Header->MachineType)); |
355 | else if (Offset < endof(DbiStreamHeader, Reserved)) |
356 | printStructField(P, Label: "reserved data" , Value: uint32_t(Header->Reserved)); |
357 | } |
358 | |
359 | static void explainDbiModiSubstreamOffset(LinePrinter &P, DbiStream &Dbi, |
360 | uint32_t Offset) { |
361 | VarStreamArray<DbiModuleDescriptor> ModuleDescriptors; |
362 | BinaryStreamRef ModiSubstreamData = Dbi.getModiSubstreamData().StreamData; |
363 | BinaryStreamReader Reader(ModiSubstreamData); |
364 | |
365 | cantFail(Err: Reader.readArray(Array&: ModuleDescriptors, Size: ModiSubstreamData.getLength())); |
366 | auto Prev = ModuleDescriptors.begin(); |
367 | assert(Prev.offset() == 0); |
368 | auto Current = Prev; |
369 | uint32_t Index = 0; |
370 | while (true) { |
371 | Prev = Current; |
372 | ++Current; |
373 | if (Current == ModuleDescriptors.end() || Offset < Current.offset()) |
374 | break; |
375 | ++Index; |
376 | } |
377 | |
378 | const DbiModuleDescriptor &Descriptor = *Prev; |
379 | P.formatLine(Fmt: "which contains the descriptor for module {0} ({1})." , Items&: Index, |
380 | Items: Descriptor.getModuleName()); |
381 | } |
382 | |
383 | template <typename T> |
384 | static void dontExplain(LinePrinter &Printer, T &Stream, uint32_t Offset) {} |
385 | |
386 | template <typename T, typename SubstreamRangeT> |
387 | static void explainSubstreamOffset(LinePrinter &P, uint32_t OffsetInStream, |
388 | T &Stream, |
389 | const SubstreamRangeT &Substreams) { |
390 | uint32_t SubOffset = OffsetInStream; |
391 | for (const auto &Entry : Substreams) { |
392 | if (Entry.Size <= 0) |
393 | continue; |
394 | uint32_t S = static_cast<uint32_t>(Entry.Size); |
395 | if (SubOffset < S) { |
396 | P.formatLine("address is at offset {0}/{1} of the {2}." , SubOffset, S, |
397 | Entry.Label); |
398 | Entry.Explain(P, Stream, SubOffset); |
399 | return; |
400 | } |
401 | SubOffset -= S; |
402 | } |
403 | } |
404 | |
405 | void ExplainOutputStyle::explainStreamOffset(DbiStream &Dbi, |
406 | uint32_t OffsetInStream) { |
407 | P.printLine(T: "Within the DBI stream:" ); |
408 | AutoIndent Indent(P); |
409 | const DbiStreamHeader * = Dbi.getHeader(); |
410 | assert(Header != nullptr); |
411 | |
412 | struct SubstreamInfo { |
413 | int32_t Size; |
414 | StringRef Label; |
415 | void (*Explain)(LinePrinter &, DbiStream &, uint32_t); |
416 | } Substreams[] = { |
417 | {.Size: sizeof(DbiStreamHeader), .Label: "DBI Stream Header" , .Explain: explainDbiHeaderOffset}, |
418 | {.Size: int32_t(Header->ModiSubstreamSize), .Label: "Module Info Substream" , |
419 | .Explain: explainDbiModiSubstreamOffset}, |
420 | {.Size: int32_t(Header->SecContrSubstreamSize), .Label: "Section Contribution Substream" , |
421 | .Explain: dontExplain<DbiStream>}, |
422 | {.Size: int32_t(Header->SectionMapSize), .Label: "Section Map" , .Explain: dontExplain<DbiStream>}, |
423 | {.Size: int32_t(Header->FileInfoSize), .Label: "File Info Substream" , |
424 | .Explain: dontExplain<DbiStream>}, |
425 | {.Size: int32_t(Header->TypeServerSize), .Label: "Type Server Map Substream" , |
426 | .Explain: dontExplain<DbiStream>}, |
427 | {.Size: int32_t(Header->ECSubstreamSize), .Label: "Edit & Continue Substream" , |
428 | .Explain: dontExplain<DbiStream>}, |
429 | {.Size: int32_t(Header->OptionalDbgHdrSize), .Label: "Optional Debug Stream Array" , |
430 | .Explain: dontExplain<DbiStream>}, |
431 | }; |
432 | |
433 | explainSubstreamOffset(P, OffsetInStream, Stream&: Dbi, Substreams); |
434 | } |
435 | |
436 | static void (LinePrinter &P, InfoStream &Info, |
437 | uint32_t Offset) { |
438 | const InfoStreamHeader * = Info.getHeader(); |
439 | assert(Header != nullptr); |
440 | |
441 | if (Offset < endof(InfoStreamHeader, Version)) |
442 | printStructField(P, Label: "the PDB Stream Version Signature" , |
443 | Value: uint32_t(Header->Version)); |
444 | else if (Offset < endof(InfoStreamHeader, Signature)) |
445 | printStructField(P, Label: "the signature of the PDB Stream" , |
446 | Value: uint32_t(Header->Signature)); |
447 | else if (Offset < endof(InfoStreamHeader, Age)) |
448 | printStructField(P, Label: "the age of the PDB" , Value: uint32_t(Header->Age)); |
449 | else if (Offset < endof(InfoStreamHeader, Guid)) |
450 | printStructField(P, Label: "the guid of the PDB" , Value: fmt_guid(Item: Header->Guid.Guid)); |
451 | } |
452 | |
453 | void ExplainOutputStyle::explainStreamOffset(InfoStream &Info, |
454 | uint32_t OffsetInStream) { |
455 | P.printLine(T: "Within the PDB stream:" ); |
456 | AutoIndent Indent(P); |
457 | |
458 | struct SubstreamInfo { |
459 | uint32_t Size; |
460 | StringRef Label; |
461 | void (*Explain)(LinePrinter &, InfoStream &, uint32_t); |
462 | } Substreams[] = {{.Size: sizeof(InfoStreamHeader), .Label: "PDB Stream Header" , |
463 | .Explain: explainPdbStreamHeaderOffset}, |
464 | {.Size: Info.getNamedStreamMapByteSize(), .Label: "Named Stream Map" , |
465 | .Explain: dontExplain<InfoStream>}, |
466 | {.Size: Info.getStreamSize(), .Label: "PDB Feature Signatures" , |
467 | .Explain: dontExplain<InfoStream>}}; |
468 | |
469 | explainSubstreamOffset(P, OffsetInStream, Stream&: Info, Substreams); |
470 | } |
471 | |