Zachary Turner | 6ba65de | 2016-04-29 17:22:58 +0000 | [diff] [blame] | 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" |
Zachary Turner | a1657a9 | 2016-06-08 17:26:39 +0000 | [diff] [blame] | 11 | #include "llvm/DebugInfo/PDB/Raw/DirectoryStreamData.h" |
Zachary Turner | d844799 | 2016-06-07 05:28:55 +0000 | [diff] [blame] | 12 | #include "llvm/DebugInfo/PDB/Raw/IPDBStreamData.h" |
Zachary Turner | a1657a9 | 2016-06-08 17:26:39 +0000 | [diff] [blame] | 13 | #include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h" |
Zachary Turner | 6ba65de | 2016-04-29 17:22:58 +0000 | [diff] [blame] | 14 | #include "llvm/DebugInfo/PDB/Raw/PDBFile.h" |
Zachary Turner | 819e77d | 2016-05-06 20:51:57 +0000 | [diff] [blame] | 15 | #include "llvm/DebugInfo/PDB/Raw/RawError.h" |
Zachary Turner | 6ba65de | 2016-04-29 17:22:58 +0000 | [diff] [blame] | 16 | |
| 17 | using namespace llvm; |
Zachary Turner | 2f09b50 | 2016-04-29 17:28:47 +0000 | [diff] [blame] | 18 | using namespace llvm::pdb; |
Zachary Turner | 6ba65de | 2016-04-29 17:22:58 +0000 | [diff] [blame] | 19 | |
Zachary Turner | a1657a9 | 2016-06-08 17:26:39 +0000 | [diff] [blame] | 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 | |
Zachary Turner | 5acb4ac | 2016-06-10 05:09:12 +0000 | [diff] [blame] | 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 | |
Zachary Turner | d844799 | 2016-06-07 05:28:55 +0000 | [diff] [blame] | 38 | MappedBlockStream::MappedBlockStream(std::unique_ptr<IPDBStreamData> Data, |
| 39 | const IPDBFile &Pdb) |
| 40 | : Pdb(Pdb), Data(std::move(Data)) {} |
Zachary Turner | 6ba65de | 2016-04-29 17:22:58 +0000 | [diff] [blame] | 41 | |
Zachary Turner | 8dbe362 | 2016-05-27 01:54:44 +0000 | [diff] [blame] | 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. |
Zachary Turner | d844799 | 2016-06-07 05:28:55 +0000 | [diff] [blame] | 45 | if (Size > Data->getLength()) |
Zachary Turner | 8dbe362 | 2016-05-27 01:54:44 +0000 | [diff] [blame] | 46 | return make_error<RawError>(raw_error_code::insufficient_buffer); |
Zachary Turner | d844799 | 2016-06-07 05:28:55 +0000 | [diff] [blame] | 47 | if (Offset > Data->getLength() - Size) |
Zachary Turner | 8dbe362 | 2016-05-27 01:54:44 +0000 | [diff] [blame] | 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()) { |
Zachary Turner | 5acb4ac | 2016-06-10 05:09:12 +0000 | [diff] [blame] | 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); |
Zachary Turner | 8dbe362 | 2016-05-27 01:54:44 +0000 | [diff] [blame] | 100 | return Error::success(); |
| 101 | } |
| 102 | |
| 103 | // Otherwise allocate a large enough buffer in the pool, memcpy the data |
Zachary Turner | 5acb4ac | 2016-06-10 05:09:12 +0000 | [diff] [blame] | 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. |
Zachary Turner | 97609bb | 2016-06-10 21:47:26 +0000 | [diff] [blame] | 107 | uint8_t *WriteBuffer = static_cast<uint8_t *>(Pool.Allocate(Size, 8)); |
Zachary Turner | 8dbe362 | 2016-05-27 01:54:44 +0000 | [diff] [blame] | 108 | if (auto EC = readBytes(Offset, MutableArrayRef<uint8_t>(WriteBuffer, Size))) |
| 109 | return EC; |
Zachary Turner | 5acb4ac | 2016-06-10 05:09:12 +0000 | [diff] [blame] | 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 | } |
Zachary Turner | 8dbe362 | 2016-05-27 01:54:44 +0000 | [diff] [blame] | 118 | Buffer = ArrayRef<uint8_t>(WriteBuffer, Size); |
| 119 | return Error::success(); |
| 120 | } |
| 121 | |
Zachary Turner | 5acb4ac | 2016-06-10 05:09:12 +0000 | [diff] [blame] | 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 | Buffer = Pdb.getBlockData(BlockList[First], Pdb.getBlockSize()); |
| 143 | Buffer = Buffer.drop_front(OffsetInFirstBlock); |
| 144 | Buffer = ArrayRef<uint8_t>(Buffer.data(), ByteSpan); |
| 145 | return Error::success(); |
| 146 | } |
| 147 | |
Zachary Turner | d844799 | 2016-06-07 05:28:55 +0000 | [diff] [blame] | 148 | uint32_t MappedBlockStream::getLength() const { return Data->getLength(); } |
| 149 | |
Zachary Turner | 8dbe362 | 2016-05-27 01:54:44 +0000 | [diff] [blame] | 150 | bool MappedBlockStream::tryReadContiguously(uint32_t Offset, uint32_t Size, |
| 151 | ArrayRef<uint8_t> &Buffer) const { |
| 152 | // Attempt to fulfill the request with a reference directly into the stream. |
| 153 | // This can work even if the request crosses a block boundary, provided that |
| 154 | // all subsequent blocks are contiguous. For example, a 10k read with a 4k |
| 155 | // block size can be filled with a reference if, from the starting offset, |
| 156 | // 3 blocks in a row are contiguous. |
| 157 | uint32_t BlockNum = Offset / Pdb.getBlockSize(); |
| 158 | uint32_t OffsetInBlock = Offset % Pdb.getBlockSize(); |
| 159 | uint32_t BytesFromFirstBlock = |
| 160 | std::min(Size, Pdb.getBlockSize() - OffsetInBlock); |
| 161 | uint32_t NumAdditionalBlocks = |
| 162 | llvm::alignTo(Size - BytesFromFirstBlock, Pdb.getBlockSize()) / |
| 163 | Pdb.getBlockSize(); |
| 164 | |
Zachary Turner | d844799 | 2016-06-07 05:28:55 +0000 | [diff] [blame] | 165 | auto BlockList = Data->getStreamBlocks(); |
Zachary Turner | 8dbe362 | 2016-05-27 01:54:44 +0000 | [diff] [blame] | 166 | uint32_t RequiredContiguousBlocks = NumAdditionalBlocks + 1; |
| 167 | uint32_t E = BlockList[BlockNum]; |
| 168 | for (uint32_t I = 0; I < RequiredContiguousBlocks; ++I, ++E) { |
| 169 | if (BlockList[I + BlockNum] != E) |
| 170 | return false; |
| 171 | } |
| 172 | |
| 173 | uint32_t FirstBlockAddr = BlockList[BlockNum]; |
Zachary Turner | e6fee88 | 2016-06-07 20:38:37 +0000 | [diff] [blame] | 174 | auto Data = Pdb.getBlockData(FirstBlockAddr, Pdb.getBlockSize()); |
| 175 | Data = Data.drop_front(OffsetInBlock); |
| 176 | Buffer = ArrayRef<uint8_t>(Data.data(), Size); |
Zachary Turner | 8dbe362 | 2016-05-27 01:54:44 +0000 | [diff] [blame] | 177 | return true; |
| 178 | } |
| 179 | |
Zachary Turner | 819e77d | 2016-05-06 20:51:57 +0000 | [diff] [blame] | 180 | Error MappedBlockStream::readBytes(uint32_t Offset, |
| 181 | MutableArrayRef<uint8_t> Buffer) const { |
Zachary Turner | 6ba65de | 2016-04-29 17:22:58 +0000 | [diff] [blame] | 182 | uint32_t BlockNum = Offset / Pdb.getBlockSize(); |
| 183 | uint32_t OffsetInBlock = Offset % Pdb.getBlockSize(); |
| 184 | |
| 185 | // Make sure we aren't trying to read beyond the end of the stream. |
Zachary Turner | d844799 | 2016-06-07 05:28:55 +0000 | [diff] [blame] | 186 | if (Buffer.size() > Data->getLength()) |
Zachary Turner | 819e77d | 2016-05-06 20:51:57 +0000 | [diff] [blame] | 187 | return make_error<RawError>(raw_error_code::insufficient_buffer); |
Zachary Turner | d844799 | 2016-06-07 05:28:55 +0000 | [diff] [blame] | 188 | if (Offset > Data->getLength() - Buffer.size()) |
Zachary Turner | 819e77d | 2016-05-06 20:51:57 +0000 | [diff] [blame] | 189 | return make_error<RawError>(raw_error_code::insufficient_buffer); |
Zachary Turner | 6ba65de | 2016-04-29 17:22:58 +0000 | [diff] [blame] | 190 | |
| 191 | uint32_t BytesLeft = Buffer.size(); |
| 192 | uint32_t BytesWritten = 0; |
| 193 | uint8_t *WriteBuffer = Buffer.data(); |
Zachary Turner | d844799 | 2016-06-07 05:28:55 +0000 | [diff] [blame] | 194 | auto BlockList = Data->getStreamBlocks(); |
Zachary Turner | 6ba65de | 2016-04-29 17:22:58 +0000 | [diff] [blame] | 195 | while (BytesLeft > 0) { |
| 196 | uint32_t StreamBlockAddr = BlockList[BlockNum]; |
| 197 | |
Zachary Turner | e6fee88 | 2016-06-07 20:38:37 +0000 | [diff] [blame] | 198 | auto Data = Pdb.getBlockData(StreamBlockAddr, Pdb.getBlockSize()); |
Zachary Turner | 6ba65de | 2016-04-29 17:22:58 +0000 | [diff] [blame] | 199 | |
Zachary Turner | e6fee88 | 2016-06-07 20:38:37 +0000 | [diff] [blame] | 200 | const uint8_t *ChunkStart = Data.data() + OffsetInBlock; |
Zachary Turner | 6ba65de | 2016-04-29 17:22:58 +0000 | [diff] [blame] | 201 | uint32_t BytesInChunk = |
| 202 | std::min(BytesLeft, Pdb.getBlockSize() - OffsetInBlock); |
| 203 | ::memcpy(WriteBuffer + BytesWritten, ChunkStart, BytesInChunk); |
| 204 | |
| 205 | BytesWritten += BytesInChunk; |
| 206 | BytesLeft -= BytesInChunk; |
| 207 | ++BlockNum; |
| 208 | OffsetInBlock = 0; |
| 209 | } |
| 210 | |
Zachary Turner | 819e77d | 2016-05-06 20:51:57 +0000 | [diff] [blame] | 211 | return Error::success(); |
Zachary Turner | 5acb4ac | 2016-06-10 05:09:12 +0000 | [diff] [blame] | 212 | } |
Zachary Turner | f5c5965 | 2016-05-03 00:28:21 +0000 | [diff] [blame] | 213 | |
Zachary Turner | 5acb4ac | 2016-06-10 05:09:12 +0000 | [diff] [blame] | 214 | Error MappedBlockStream::writeBytes(uint32_t Offset, |
| 215 | ArrayRef<uint8_t> Buffer) const { |
| 216 | // Make sure we aren't trying to write beyond the end of the stream. |
| 217 | if (Buffer.size() > Data->getLength()) |
| 218 | return make_error<RawError>(raw_error_code::insufficient_buffer); |
| 219 | |
| 220 | if (Offset > Data->getLength() - Buffer.size()) |
| 221 | return make_error<RawError>(raw_error_code::insufficient_buffer); |
| 222 | |
| 223 | uint32_t BlockNum = Offset / Pdb.getBlockSize(); |
| 224 | uint32_t OffsetInBlock = Offset % Pdb.getBlockSize(); |
| 225 | |
| 226 | uint32_t BytesLeft = Buffer.size(); |
| 227 | auto BlockList = Data->getStreamBlocks(); |
| 228 | uint32_t BytesWritten = 0; |
| 229 | while (BytesLeft > 0) { |
| 230 | uint32_t StreamBlockAddr = BlockList[BlockNum]; |
| 231 | uint32_t BytesToWriteInChunk = |
| 232 | std::min(BytesLeft, Pdb.getBlockSize() - OffsetInBlock); |
| 233 | |
| 234 | const uint8_t *Chunk = Buffer.data() + BytesWritten; |
| 235 | ArrayRef<uint8_t> ChunkData(Chunk, BytesToWriteInChunk); |
| 236 | if (auto EC = Pdb.setBlockData(StreamBlockAddr, OffsetInBlock, ChunkData)) |
| 237 | return EC; |
| 238 | |
| 239 | BytesLeft -= BytesToWriteInChunk; |
| 240 | BytesWritten += BytesToWriteInChunk; |
| 241 | ++BlockNum; |
| 242 | OffsetInBlock = 0; |
| 243 | } |
| 244 | |
| 245 | // If this write overlapped a read which previously came from the pool, |
| 246 | // someone may still be holding a pointer to that alloc which is now invalid. |
| 247 | // Compute the overlapping range and update the cache entry, so any |
| 248 | // outstanding buffers are automatically updated. |
| 249 | for (const auto &MapEntry : CacheMap) { |
| 250 | // If the end of the written extent precedes the beginning of the cached |
| 251 | // extent, ignore this map entry. |
| 252 | if (Offset + BytesWritten < MapEntry.first) |
| 253 | continue; |
| 254 | for (const auto &Alloc : MapEntry.second) { |
| 255 | // If the end of the cached extent precedes the beginning of the written |
| 256 | // extent, ignore this alloc. |
| 257 | if (MapEntry.first + Alloc.size() < Offset) |
| 258 | continue; |
| 259 | |
| 260 | // If we get here, they are guaranteed to overlap. |
| 261 | Interval WriteInterval = std::make_pair(Offset, Offset + BytesWritten); |
| 262 | Interval CachedInterval = |
| 263 | std::make_pair(MapEntry.first, MapEntry.first + Alloc.size()); |
| 264 | // If they overlap, we need to write the new data into the overlapping |
| 265 | // range. |
| 266 | auto Intersection = intersect(WriteInterval, CachedInterval); |
| 267 | assert(Intersection.first <= Intersection.second); |
| 268 | |
| 269 | uint32_t Length = Intersection.second - Intersection.first; |
| 270 | uint32_t SrcOffset = |
| 271 | AbsoluteDifference(WriteInterval.first, Intersection.first); |
| 272 | uint32_t DestOffset = |
| 273 | AbsoluteDifference(CachedInterval.first, Intersection.first); |
| 274 | ::memcpy(Alloc.data() + DestOffset, Buffer.data() + SrcOffset, Length); |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | return Error::success(); |
Zachary Turner | f5c5965 | 2016-05-03 00:28:21 +0000 | [diff] [blame] | 279 | } |
Zachary Turner | 90b8b8d | 2016-05-31 22:41:52 +0000 | [diff] [blame] | 280 | |
| 281 | uint32_t MappedBlockStream::getNumBytesCopied() const { |
| 282 | return static_cast<uint32_t>(Pool.getBytesAllocated()); |
| 283 | } |
Zachary Turner | a1657a9 | 2016-06-08 17:26:39 +0000 | [diff] [blame] | 284 | |
| 285 | Expected<std::unique_ptr<MappedBlockStream>> |
| 286 | MappedBlockStream::createIndexedStream(uint32_t StreamIdx, |
| 287 | const IPDBFile &File) { |
| 288 | if (StreamIdx >= File.getNumStreams()) |
| 289 | return make_error<RawError>(raw_error_code::no_stream); |
| 290 | |
| 291 | auto Data = llvm::make_unique<IndexedStreamData>(StreamIdx, File); |
| 292 | return llvm::make_unique<MappedBlockStreamImpl>(std::move(Data), File); |
| 293 | } |
| 294 | |
| 295 | Expected<std::unique_ptr<MappedBlockStream>> |
| 296 | MappedBlockStream::createDirectoryStream(const PDBFile &File) { |
| 297 | auto Data = llvm::make_unique<DirectoryStreamData>(File); |
| 298 | return llvm::make_unique<MappedBlockStreamImpl>(std::move(Data), File); |
| 299 | } |