blob: 70deb2e7277be9e629ae583154029aacdda4c9a0 [file] [log] [blame]
Zachary Turnerea40f402018-03-29 16:28:20 +00001//===- ExplainOutputStyle.cpp --------------------------------- *- C++ --*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "ExplainOutputStyle.h"
11
12#include "FormatUtil.h"
13#include "StreamUtil.h"
14#include "llvm-pdbutil.h"
15
Zachary Turnerd5cf5cf2018-03-30 17:16:50 +000016#include "llvm/DebugInfo/CodeView/Formatters.h"
Zachary Turnerea40f402018-03-29 16:28:20 +000017#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
Zachary Turnerd5cf5cf2018-03-30 17:16:50 +000018#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
19#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
Zachary Turnerea40f402018-03-29 16:28:20 +000020#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
Zachary Turnerd5cf5cf2018-03-30 17:16:50 +000021#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
22#include "llvm/Support/BinaryStreamArray.h"
23#include "llvm/Support/Error.h"
Zachary Turnerea40f402018-03-29 16:28:20 +000024
25using namespace llvm;
26using namespace llvm::codeview;
27using namespace llvm::msf;
28using namespace llvm::pdb;
29
30ExplainOutputStyle::ExplainOutputStyle(PDBFile &File, uint64_t FileOffset)
31 : File(File), FileOffset(FileOffset),
32 BlockIndex(FileOffset / File.getBlockSize()),
33 OffsetInBlock(FileOffset - BlockIndex * File.getBlockSize()),
34 P(2, false, outs()) {}
35
36Error ExplainOutputStyle::dump() {
37 P.formatLine("Explaining file offset {0} of file '{1}'.", FileOffset,
38 File.getFilePath());
39
40 bool IsAllocated = explainBlockStatus();
41 if (!IsAllocated)
42 return Error::success();
43
44 AutoIndent Indent(P);
45 if (isSuperBlock())
46 explainSuperBlockOffset();
47 else if (isFpmBlock())
48 explainFpmBlockOffset();
49 else if (isBlockMapBlock())
50 explainBlockMapOffset();
51 else if (isStreamDirectoryBlock())
52 explainStreamDirectoryOffset();
53 else if (auto Index = getBlockStreamIndex())
54 explainStreamOffset(*Index);
55 else
56 explainUnknownBlock();
57 return Error::success();
58}
59
60bool ExplainOutputStyle::isSuperBlock() const { return BlockIndex == 0; }
61
62bool ExplainOutputStyle::isFpm1() const {
63 return ((BlockIndex - 1) % File.getBlockSize() == 0);
64}
65bool ExplainOutputStyle::isFpm2() const {
66 return ((BlockIndex - 2) % File.getBlockSize() == 0);
67}
68
69bool ExplainOutputStyle::isFpmBlock() const { return isFpm1() || isFpm2(); }
70
71bool ExplainOutputStyle::isBlockMapBlock() const {
72 return BlockIndex == File.getBlockMapIndex();
73}
74
75bool ExplainOutputStyle::isStreamDirectoryBlock() const {
76 const auto &Layout = File.getMsfLayout();
77 return llvm::is_contained(Layout.DirectoryBlocks, BlockIndex);
78}
79
80Optional<uint32_t> ExplainOutputStyle::getBlockStreamIndex() const {
81 const auto &Layout = File.getMsfLayout();
82 for (const auto &Entry : enumerate(Layout.StreamMap)) {
83 if (!llvm::is_contained(Entry.value(), BlockIndex))
84 continue;
85 return Entry.index();
86 }
87 return None;
88}
89
90bool ExplainOutputStyle::explainBlockStatus() {
91 if (FileOffset >= File.getFileSize()) {
92 P.formatLine("Address {0} is not in the file (file size = {1}).",
93 FileOffset, File.getFileSize());
94 return false;
95 }
96 P.formatLine("Block:Offset = {2:X-}:{1:X-4}.", FileOffset, OffsetInBlock,
97 BlockIndex);
98
99 bool IsFree = File.getMsfLayout().FreePageMap[BlockIndex];
100 P.formatLine("Address is in block {0} ({1}allocated).", BlockIndex,
101 IsFree ? "un" : "");
102 return !IsFree;
103}
104
Zachary Turner1b204162018-03-29 17:11:14 +0000105#define endof(Class, Field) (offsetof(Class, Field) + sizeof(Class::Field))
106
Zachary Turnerea40f402018-03-29 16:28:20 +0000107void ExplainOutputStyle::explainSuperBlockOffset() {
Zachary Turner1b204162018-03-29 17:11:14 +0000108 P.formatLine("This corresponds to offset {0} of the MSF super block, ",
Zachary Turnerea40f402018-03-29 16:28:20 +0000109 OffsetInBlock);
Zachary Turner1b204162018-03-29 17:11:14 +0000110 if (OffsetInBlock < endof(SuperBlock, MagicBytes))
Zachary Turnerea40f402018-03-29 16:28:20 +0000111 P.printLine("which is part of the MSF file magic.");
Zachary Turnerf4b6dcf2018-03-29 17:45:34 +0000112 else if (OffsetInBlock < endof(SuperBlock, BlockSize)) {
Zachary Turnerea40f402018-03-29 16:28:20 +0000113 P.printLine("which contains the block size of the file.");
Zachary Turnerf4b6dcf2018-03-29 17:45:34 +0000114 P.formatLine("The current value is {0}.",
115 uint32_t(File.getMsfLayout().SB->BlockSize));
116 } else if (OffsetInBlock < endof(SuperBlock, FreeBlockMapBlock)) {
Zachary Turnerea40f402018-03-29 16:28:20 +0000117 P.printLine("which contains the index of the FPM block (e.g. 1 or 2).");
Zachary Turnerf4b6dcf2018-03-29 17:45:34 +0000118 P.formatLine("The current value is {0}.",
119 uint32_t(File.getMsfLayout().SB->FreeBlockMapBlock));
120 } else if (OffsetInBlock < endof(SuperBlock, NumBlocks)) {
Zachary Turnerea40f402018-03-29 16:28:20 +0000121 P.printLine("which contains the number of blocks in the file.");
Zachary Turnerf4b6dcf2018-03-29 17:45:34 +0000122 P.formatLine("The current value is {0}.",
123 uint32_t(File.getMsfLayout().SB->NumBlocks));
124 } else if (OffsetInBlock < endof(SuperBlock, NumDirectoryBytes)) {
Zachary Turnerea40f402018-03-29 16:28:20 +0000125 P.printLine("which contains the number of bytes in the stream directory.");
Zachary Turnerf4b6dcf2018-03-29 17:45:34 +0000126 P.formatLine("The current value is {0}.",
127 uint32_t(File.getMsfLayout().SB->NumDirectoryBytes));
128 } else if (OffsetInBlock < endof(SuperBlock, Unknown1)) {
Zachary Turnerea40f402018-03-29 16:28:20 +0000129 P.printLine("whose purpose is unknown.");
Zachary Turnerf4b6dcf2018-03-29 17:45:34 +0000130 P.formatLine("The current value is {0}.",
131 uint32_t(File.getMsfLayout().SB->Unknown1));
132 } else if (OffsetInBlock < endof(SuperBlock, BlockMapAddr)) {
Zachary Turnerea40f402018-03-29 16:28:20 +0000133 P.printLine("which contains the file offset of the block map.");
Zachary Turnerf4b6dcf2018-03-29 17:45:34 +0000134 P.formatLine("The current value is {0}.",
135 uint32_t(File.getMsfLayout().SB->BlockMapAddr));
136 } else {
Zachary Turnerea40f402018-03-29 16:28:20 +0000137 assert(OffsetInBlock > sizeof(SuperBlock));
138 P.printLine(
139 "which is outside the range of valid data for the super block.");
140 }
141}
142
Zachary Turnerf4b6dcf2018-03-29 17:45:34 +0000143static std::string toBinaryString(uint8_t Byte) {
144 char Result[9] = {0};
145 for (int I = 0; I < 8; ++I) {
146 char C = (Byte & 1) ? '1' : '0';
147 Result[I] = C;
148 Byte >>= 1;
149 }
150 return std::string(Result);
151}
152
Zachary Turnerea40f402018-03-29 16:28:20 +0000153void ExplainOutputStyle::explainFpmBlockOffset() {
154 const MSFLayout &Layout = File.getMsfLayout();
155 uint32_t MainFpm = Layout.mainFpmBlock();
156 uint32_t AltFpm = Layout.alternateFpmBlock();
157
158 assert(isFpmBlock());
159 uint32_t Fpm = isFpm1() ? 1 : 2;
160 uint32_t FpmChunk = BlockIndex / File.getBlockSize();
161 assert((Fpm == MainFpm) || (Fpm == AltFpm));
162 (void)AltFpm;
163 bool IsMain = (Fpm == MainFpm);
164 P.formatLine("Address is in FPM{0} ({1} FPM)", Fpm, IsMain ? "Main" : "Alt");
165 uint32_t DescribedBlockStart =
166 8 * (FpmChunk * File.getBlockSize() + OffsetInBlock);
167 if (DescribedBlockStart > File.getBlockCount()) {
168 P.printLine("Address is in extraneous FPM space.");
169 return;
170 }
171
172 P.formatLine("Address describes the allocation status of blocks [{0},{1})",
173 DescribedBlockStart, DescribedBlockStart + 8);
Zachary Turnerf4b6dcf2018-03-29 17:45:34 +0000174 ArrayRef<uint8_t> Bytes;
175 cantFail(File.getMsfBuffer().readBytes(FileOffset, 1, Bytes));
176 P.formatLine("Status = {0} (Note: 0 = allocated, 1 = free)",
177 toBinaryString(Bytes[0]));
Zachary Turnerea40f402018-03-29 16:28:20 +0000178}
179
Zachary Turnerea40f402018-03-29 16:28:20 +0000180void ExplainOutputStyle::explainBlockMapOffset() {
Zachary Turnerea40f402018-03-29 16:28:20 +0000181 uint64_t BlockMapOffset = File.getBlockMapOffset();
182 uint32_t OffsetInBlock = FileOffset - BlockMapOffset;
183 P.formatLine("Address is at offset {0} of the directory block list",
184 OffsetInBlock);
185}
186
187static uint32_t getOffsetInStream(ArrayRef<support::ulittle32_t> StreamBlocks,
188 uint64_t FileOffset, uint32_t BlockSize) {
189 uint32_t BlockIndex = FileOffset / BlockSize;
190 uint32_t OffsetInBlock = FileOffset - BlockIndex * BlockSize;
191
192 auto Iter = llvm::find(StreamBlocks, BlockIndex);
193 assert(Iter != StreamBlocks.end());
194 uint32_t StreamBlockIndex = std::distance(StreamBlocks.begin(), Iter);
195 return StreamBlockIndex * BlockSize + OffsetInBlock;
196}
197
198void ExplainOutputStyle::explainStreamOffset(uint32_t Stream) {
199 SmallVector<StreamInfo, 12> Streams;
200 discoverStreamPurposes(File, Streams);
201
202 assert(Stream <= Streams.size());
203 const StreamInfo &S = Streams[Stream];
204 const auto &Layout = File.getStreamLayout(Stream);
205 uint32_t StreamOff =
206 getOffsetInStream(Layout.Blocks, FileOffset, File.getBlockSize());
207 P.formatLine("Address is at offset {0}/{1} of Stream {2} ({3}){4}.",
208 StreamOff, Layout.Length, Stream, S.getLongName(),
209 (StreamOff > Layout.Length) ? " in unused space" : "");
Zachary Turnerd5cf5cf2018-03-30 17:16:50 +0000210 switch (S.getPurpose()) {
211 case StreamPurpose::DBI:
212 explainDbiStream(Stream, StreamOff);
213 break;
214 case StreamPurpose::PDB:
215 explainPdbStream(Stream, StreamOff);
216 break;
217 case StreamPurpose::IPI:
218 case StreamPurpose::TPI:
219 case StreamPurpose::ModuleStream:
220 case StreamPurpose::NamedStream:
221 default:
222 break;
223 }
Zachary Turnerea40f402018-03-29 16:28:20 +0000224}
225
226void ExplainOutputStyle::explainStreamDirectoryOffset() {
227 auto DirectoryBlocks = File.getDirectoryBlockArray();
228 const auto &Layout = File.getMsfLayout();
229 uint32_t StreamOff =
230 getOffsetInStream(DirectoryBlocks, FileOffset, File.getBlockSize());
231 P.formatLine("Address is at offset {0}/{1} of Stream Directory{2}.",
232 StreamOff, uint32_t(Layout.SB->NumDirectoryBytes),
233 uint32_t(StreamOff > Layout.SB->NumDirectoryBytes)
234 ? " in unused space"
235 : "");
236}
237
238void ExplainOutputStyle::explainUnknownBlock() {
239 P.formatLine("Address has unknown purpose.");
240}
Zachary Turnerd5cf5cf2018-03-30 17:16:50 +0000241
242template <typename T>
243static void printStructField(LinePrinter &P, StringRef Label, T Value) {
244 P.formatLine("which contains {0}.", Label);
245 P.formatLine("The current value is {0}.", Value);
246}
247
248static void explainDbiHeaderOffset(LinePrinter &P, DbiStream &Dbi,
249 uint32_t Offset) {
250 const DbiStreamHeader *Header = Dbi.getHeader();
251 assert(Header != nullptr);
252
253 if (Offset < endof(DbiStreamHeader, VersionSignature))
254 printStructField(P, "the DBI Stream Version Signature",
255 int32_t(Header->VersionSignature));
256 else if (Offset < endof(DbiStreamHeader, VersionHeader))
257 printStructField(P, "the DBI Stream Version Header",
258 uint32_t(Header->VersionHeader));
259 else if (Offset < endof(DbiStreamHeader, Age))
260 printStructField(P, "the age of the DBI Stream", uint32_t(Header->Age));
261 else if (Offset < endof(DbiStreamHeader, GlobalSymbolStreamIndex))
262 printStructField(P, "the index of the Global Symbol Stream",
263 uint16_t(Header->GlobalSymbolStreamIndex));
264 else if (Offset < endof(DbiStreamHeader, BuildNumber))
265 printStructField(P, "the build number", uint16_t(Header->BuildNumber));
266 else if (Offset < endof(DbiStreamHeader, PublicSymbolStreamIndex))
267 printStructField(P, "the index of the Public Symbol Stream",
268 uint16_t(Header->PublicSymbolStreamIndex));
269 else if (Offset < endof(DbiStreamHeader, PdbDllVersion))
270 printStructField(P, "the version of mspdb.dll",
271 uint16_t(Header->PdbDllVersion));
272 else if (Offset < endof(DbiStreamHeader, SymRecordStreamIndex))
273 printStructField(P, "the index of the Symbol Record Stream",
274 uint16_t(Header->SymRecordStreamIndex));
275 else if (Offset < endof(DbiStreamHeader, PdbDllRbld))
276 printStructField(P, "the rbld of mspdb.dll", uint16_t(Header->PdbDllRbld));
277 else if (Offset < endof(DbiStreamHeader, ModiSubstreamSize))
278 printStructField(P, "the size of the Module Info Substream",
279 int32_t(Header->ModiSubstreamSize));
280 else if (Offset < endof(DbiStreamHeader, SecContrSubstreamSize))
281 printStructField(P, "the size of the Section Contribution Substream",
282 int32_t(Header->SecContrSubstreamSize));
283 else if (Offset < endof(DbiStreamHeader, SectionMapSize))
284 printStructField(P, "the size of the Section Map Substream",
285 int32_t(Header->SectionMapSize));
286 else if (Offset < endof(DbiStreamHeader, FileInfoSize))
287 printStructField(P, "the size of the File Info Substream",
288 int32_t(Header->FileInfoSize));
289 else if (Offset < endof(DbiStreamHeader, TypeServerSize))
290 printStructField(P, "the size of the Type Server Map",
291 int32_t(Header->TypeServerSize));
292 else if (Offset < endof(DbiStreamHeader, MFCTypeServerIndex))
293 printStructField(P, "the index of the MFC Type Server stream",
294 uint32_t(Header->MFCTypeServerIndex));
295 else if (Offset < endof(DbiStreamHeader, OptionalDbgHdrSize))
296 printStructField(P, "the size of the Optional Debug Stream array",
297 int32_t(Header->OptionalDbgHdrSize));
298 else if (Offset < endof(DbiStreamHeader, ECSubstreamSize))
299 printStructField(P, "the size of the Edit & Continue Substream",
300 int32_t(Header->ECSubstreamSize));
301 else if (Offset < endof(DbiStreamHeader, Flags))
302 printStructField(P, "the DBI Stream flags", uint16_t(Header->Flags));
303 else if (Offset < endof(DbiStreamHeader, MachineType))
304 printStructField(P, "the machine type", uint16_t(Header->MachineType));
305 else if (Offset < endof(DbiStreamHeader, Reserved))
306 printStructField(P, "reserved data", uint32_t(Header->Reserved));
307}
308
309static void explainDbiModiSubstreamOffset(LinePrinter &P, DbiStream &Dbi,
310 uint32_t Offset) {
311 VarStreamArray<DbiModuleDescriptor> ModuleDescriptors;
312 BinaryStreamRef ModiSubstreamData = Dbi.getModiSubstreamData().StreamData;
313 BinaryStreamReader Reader(ModiSubstreamData);
314
315 cantFail(Reader.readArray(ModuleDescriptors, ModiSubstreamData.getLength()));
316 auto Prev = ModuleDescriptors.begin();
317 assert(Prev.offset() == 0);
318 auto Current = Prev;
319 uint32_t Index = 0;
320 while (true) {
321 Prev = Current;
322 ++Current;
323 if (Current == ModuleDescriptors.end() || Offset < Current.offset())
324 break;
325 ++Index;
326 }
327
328 DbiModuleDescriptor &Descriptor = *Prev;
329 P.formatLine("which contains the descriptor for module {0} ({1}).", Index,
330 Descriptor.getModuleName());
331}
332
333template <typename T>
334static void dontExplain(LinePrinter &Printer, T &Stream, uint32_t Offset) {}
335
336template <typename T, typename SubstreamRangeT>
337static void explainSubstreamOffset(LinePrinter &P, uint32_t OffsetInStream,
338 T &Stream,
339 const SubstreamRangeT &Substreams) {
340 uint32_t SubOffset = OffsetInStream;
341 for (const auto &Entry : Substreams) {
342 if (Entry.Size == 0)
343 continue;
344 if (SubOffset < Entry.Size) {
345 P.formatLine("address is at offset {0}/{1} of the {2}.", SubOffset,
346 Entry.Size, Entry.Label);
347 Entry.Explain(P, Stream, SubOffset);
348 return;
349 }
350 SubOffset -= Entry.Size;
351 }
352}
353
354void ExplainOutputStyle::explainDbiStream(uint32_t StreamIdx,
355 uint32_t OffsetInStream) {
356 P.printLine("Within the DBI stream:");
357 DbiStream &Dbi = cantFail(File.getPDBDbiStream());
358 AutoIndent Indent(P);
359 const DbiStreamHeader *Header = Dbi.getHeader();
360 assert(Header != nullptr);
361
362 struct SubstreamInfo {
363 uint32_t Size;
364 StringRef Label;
365 void (*Explain)(LinePrinter &, DbiStream &, uint32_t);
366 } Substreams[] = {
367 {sizeof(DbiStreamHeader), "DBI Stream Header", explainDbiHeaderOffset},
368 {Header->ModiSubstreamSize, "Module Info Substream",
369 explainDbiModiSubstreamOffset},
370 {Header->SecContrSubstreamSize, "Section Contribution Substream",
371 dontExplain<DbiStream>},
372 {Header->SectionMapSize, "Section Map", dontExplain<DbiStream>},
373 {Header->FileInfoSize, "File Info Substream", dontExplain<DbiStream>},
374 {Header->TypeServerSize, "Type Server Map Substream",
375 dontExplain<DbiStream>},
376 {Header->ECSubstreamSize, "Edit & Continue Substream",
377 dontExplain<DbiStream>},
378 {Header->OptionalDbgHdrSize, "Optional Debug Stream Array",
379 dontExplain<DbiStream>},
380 };
381
382 explainSubstreamOffset(P, OffsetInStream, Dbi, Substreams);
383}
384
385static void explainPdbStreamHeaderOffset(LinePrinter &P, InfoStream &Info,
386 uint32_t Offset) {
387 const InfoStreamHeader *Header = Info.getHeader();
388 assert(Header != nullptr);
389
390 if (Offset < endof(InfoStreamHeader, Version))
391 printStructField(P, "the PDB Stream Version Signature",
392 uint32_t(Header->Version));
393 else if (Offset < endof(InfoStreamHeader, Signature))
394 printStructField(P, "the signature of the PDB Stream",
395 uint32_t(Header->Signature));
396 else if (Offset < endof(InfoStreamHeader, Age))
397 printStructField(P, "the age of the PDB", uint32_t(Header->Age));
398 else if (Offset < endof(InfoStreamHeader, Guid))
399 printStructField(P, "the guid of the PDB", fmt_guid(Header->Guid.Guid));
400}
401
402void ExplainOutputStyle::explainPdbStream(uint32_t StreamIdx,
403 uint32_t OffsetInStream) {
404 P.printLine("Within the PDB stream:");
405 InfoStream &Info = cantFail(File.getPDBInfoStream());
406 AutoIndent Indent(P);
407
408 struct SubstreamInfo {
409 uint32_t Size;
410 StringRef Label;
411 void (*Explain)(LinePrinter &, InfoStream &, uint32_t);
412 } Substreams[] = {{sizeof(InfoStreamHeader), "PDB Stream Header",
413 explainPdbStreamHeaderOffset},
414 {Info.getNamedStreamMapByteSize(), "Named Stream Map",
415 dontExplain<InfoStream>},
416 {Info.getStreamSize(), "PDB Feature Signatures",
417 dontExplain<InfoStream>}};
418
419 explainSubstreamOffset(P, OffsetInStream, Info, Substreams);
420}