1 //===- MappedBlockStream.cpp - Reads stream data from a PDBFile -----------===// 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 "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h" 11 #include "llvm/DebugInfo/PDB/Raw/DirectoryStreamData.h" 12 #include "llvm/DebugInfo/PDB/Raw/IPDBStreamData.h" 13 #include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h" 14 #include "llvm/DebugInfo/PDB/Raw/PDBFile.h" 15 #include "llvm/DebugInfo/PDB/Raw/RawError.h" 16 17 using namespace llvm; 18 using namespace llvm::pdb; 19 20 namespace { 21 // This exists so that we can use make_unique while still keeping the 22 // constructor of MappedBlockStream private, forcing users to go through 23 // the `create` interface. 24 class MappedBlockStreamImpl : public MappedBlockStream { 25 public: 26 MappedBlockStreamImpl(std::unique_ptr<IPDBStreamData> Data, 27 const IPDBFile &File) 28 : MappedBlockStream(std::move(Data), File) {} 29 }; 30 } 31 32 typedef std::pair<uint32_t, uint32_t> Interval; 33 static Interval intersect(const Interval &I1, const Interval &I2) { 34 return std::make_pair(std::max(I1.first, I2.first), 35 std::min(I1.second, I2.second)); 36 } 37 38 MappedBlockStream::MappedBlockStream(std::unique_ptr<IPDBStreamData> Data, 39 const IPDBFile &Pdb) 40 : Pdb(Pdb), Data(std::move(Data)) {} 41 42 Error MappedBlockStream::readBytes(uint32_t Offset, uint32_t Size, 43 ArrayRef<uint8_t> &Buffer) const { 44 // Make sure we aren't trying to read beyond the end of the stream. 45 if (Size > Data->getLength()) 46 return make_error<RawError>(raw_error_code::insufficient_buffer); 47 if (Offset > Data->getLength() - Size) 48 return make_error<RawError>(raw_error_code::insufficient_buffer); 49 50 if (tryReadContiguously(Offset, Size, Buffer)) 51 return Error::success(); 52 53 auto CacheIter = CacheMap.find(Offset); 54 if (CacheIter != CacheMap.end()) { 55 // Try to find an alloc that was large enough for this request. 56 for (auto &Entry : CacheIter->second) { 57 if (Entry.size() >= Size) { 58 Buffer = Entry.slice(0, Size); 59 return Error::success(); 60 } 61 } 62 } 63 64 // We couldn't find a buffer that started at the correct offset (the most 65 // common scenario). Try to see if there is a buffer that starts at some 66 // other offset but overlaps the desired range. 67 for (auto &CacheItem : CacheMap) { 68 Interval RequestExtent = std::make_pair(Offset, Offset + Size); 69 70 // We already checked this one on the fast path above. 71 if (CacheItem.first == Offset) 72 continue; 73 // If the initial extent of the cached item is beyond the ending extent 74 // of the request, there is no overlap. 75 if (CacheItem.first >= Offset + Size) 76 continue; 77 78 // We really only have to check the last item in the list, since we append 79 // in order of increasing length. 80 if (CacheItem.second.empty()) 81 continue; 82 83 auto CachedAlloc = CacheItem.second.back(); 84 // If the initial extent of the request is beyond the ending extent of 85 // the cached item, there is no overlap. 86 Interval CachedExtent = 87 std::make_pair(CacheItem.first, CacheItem.first + CachedAlloc.size()); 88 if (RequestExtent.first >= CachedExtent.first + CachedExtent.second) 89 continue; 90 91 Interval Intersection = intersect(CachedExtent, RequestExtent); 92 // Only use this if the entire request extent is contained in the cached 93 // extent. 94 if (Intersection != RequestExtent) 95 continue; 96 97 uint32_t CacheRangeOffset = 98 AbsoluteDifference(CachedExtent.first, Intersection.first); 99 Buffer = CachedAlloc.slice(CacheRangeOffset, Size); 100 return Error::success(); 101 } 102 103 // Otherwise allocate a large enough buffer in the pool, memcpy the data 104 // into it, and return an ArrayRef to that. Do not touch existing pool 105 // allocations, as existing clients may be holding a pointer which must 106 // not be invalidated. 107 uint8_t *WriteBuffer = static_cast<uint8_t *>(Pool.Allocate(Size, 8)); 108 if (auto EC = readBytes(Offset, MutableArrayRef<uint8_t>(WriteBuffer, Size))) 109 return EC; 110 111 if (CacheIter != CacheMap.end()) { 112 CacheIter->second.emplace_back(WriteBuffer, Size); 113 } else { 114 std::vector<CacheEntry> List; 115 List.emplace_back(WriteBuffer, Size); 116 CacheMap.insert(std::make_pair(Offset, List)); 117 } 118 Buffer = ArrayRef<uint8_t>(WriteBuffer, Size); 119 return Error::success(); 120 } 121 122 Error MappedBlockStream::readLongestContiguousChunk( 123 uint32_t Offset, ArrayRef<uint8_t> &Buffer) const { 124 // Make sure we aren't trying to read beyond the end of the stream. 125 if (Offset >= Data->getLength()) 126 return make_error<RawError>(raw_error_code::insufficient_buffer); 127 uint32_t First = Offset / Pdb.getBlockSize(); 128 uint32_t Last = First; 129 130 auto BlockList = Data->getStreamBlocks(); 131 while (Last < Pdb.getBlockCount() - 1) { 132 if (BlockList[Last] != BlockList[Last + 1] - 1) 133 break; 134 ++Last; 135 } 136 137 uint32_t OffsetInFirstBlock = Offset % Pdb.getBlockSize(); 138 uint32_t BytesFromFirstBlock = Pdb.getBlockSize() - OffsetInFirstBlock; 139 uint32_t BlockSpan = Last - First + 1; 140 uint32_t ByteSpan = 141 BytesFromFirstBlock + (BlockSpan - 1) * Pdb.getBlockSize(); 142 auto Result = Pdb.getBlockData(BlockList[First], Pdb.getBlockSize()); 143 if (!Result) 144 return Result.takeError(); 145 Buffer = Result->drop_front(OffsetInFirstBlock); 146 Buffer = ArrayRef<uint8_t>(Buffer.data(), ByteSpan); 147 return Error::success(); 148 } 149 150 uint32_t MappedBlockStream::getLength() const { return Data->getLength(); } 151 152 Error MappedBlockStream::commit() const { return Error::success(); } 153 154 bool MappedBlockStream::tryReadContiguously(uint32_t Offset, uint32_t Size, 155 ArrayRef<uint8_t> &Buffer) const { 156 // Attempt to fulfill the request with a reference directly into the stream. 157 // This can work even if the request crosses a block boundary, provided that 158 // all subsequent blocks are contiguous. For example, a 10k read with a 4k 159 // block size can be filled with a reference if, from the starting offset, 160 // 3 blocks in a row are contiguous. 161 uint32_t BlockNum = Offset / Pdb.getBlockSize(); 162 uint32_t OffsetInBlock = Offset % Pdb.getBlockSize(); 163 uint32_t BytesFromFirstBlock = 164 std::min(Size, Pdb.getBlockSize() - OffsetInBlock); 165 uint32_t NumAdditionalBlocks = 166 llvm::alignTo(Size - BytesFromFirstBlock, Pdb.getBlockSize()) / 167 Pdb.getBlockSize(); 168 169 auto BlockList = Data->getStreamBlocks(); 170 uint32_t RequiredContiguousBlocks = NumAdditionalBlocks + 1; 171 uint32_t E = BlockList[BlockNum]; 172 for (uint32_t I = 0; I < RequiredContiguousBlocks; ++I, ++E) { 173 if (BlockList[I + BlockNum] != E) 174 return false; 175 } 176 177 uint32_t FirstBlockAddr = BlockList[BlockNum]; 178 auto Result = Pdb.getBlockData(FirstBlockAddr, Pdb.getBlockSize()); 179 if (!Result) { 180 consumeError(Result.takeError()); 181 return false; 182 } 183 auto Data = Result->drop_front(OffsetInBlock); 184 Buffer = ArrayRef<uint8_t>(Data.data(), Size); 185 return true; 186 } 187 188 Error MappedBlockStream::readBytes(uint32_t Offset, 189 MutableArrayRef<uint8_t> Buffer) const { 190 uint32_t BlockNum = Offset / Pdb.getBlockSize(); 191 uint32_t OffsetInBlock = Offset % Pdb.getBlockSize(); 192 193 // Make sure we aren't trying to read beyond the end of the stream. 194 if (Buffer.size() > Data->getLength()) 195 return make_error<RawError>(raw_error_code::insufficient_buffer); 196 if (Offset > Data->getLength() - Buffer.size()) 197 return make_error<RawError>(raw_error_code::insufficient_buffer); 198 199 uint32_t BytesLeft = Buffer.size(); 200 uint32_t BytesWritten = 0; 201 uint8_t *WriteBuffer = Buffer.data(); 202 auto BlockList = Data->getStreamBlocks(); 203 while (BytesLeft > 0) { 204 uint32_t StreamBlockAddr = BlockList[BlockNum]; 205 206 auto Result = Pdb.getBlockData(StreamBlockAddr, Pdb.getBlockSize()); 207 if (!Result) 208 return Result.takeError(); 209 210 auto Data = *Result; 211 const uint8_t *ChunkStart = Data.data() + OffsetInBlock; 212 uint32_t BytesInChunk = 213 std::min(BytesLeft, Pdb.getBlockSize() - OffsetInBlock); 214 ::memcpy(WriteBuffer + BytesWritten, ChunkStart, BytesInChunk); 215 216 BytesWritten += BytesInChunk; 217 BytesLeft -= BytesInChunk; 218 ++BlockNum; 219 OffsetInBlock = 0; 220 } 221 222 return Error::success(); 223 } 224 225 Error MappedBlockStream::writeBytes(uint32_t Offset, 226 ArrayRef<uint8_t> Buffer) const { 227 // Make sure we aren't trying to write beyond the end of the stream. 228 if (Buffer.size() > Data->getLength()) 229 return make_error<RawError>(raw_error_code::insufficient_buffer); 230 231 if (Offset > Data->getLength() - Buffer.size()) 232 return make_error<RawError>(raw_error_code::insufficient_buffer); 233 234 uint32_t BlockNum = Offset / Pdb.getBlockSize(); 235 uint32_t OffsetInBlock = Offset % Pdb.getBlockSize(); 236 237 uint32_t BytesLeft = Buffer.size(); 238 auto BlockList = Data->getStreamBlocks(); 239 uint32_t BytesWritten = 0; 240 while (BytesLeft > 0) { 241 uint32_t StreamBlockAddr = BlockList[BlockNum]; 242 uint32_t BytesToWriteInChunk = 243 std::min(BytesLeft, Pdb.getBlockSize() - OffsetInBlock); 244 245 const uint8_t *Chunk = Buffer.data() + BytesWritten; 246 ArrayRef<uint8_t> ChunkData(Chunk, BytesToWriteInChunk); 247 if (auto EC = Pdb.setBlockData(StreamBlockAddr, OffsetInBlock, ChunkData)) 248 return EC; 249 250 BytesLeft -= BytesToWriteInChunk; 251 BytesWritten += BytesToWriteInChunk; 252 ++BlockNum; 253 OffsetInBlock = 0; 254 } 255 256 // If this write overlapped a read which previously came from the pool, 257 // someone may still be holding a pointer to that alloc which is now invalid. 258 // Compute the overlapping range and update the cache entry, so any 259 // outstanding buffers are automatically updated. 260 for (const auto &MapEntry : CacheMap) { 261 // If the end of the written extent precedes the beginning of the cached 262 // extent, ignore this map entry. 263 if (Offset + BytesWritten < MapEntry.first) 264 continue; 265 for (const auto &Alloc : MapEntry.second) { 266 // If the end of the cached extent precedes the beginning of the written 267 // extent, ignore this alloc. 268 if (MapEntry.first + Alloc.size() < Offset) 269 continue; 270 271 // If we get here, they are guaranteed to overlap. 272 Interval WriteInterval = std::make_pair(Offset, Offset + BytesWritten); 273 Interval CachedInterval = 274 std::make_pair(MapEntry.first, MapEntry.first + Alloc.size()); 275 // If they overlap, we need to write the new data into the overlapping 276 // range. 277 auto Intersection = intersect(WriteInterval, CachedInterval); 278 assert(Intersection.first <= Intersection.second); 279 280 uint32_t Length = Intersection.second - Intersection.first; 281 uint32_t SrcOffset = 282 AbsoluteDifference(WriteInterval.first, Intersection.first); 283 uint32_t DestOffset = 284 AbsoluteDifference(CachedInterval.first, Intersection.first); 285 ::memcpy(Alloc.data() + DestOffset, Buffer.data() + SrcOffset, Length); 286 } 287 } 288 289 return Error::success(); 290 } 291 292 uint32_t MappedBlockStream::getNumBytesCopied() const { 293 return static_cast<uint32_t>(Pool.getBytesAllocated()); 294 } 295 296 Expected<std::unique_ptr<MappedBlockStream>> 297 MappedBlockStream::createIndexedStream(uint32_t StreamIdx, 298 const IPDBFile &File) { 299 if (StreamIdx >= File.getNumStreams()) 300 return make_error<RawError>(raw_error_code::no_stream); 301 302 auto Data = llvm::make_unique<IndexedStreamData>(StreamIdx, File); 303 return llvm::make_unique<MappedBlockStreamImpl>(std::move(Data), File); 304 } 305 306 Expected<std::unique_ptr<MappedBlockStream>> 307 MappedBlockStream::createDirectoryStream(const PDBFile &File) { 308 auto Data = llvm::make_unique<DirectoryStreamData>(File); 309 return llvm::make_unique<MappedBlockStreamImpl>(std::move(Data), File); 310 } 311