| 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 = {0:X-}:{1:X-4}." , Items: pdbBlockIndex(), |
| 143 | Items: pdbBlockOffset()); |
| 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 | |