blob: 06ca694174b0d7cfc0d7d2d379f5ea12d8d32432 [file] [log] [blame]
Zachary Turnerbac69d32016-07-22 19:56:05 +00001//===- MappedBlockStream.cpp - Reads stream data from an MSF file ---------===//
Zachary Turner6ba65de2016-04-29 17:22:58 +00002//
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
Zachary Turnerbac69d32016-07-22 19:56:05 +000010#include "llvm/DebugInfo/Msf/MappedBlockStream.h"
Zachary Turner199f48a2016-07-28 19:11:09 +000011
12#include "llvm/DebugInfo/Msf/IMsfFile.h"
Zachary Turnerbac69d32016-07-22 19:56:05 +000013#include "llvm/DebugInfo/Msf/MsfError.h"
Zachary Turner199f48a2016-07-28 19:11:09 +000014#include "llvm/DebugInfo/Msf/MsfStreamLayout.h"
Zachary Turner6ba65de2016-04-29 17:22:58 +000015
16using namespace llvm;
Zachary Turnerbac69d32016-07-22 19:56:05 +000017using namespace llvm::msf;
Zachary Turner6ba65de2016-04-29 17:22:58 +000018
Zachary Turnera1657a92016-06-08 17:26:39 +000019namespace {
Zachary Turnerbac69d32016-07-22 19:56:05 +000020// This exists so that we can use make_unique (which requires a public default
21// constructor, while still keeping the constructor of MappedBlockStream
22// protected, forcing users to go through the `create` interface.
Zachary Turnera1657a92016-06-08 17:26:39 +000023class MappedBlockStreamImpl : public MappedBlockStream {
24public:
Zachary Turner199f48a2016-07-28 19:11:09 +000025 MappedBlockStreamImpl(const MsfStreamLayout &Layout, const IMsfFile &File)
26 : MappedBlockStream(Layout, File) {}
Zachary Turnera1657a92016-06-08 17:26:39 +000027};
28}
29
Zachary Turner5acb4ac2016-06-10 05:09:12 +000030typedef std::pair<uint32_t, uint32_t> Interval;
31static Interval intersect(const Interval &I1, const Interval &I2) {
32 return std::make_pair(std::max(I1.first, I2.first),
33 std::min(I1.second, I2.second));
34}
35
Zachary Turner199f48a2016-07-28 19:11:09 +000036MappedBlockStream::MappedBlockStream(const MsfStreamLayout &Layout,
Zachary Turnerbac69d32016-07-22 19:56:05 +000037 const IMsfFile &File)
Zachary Turner199f48a2016-07-28 19:11:09 +000038 : Msf(File), Layout(Layout) {}
Zachary Turner6ba65de2016-04-29 17:22:58 +000039
Zachary Turner8dbe3622016-05-27 01:54:44 +000040Error MappedBlockStream::readBytes(uint32_t Offset, uint32_t Size,
41 ArrayRef<uint8_t> &Buffer) const {
42 // Make sure we aren't trying to read beyond the end of the stream.
Zachary Turner199f48a2016-07-28 19:11:09 +000043 if (Size > Layout.Length)
Zachary Turnerbac69d32016-07-22 19:56:05 +000044 return make_error<MsfError>(msf_error_code::insufficient_buffer);
Zachary Turner199f48a2016-07-28 19:11:09 +000045 if (Offset > Layout.Length - Size)
Zachary Turnerbac69d32016-07-22 19:56:05 +000046 return make_error<MsfError>(msf_error_code::insufficient_buffer);
Zachary Turner8dbe3622016-05-27 01:54:44 +000047
48 if (tryReadContiguously(Offset, Size, Buffer))
49 return Error::success();
50
51 auto CacheIter = CacheMap.find(Offset);
52 if (CacheIter != CacheMap.end()) {
Zachary Turner5acb4ac2016-06-10 05:09:12 +000053 // Try to find an alloc that was large enough for this request.
54 for (auto &Entry : CacheIter->second) {
55 if (Entry.size() >= Size) {
56 Buffer = Entry.slice(0, Size);
57 return Error::success();
58 }
59 }
60 }
61
62 // We couldn't find a buffer that started at the correct offset (the most
63 // common scenario). Try to see if there is a buffer that starts at some
64 // other offset but overlaps the desired range.
65 for (auto &CacheItem : CacheMap) {
66 Interval RequestExtent = std::make_pair(Offset, Offset + Size);
67
68 // We already checked this one on the fast path above.
69 if (CacheItem.first == Offset)
70 continue;
71 // If the initial extent of the cached item is beyond the ending extent
72 // of the request, there is no overlap.
73 if (CacheItem.first >= Offset + Size)
74 continue;
75
76 // We really only have to check the last item in the list, since we append
77 // in order of increasing length.
78 if (CacheItem.second.empty())
79 continue;
80
81 auto CachedAlloc = CacheItem.second.back();
82 // If the initial extent of the request is beyond the ending extent of
83 // the cached item, there is no overlap.
84 Interval CachedExtent =
85 std::make_pair(CacheItem.first, CacheItem.first + CachedAlloc.size());
86 if (RequestExtent.first >= CachedExtent.first + CachedExtent.second)
87 continue;
88
89 Interval Intersection = intersect(CachedExtent, RequestExtent);
90 // Only use this if the entire request extent is contained in the cached
91 // extent.
92 if (Intersection != RequestExtent)
93 continue;
94
95 uint32_t CacheRangeOffset =
96 AbsoluteDifference(CachedExtent.first, Intersection.first);
97 Buffer = CachedAlloc.slice(CacheRangeOffset, Size);
Zachary Turner8dbe3622016-05-27 01:54:44 +000098 return Error::success();
99 }
100
101 // Otherwise allocate a large enough buffer in the pool, memcpy the data
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000102 // into it, and return an ArrayRef to that. Do not touch existing pool
103 // allocations, as existing clients may be holding a pointer which must
104 // not be invalidated.
Zachary Turner97609bb2016-06-10 21:47:26 +0000105 uint8_t *WriteBuffer = static_cast<uint8_t *>(Pool.Allocate(Size, 8));
Zachary Turner8dbe3622016-05-27 01:54:44 +0000106 if (auto EC = readBytes(Offset, MutableArrayRef<uint8_t>(WriteBuffer, Size)))
107 return EC;
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000108
109 if (CacheIter != CacheMap.end()) {
110 CacheIter->second.emplace_back(WriteBuffer, Size);
111 } else {
112 std::vector<CacheEntry> List;
113 List.emplace_back(WriteBuffer, Size);
114 CacheMap.insert(std::make_pair(Offset, List));
115 }
Zachary Turner8dbe3622016-05-27 01:54:44 +0000116 Buffer = ArrayRef<uint8_t>(WriteBuffer, Size);
117 return Error::success();
118}
119
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000120Error MappedBlockStream::readLongestContiguousChunk(
121 uint32_t Offset, ArrayRef<uint8_t> &Buffer) const {
122 // Make sure we aren't trying to read beyond the end of the stream.
Zachary Turner199f48a2016-07-28 19:11:09 +0000123 if (Offset >= Layout.Length)
Zachary Turnerbac69d32016-07-22 19:56:05 +0000124 return make_error<MsfError>(msf_error_code::insufficient_buffer);
125 uint32_t First = Offset / Msf.getBlockSize();
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000126 uint32_t Last = First;
127
Zachary Turnerbac69d32016-07-22 19:56:05 +0000128 while (Last < Msf.getBlockCount() - 1) {
Zachary Turner199f48a2016-07-28 19:11:09 +0000129 if (Layout.Blocks[Last] != Layout.Blocks[Last + 1] - 1)
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000130 break;
131 ++Last;
132 }
133
Zachary Turnerbac69d32016-07-22 19:56:05 +0000134 uint32_t OffsetInFirstBlock = Offset % Msf.getBlockSize();
135 uint32_t BytesFromFirstBlock = Msf.getBlockSize() - OffsetInFirstBlock;
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000136 uint32_t BlockSpan = Last - First + 1;
137 uint32_t ByteSpan =
Zachary Turnerbac69d32016-07-22 19:56:05 +0000138 BytesFromFirstBlock + (BlockSpan - 1) * Msf.getBlockSize();
Zachary Turner199f48a2016-07-28 19:11:09 +0000139 auto Result = Msf.getBlockData(Layout.Blocks[First], Msf.getBlockSize());
David Majnemer6211b1f2016-07-10 03:34:47 +0000140 if (!Result)
141 return Result.takeError();
142 Buffer = Result->drop_front(OffsetInFirstBlock);
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000143 Buffer = ArrayRef<uint8_t>(Buffer.data(), ByteSpan);
144 return Error::success();
145}
146
Zachary Turner199f48a2016-07-28 19:11:09 +0000147uint32_t MappedBlockStream::getLength() const { return Layout.Length; }
Zachary Turnerd8447992016-06-07 05:28:55 +0000148
Zachary Turnerab58ae82016-06-30 17:43:00 +0000149Error MappedBlockStream::commit() const { return Error::success(); }
150
Zachary Turner8dbe3622016-05-27 01:54:44 +0000151bool MappedBlockStream::tryReadContiguously(uint32_t Offset, uint32_t Size,
152 ArrayRef<uint8_t> &Buffer) const {
153 // Attempt to fulfill the request with a reference directly into the stream.
154 // This can work even if the request crosses a block boundary, provided that
155 // all subsequent blocks are contiguous. For example, a 10k read with a 4k
156 // block size can be filled with a reference if, from the starting offset,
157 // 3 blocks in a row are contiguous.
Zachary Turnerbac69d32016-07-22 19:56:05 +0000158 uint32_t BlockNum = Offset / Msf.getBlockSize();
159 uint32_t OffsetInBlock = Offset % Msf.getBlockSize();
Zachary Turner8dbe3622016-05-27 01:54:44 +0000160 uint32_t BytesFromFirstBlock =
Zachary Turnerbac69d32016-07-22 19:56:05 +0000161 std::min(Size, Msf.getBlockSize() - OffsetInBlock);
Zachary Turner8dbe3622016-05-27 01:54:44 +0000162 uint32_t NumAdditionalBlocks =
Zachary Turnerbac69d32016-07-22 19:56:05 +0000163 llvm::alignTo(Size - BytesFromFirstBlock, Msf.getBlockSize()) /
164 Msf.getBlockSize();
Zachary Turner8dbe3622016-05-27 01:54:44 +0000165
166 uint32_t RequiredContiguousBlocks = NumAdditionalBlocks + 1;
Zachary Turner199f48a2016-07-28 19:11:09 +0000167 uint32_t E = Layout.Blocks[BlockNum];
Zachary Turner8dbe3622016-05-27 01:54:44 +0000168 for (uint32_t I = 0; I < RequiredContiguousBlocks; ++I, ++E) {
Zachary Turner199f48a2016-07-28 19:11:09 +0000169 if (Layout.Blocks[I + BlockNum] != E)
Zachary Turner8dbe3622016-05-27 01:54:44 +0000170 return false;
171 }
172
Zachary Turner199f48a2016-07-28 19:11:09 +0000173 uint32_t FirstBlockAddr = Layout.Blocks[BlockNum];
Zachary Turnerbac69d32016-07-22 19:56:05 +0000174 auto Result = Msf.getBlockData(FirstBlockAddr, Msf.getBlockSize());
David Majnemer6211b1f2016-07-10 03:34:47 +0000175 if (!Result) {
176 consumeError(Result.takeError());
177 return false;
178 }
179 auto Data = Result->drop_front(OffsetInBlock);
Zachary Turnere6fee882016-06-07 20:38:37 +0000180 Buffer = ArrayRef<uint8_t>(Data.data(), Size);
Zachary Turner8dbe3622016-05-27 01:54:44 +0000181 return true;
182}
183
Zachary Turner819e77d2016-05-06 20:51:57 +0000184Error MappedBlockStream::readBytes(uint32_t Offset,
185 MutableArrayRef<uint8_t> Buffer) const {
Zachary Turnerbac69d32016-07-22 19:56:05 +0000186 uint32_t BlockNum = Offset / Msf.getBlockSize();
187 uint32_t OffsetInBlock = Offset % Msf.getBlockSize();
Zachary Turner6ba65de2016-04-29 17:22:58 +0000188
189 // Make sure we aren't trying to read beyond the end of the stream.
Zachary Turner199f48a2016-07-28 19:11:09 +0000190 if (Buffer.size() > Layout.Length)
Zachary Turnerbac69d32016-07-22 19:56:05 +0000191 return make_error<MsfError>(msf_error_code::insufficient_buffer);
Zachary Turner199f48a2016-07-28 19:11:09 +0000192 if (Offset > Layout.Length - Buffer.size())
Zachary Turnerbac69d32016-07-22 19:56:05 +0000193 return make_error<MsfError>(msf_error_code::insufficient_buffer);
Zachary Turner6ba65de2016-04-29 17:22:58 +0000194
195 uint32_t BytesLeft = Buffer.size();
196 uint32_t BytesWritten = 0;
197 uint8_t *WriteBuffer = Buffer.data();
198 while (BytesLeft > 0) {
Zachary Turner199f48a2016-07-28 19:11:09 +0000199 uint32_t StreamBlockAddr = Layout.Blocks[BlockNum];
Zachary Turner6ba65de2016-04-29 17:22:58 +0000200
Zachary Turnerbac69d32016-07-22 19:56:05 +0000201 auto Result = Msf.getBlockData(StreamBlockAddr, Msf.getBlockSize());
David Majnemer6211b1f2016-07-10 03:34:47 +0000202 if (!Result)
203 return Result.takeError();
Zachary Turner6ba65de2016-04-29 17:22:58 +0000204
David Majnemer6211b1f2016-07-10 03:34:47 +0000205 auto Data = *Result;
Zachary Turnere6fee882016-06-07 20:38:37 +0000206 const uint8_t *ChunkStart = Data.data() + OffsetInBlock;
Zachary Turner6ba65de2016-04-29 17:22:58 +0000207 uint32_t BytesInChunk =
Zachary Turnerbac69d32016-07-22 19:56:05 +0000208 std::min(BytesLeft, Msf.getBlockSize() - OffsetInBlock);
Zachary Turner6ba65de2016-04-29 17:22:58 +0000209 ::memcpy(WriteBuffer + BytesWritten, ChunkStart, BytesInChunk);
210
211 BytesWritten += BytesInChunk;
212 BytesLeft -= BytesInChunk;
213 ++BlockNum;
214 OffsetInBlock = 0;
215 }
216
Zachary Turner819e77d2016-05-06 20:51:57 +0000217 return Error::success();
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000218}
Zachary Turnerf5c59652016-05-03 00:28:21 +0000219
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000220Error MappedBlockStream::writeBytes(uint32_t Offset,
221 ArrayRef<uint8_t> Buffer) const {
222 // Make sure we aren't trying to write beyond the end of the stream.
Zachary Turner199f48a2016-07-28 19:11:09 +0000223 if (Buffer.size() > Layout.Length)
Zachary Turnerbac69d32016-07-22 19:56:05 +0000224 return make_error<MsfError>(msf_error_code::insufficient_buffer);
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000225
Zachary Turner199f48a2016-07-28 19:11:09 +0000226 if (Offset > Layout.Length - Buffer.size())
Zachary Turnerbac69d32016-07-22 19:56:05 +0000227 return make_error<MsfError>(msf_error_code::insufficient_buffer);
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000228
Zachary Turnerbac69d32016-07-22 19:56:05 +0000229 uint32_t BlockNum = Offset / Msf.getBlockSize();
230 uint32_t OffsetInBlock = Offset % Msf.getBlockSize();
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000231
232 uint32_t BytesLeft = Buffer.size();
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000233 uint32_t BytesWritten = 0;
234 while (BytesLeft > 0) {
Zachary Turner199f48a2016-07-28 19:11:09 +0000235 uint32_t StreamBlockAddr = Layout.Blocks[BlockNum];
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000236 uint32_t BytesToWriteInChunk =
Zachary Turnerbac69d32016-07-22 19:56:05 +0000237 std::min(BytesLeft, Msf.getBlockSize() - OffsetInBlock);
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000238
239 const uint8_t *Chunk = Buffer.data() + BytesWritten;
240 ArrayRef<uint8_t> ChunkData(Chunk, BytesToWriteInChunk);
Zachary Turnerbac69d32016-07-22 19:56:05 +0000241 if (auto EC = Msf.setBlockData(StreamBlockAddr, OffsetInBlock, ChunkData))
Zachary Turner5acb4ac2016-06-10 05:09:12 +0000242 return EC;
243
244 BytesLeft -= BytesToWriteInChunk;
245 BytesWritten += BytesToWriteInChunk;
246 ++BlockNum;
247 OffsetInBlock = 0;
248 }
249
250 // If this write overlapped a read which previously came from the pool,
251 // someone may still be holding a pointer to that alloc which is now invalid.
252 // Compute the overlapping range and update the cache entry, so any
253 // outstanding buffers are automatically updated.
254 for (const auto &MapEntry : CacheMap) {
255 // If the end of the written extent precedes the beginning of the cached
256 // extent, ignore this map entry.
257 if (Offset + BytesWritten < MapEntry.first)
258 continue;
259 for (const auto &Alloc : MapEntry.second) {
260 // If the end of the cached extent precedes the beginning of the written
261 // extent, ignore this alloc.
262 if (MapEntry.first + Alloc.size() < Offset)
263 continue;
264
265 // If we get here, they are guaranteed to overlap.
266 Interval WriteInterval = std::make_pair(Offset, Offset + BytesWritten);
267 Interval CachedInterval =
268 std::make_pair(MapEntry.first, MapEntry.first + Alloc.size());
269 // If they overlap, we need to write the new data into the overlapping
270 // range.
271 auto Intersection = intersect(WriteInterval, CachedInterval);
272 assert(Intersection.first <= Intersection.second);
273
274 uint32_t Length = Intersection.second - Intersection.first;
275 uint32_t SrcOffset =
276 AbsoluteDifference(WriteInterval.first, Intersection.first);
277 uint32_t DestOffset =
278 AbsoluteDifference(CachedInterval.first, Intersection.first);
279 ::memcpy(Alloc.data() + DestOffset, Buffer.data() + SrcOffset, Length);
280 }
281 }
282
283 return Error::success();
Zachary Turnerf5c59652016-05-03 00:28:21 +0000284}
Zachary Turner90b8b8d2016-05-31 22:41:52 +0000285
286uint32_t MappedBlockStream::getNumBytesCopied() const {
287 return static_cast<uint32_t>(Pool.getBytesAllocated());
288}
Zachary Turnera1657a92016-06-08 17:26:39 +0000289
290Expected<std::unique_ptr<MappedBlockStream>>
291MappedBlockStream::createIndexedStream(uint32_t StreamIdx,
Zachary Turnerbac69d32016-07-22 19:56:05 +0000292 const IMsfFile &File) {
Zachary Turnera1657a92016-06-08 17:26:39 +0000293 if (StreamIdx >= File.getNumStreams())
Zachary Turnerbac69d32016-07-22 19:56:05 +0000294 return make_error<MsfError>(msf_error_code::no_stream);
Zachary Turner199f48a2016-07-28 19:11:09 +0000295 MsfStreamLayout L;
296 L.Blocks = File.getStreamBlockList(StreamIdx);
297 L.Length = File.getStreamByteSize(StreamIdx);
298 return llvm::make_unique<MappedBlockStreamImpl>(L, File);
Zachary Turnera1657a92016-06-08 17:26:39 +0000299}
300
301Expected<std::unique_ptr<MappedBlockStream>>
Zachary Turnerbac69d32016-07-22 19:56:05 +0000302MappedBlockStream::createDirectoryStream(uint32_t Length,
303 ArrayRef<support::ulittle32_t> Blocks,
304 const IMsfFile &File) {
Zachary Turner199f48a2016-07-28 19:11:09 +0000305 MsfStreamLayout L;
306 L.Blocks = Blocks;
307 L.Length = Length;
308
309 return llvm::make_unique<MappedBlockStreamImpl>(L, File);
Zachary Turnera1657a92016-06-08 17:26:39 +0000310}