xref: /llvm-project/llvm/tools/llvm-pdbutil/ExplainOutputStyle.cpp (revision f4b6dcf6af9d1ef38f60f112c8ccbf2521e07e88)
1 //===- 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 
16 #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
17 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
18 
19 using namespace llvm;
20 using namespace llvm::codeview;
21 using namespace llvm::msf;
22 using namespace llvm::pdb;
23 
24 ExplainOutputStyle::ExplainOutputStyle(PDBFile &File, uint64_t FileOffset)
25     : File(File), FileOffset(FileOffset),
26       BlockIndex(FileOffset / File.getBlockSize()),
27       OffsetInBlock(FileOffset - BlockIndex * File.getBlockSize()),
28       P(2, false, outs()) {}
29 
30 Error ExplainOutputStyle::dump() {
31   P.formatLine("Explaining file offset {0} of file '{1}'.", FileOffset,
32                File.getFilePath());
33 
34   bool IsAllocated = explainBlockStatus();
35   if (!IsAllocated)
36     return Error::success();
37 
38   AutoIndent Indent(P);
39   if (isSuperBlock())
40     explainSuperBlockOffset();
41   else if (isFpmBlock())
42     explainFpmBlockOffset();
43   else if (isBlockMapBlock())
44     explainBlockMapOffset();
45   else if (isStreamDirectoryBlock())
46     explainStreamDirectoryOffset();
47   else if (auto Index = getBlockStreamIndex())
48     explainStreamOffset(*Index);
49   else
50     explainUnknownBlock();
51   return Error::success();
52 }
53 
54 bool ExplainOutputStyle::isSuperBlock() const { return BlockIndex == 0; }
55 
56 bool ExplainOutputStyle::isFpm1() const {
57   return ((BlockIndex - 1) % File.getBlockSize() == 0);
58 }
59 bool ExplainOutputStyle::isFpm2() const {
60   return ((BlockIndex - 2) % File.getBlockSize() == 0);
61 }
62 
63 bool ExplainOutputStyle::isFpmBlock() const { return isFpm1() || isFpm2(); }
64 
65 bool ExplainOutputStyle::isBlockMapBlock() const {
66   return BlockIndex == File.getBlockMapIndex();
67 }
68 
69 bool ExplainOutputStyle::isStreamDirectoryBlock() const {
70   const auto &Layout = File.getMsfLayout();
71   return llvm::is_contained(Layout.DirectoryBlocks, BlockIndex);
72 }
73 
74 Optional<uint32_t> ExplainOutputStyle::getBlockStreamIndex() const {
75   const auto &Layout = File.getMsfLayout();
76   for (const auto &Entry : enumerate(Layout.StreamMap)) {
77     if (!llvm::is_contained(Entry.value(), BlockIndex))
78       continue;
79     return Entry.index();
80   }
81   return None;
82 }
83 
84 bool ExplainOutputStyle::explainBlockStatus() {
85   if (FileOffset >= File.getFileSize()) {
86     P.formatLine("Address {0} is not in the file (file size = {1}).",
87                  FileOffset, File.getFileSize());
88     return false;
89   }
90   P.formatLine("Block:Offset = {2:X-}:{1:X-4}.", FileOffset, OffsetInBlock,
91                BlockIndex);
92 
93   bool IsFree = File.getMsfLayout().FreePageMap[BlockIndex];
94   P.formatLine("Address is in block {0} ({1}allocated).", BlockIndex,
95                IsFree ? "un" : "");
96   return !IsFree;
97 }
98 
99 #define endof(Class, Field) (offsetof(Class, Field) + sizeof(Class::Field))
100 
101 void ExplainOutputStyle::explainSuperBlockOffset() {
102   P.formatLine("This corresponds to offset {0} of the MSF super block, ",
103                OffsetInBlock);
104   if (OffsetInBlock < endof(SuperBlock, MagicBytes))
105     P.printLine("which is part of the MSF file magic.");
106   else if (OffsetInBlock < endof(SuperBlock, BlockSize)) {
107     P.printLine("which contains the block size of the file.");
108     P.formatLine("The current value is {0}.",
109                  uint32_t(File.getMsfLayout().SB->BlockSize));
110   } else if (OffsetInBlock < endof(SuperBlock, FreeBlockMapBlock)) {
111     P.printLine("which contains the index of the FPM block (e.g. 1 or 2).");
112     P.formatLine("The current value is {0}.",
113                  uint32_t(File.getMsfLayout().SB->FreeBlockMapBlock));
114   } else if (OffsetInBlock < endof(SuperBlock, NumBlocks)) {
115     P.printLine("which contains the number of blocks in the file.");
116     P.formatLine("The current value is {0}.",
117                  uint32_t(File.getMsfLayout().SB->NumBlocks));
118   } else if (OffsetInBlock < endof(SuperBlock, NumDirectoryBytes)) {
119     P.printLine("which contains the number of bytes in the stream directory.");
120     P.formatLine("The current value is {0}.",
121                  uint32_t(File.getMsfLayout().SB->NumDirectoryBytes));
122   } else if (OffsetInBlock < endof(SuperBlock, Unknown1)) {
123     P.printLine("whose purpose is unknown.");
124     P.formatLine("The current value is {0}.",
125                  uint32_t(File.getMsfLayout().SB->Unknown1));
126   } else if (OffsetInBlock < endof(SuperBlock, BlockMapAddr)) {
127     P.printLine("which contains the file offset of the block map.");
128     P.formatLine("The current value is {0}.",
129                  uint32_t(File.getMsfLayout().SB->BlockMapAddr));
130   } else {
131     assert(OffsetInBlock > sizeof(SuperBlock));
132     P.printLine(
133         "which is outside the range of valid data for the super block.");
134   }
135 }
136 
137 static std::string toBinaryString(uint8_t Byte) {
138   char Result[9] = {0};
139   for (int I = 0; I < 8; ++I) {
140     char C = (Byte & 1) ? '1' : '0';
141     Result[I] = C;
142     Byte >>= 1;
143   }
144   return std::string(Result);
145 }
146 
147 void ExplainOutputStyle::explainFpmBlockOffset() {
148   const MSFLayout &Layout = File.getMsfLayout();
149   uint32_t MainFpm = Layout.mainFpmBlock();
150   uint32_t AltFpm = Layout.alternateFpmBlock();
151 
152   assert(isFpmBlock());
153   uint32_t Fpm = isFpm1() ? 1 : 2;
154   uint32_t FpmChunk = BlockIndex / File.getBlockSize();
155   assert((Fpm == MainFpm) || (Fpm == AltFpm));
156   (void)AltFpm;
157   bool IsMain = (Fpm == MainFpm);
158   P.formatLine("Address is in FPM{0} ({1} FPM)", Fpm, IsMain ? "Main" : "Alt");
159   uint32_t DescribedBlockStart =
160       8 * (FpmChunk * File.getBlockSize() + OffsetInBlock);
161   if (DescribedBlockStart > File.getBlockCount()) {
162     P.printLine("Address is in extraneous FPM space.");
163     return;
164   }
165 
166   P.formatLine("Address describes the allocation status of blocks [{0},{1})",
167                DescribedBlockStart, DescribedBlockStart + 8);
168   ArrayRef<uint8_t> Bytes;
169   cantFail(File.getMsfBuffer().readBytes(FileOffset, 1, Bytes));
170   P.formatLine("Status = {0} (Note: 0 = allocated, 1 = free)",
171                toBinaryString(Bytes[0]));
172 }
173 
174 void ExplainOutputStyle::explainBlockMapOffset() {
175   uint64_t BlockMapOffset = File.getBlockMapOffset();
176   uint32_t OffsetInBlock = FileOffset - BlockMapOffset;
177   P.formatLine("Address is at offset {0} of the directory block list",
178                OffsetInBlock);
179 }
180 
181 static uint32_t getOffsetInStream(ArrayRef<support::ulittle32_t> StreamBlocks,
182                                   uint64_t FileOffset, uint32_t BlockSize) {
183   uint32_t BlockIndex = FileOffset / BlockSize;
184   uint32_t OffsetInBlock = FileOffset - BlockIndex * BlockSize;
185 
186   auto Iter = llvm::find(StreamBlocks, BlockIndex);
187   assert(Iter != StreamBlocks.end());
188   uint32_t StreamBlockIndex = std::distance(StreamBlocks.begin(), Iter);
189   return StreamBlockIndex * BlockSize + OffsetInBlock;
190 }
191 
192 void ExplainOutputStyle::explainStreamOffset(uint32_t Stream) {
193   SmallVector<StreamInfo, 12> Streams;
194   discoverStreamPurposes(File, Streams);
195 
196   assert(Stream <= Streams.size());
197   const StreamInfo &S = Streams[Stream];
198   const auto &Layout = File.getStreamLayout(Stream);
199   uint32_t StreamOff =
200       getOffsetInStream(Layout.Blocks, FileOffset, File.getBlockSize());
201   P.formatLine("Address is at offset {0}/{1} of Stream {2} ({3}){4}.",
202                StreamOff, Layout.Length, Stream, S.getLongName(),
203                (StreamOff > Layout.Length) ? " in unused space" : "");
204 }
205 
206 void ExplainOutputStyle::explainStreamDirectoryOffset() {
207   auto DirectoryBlocks = File.getDirectoryBlockArray();
208   const auto &Layout = File.getMsfLayout();
209   uint32_t StreamOff =
210       getOffsetInStream(DirectoryBlocks, FileOffset, File.getBlockSize());
211   P.formatLine("Address is at offset {0}/{1} of Stream Directory{2}.",
212                StreamOff, uint32_t(Layout.SB->NumDirectoryBytes),
213                uint32_t(StreamOff > Layout.SB->NumDirectoryBytes)
214                    ? " in unused space"
215                    : "");
216 }
217 
218 void ExplainOutputStyle::explainUnknownBlock() {
219   P.formatLine("Address has unknown purpose.");
220 }
221