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