blob: fecefbfeb8d526cafa7a3391333522e960d6ff43 [file] [log] [blame]
Zachary Turnerf52a8992016-07-15 20:43:38 +00001//===- MSFBuilder.cpp - MSF Directory & Metadata Builder --------*- 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 "llvm/DebugInfo/PDB/Raw/MsfBuilder.h"
11#include "llvm/DebugInfo/PDB/Raw/RawError.h"
12
13using namespace llvm;
14using namespace llvm::pdb;
15using namespace llvm::pdb::msf;
16using namespace llvm::support;
17
18namespace {
19const uint32_t kSuperBlockBlock = 0;
20const uint32_t kDefaultBlockMapAddr = 1;
21}
22
23MsfBuilder::MsfBuilder(uint32_t BlockSize, uint32_t MinBlockCount, bool CanGrow,
24 BumpPtrAllocator &Allocator)
Saleem Abdulrasoolea6a4fe2016-07-15 21:10:31 +000025 : Allocator(Allocator), IsGrowable(CanGrow), BlockSize(BlockSize),
26 MininumBlocks(MinBlockCount), BlockMapAddr(kDefaultBlockMapAddr),
Zachary Turnerfaa554b2016-07-15 22:16:56 +000027 FreeBlocks(std::max(MinBlockCount, 2U), true) {
Zachary Turnerf52a8992016-07-15 20:43:38 +000028 FreeBlocks[kSuperBlockBlock] = false;
29 FreeBlocks[BlockMapAddr] = false;
30}
31
32Expected<MsfBuilder> MsfBuilder::create(BumpPtrAllocator &Allocator,
33 uint32_t BlockSize,
34 uint32_t MinBlockCount, bool CanGrow) {
35 if (!msf::isValidBlockSize(BlockSize))
36 return make_error<RawError>(raw_error_code::unspecified,
37 "The requested block size is unsupported");
38
39 return MsfBuilder(BlockSize, MinBlockCount, CanGrow, Allocator);
40}
41
42Error MsfBuilder::setBlockMapAddr(uint32_t Addr) {
43 if (Addr == BlockMapAddr)
44 return Error::success();
45
46 if (Addr >= FreeBlocks.size()) {
47 if (!IsGrowable)
48 return make_error<RawError>(raw_error_code::unspecified,
49 "Cannot grow the number of blocks");
50 FreeBlocks.resize(Addr + 1);
51 }
52
53 if (!isBlockFree(Addr))
54 return make_error<RawError>(raw_error_code::unspecified,
55 "Attempt to reuse an allocated block");
56 FreeBlocks[BlockMapAddr] = true;
57 FreeBlocks[Addr] = false;
58 BlockMapAddr = Addr;
59 return Error::success();
60}
61
Zachary Turnerfaa554b2016-07-15 22:16:56 +000062void MsfBuilder::setUnknown0(uint32_t Unk0) { Unknown0 = Unk0; }
63
64void MsfBuilder::setUnknown1(uint32_t Unk1) { Unknown1 = Unk1; }
65
66Error MsfBuilder::setDirectoryBlocksHint(ArrayRef<uint32_t> DirBlocks) {
67 for (auto B : DirectoryBlocks)
68 FreeBlocks[B] = true;
69 for (auto B : DirBlocks) {
70 if (!isBlockFree(B)) {
71 return make_error<RawError>(raw_error_code::unspecified,
72 "Attempt to reuse an allocated block");
73 }
74 FreeBlocks[B] = false;
75 }
76
77 DirectoryBlocks = DirBlocks;
78 return Error::success();
79}
80
Zachary Turnerf52a8992016-07-15 20:43:38 +000081Error MsfBuilder::allocateBlocks(uint32_t NumBlocks,
82 MutableArrayRef<uint32_t> Blocks) {
83 if (NumBlocks == 0)
84 return Error::success();
85
86 uint32_t NumFreeBlocks = FreeBlocks.count();
87 if (NumFreeBlocks < NumBlocks) {
88 if (!IsGrowable)
89 return make_error<RawError>(raw_error_code::unspecified,
90 "There are no free Blocks in the file");
91 uint32_t AllocBlocks = NumBlocks - NumFreeBlocks;
92 FreeBlocks.resize(AllocBlocks + FreeBlocks.size(), true);
93 }
94
95 int I = 0;
96 int Block = FreeBlocks.find_first();
97 do {
98 assert(Block != -1 && "We ran out of Blocks!");
99
100 uint32_t NextBlock = static_cast<uint32_t>(Block);
101 Blocks[I++] = NextBlock;
102 FreeBlocks.reset(NextBlock);
103 Block = FreeBlocks.find_next(Block);
104 } while (--NumBlocks > 0);
105 return Error::success();
106}
107
108uint32_t MsfBuilder::getNumUsedBlocks() const {
109 return getTotalBlockCount() - getNumFreeBlocks();
110}
111
112uint32_t MsfBuilder::getNumFreeBlocks() const { return FreeBlocks.count(); }
113
114uint32_t MsfBuilder::getTotalBlockCount() const { return FreeBlocks.size(); }
115
116bool MsfBuilder::isBlockFree(uint32_t Idx) const { return FreeBlocks[Idx]; }
117
118Error MsfBuilder::addStream(uint32_t Size, ArrayRef<uint32_t> Blocks) {
119 // Add a new stream mapped to the specified blocks. Verify that the specified
120 // blocks are both necessary and sufficient for holding the requested number
121 // of bytes, and verify that all requested blocks are free.
122 uint32_t ReqBlocks = bytesToBlocks(Size, BlockSize);
123 if (ReqBlocks != Blocks.size())
124 return make_error<RawError>(
125 raw_error_code::unspecified,
126 "Incorrect number of blocks for requested stream size");
127 for (auto Block : Blocks) {
128 if (Block >= FreeBlocks.size())
129 FreeBlocks.resize(Block + 1, true);
130
131 if (!FreeBlocks.test(Block))
132 return make_error<RawError>(
133 raw_error_code::unspecified,
134 "Attempt to re-use an already allocated block");
135 }
136 // Mark all the blocks occupied by the new stream as not free.
137 for (auto Block : Blocks) {
138 FreeBlocks.reset(Block);
139 }
140 StreamData.push_back(std::make_pair(Size, Blocks));
141 return Error::success();
142}
143
144Error MsfBuilder::addStream(uint32_t Size) {
145 uint32_t ReqBlocks = bytesToBlocks(Size, BlockSize);
146 std::vector<uint32_t> NewBlocks;
147 NewBlocks.resize(ReqBlocks);
148 if (auto EC = allocateBlocks(ReqBlocks, NewBlocks))
149 return EC;
150 StreamData.push_back(std::make_pair(Size, NewBlocks));
151 return Error::success();
152}
153
154Error MsfBuilder::setStreamSize(uint32_t Idx, uint32_t Size) {
155 uint32_t OldSize = getStreamSize(Idx);
156 if (OldSize == Size)
157 return Error::success();
158
159 uint32_t NewBlocks = bytesToBlocks(Size, BlockSize);
160 uint32_t OldBlocks = bytesToBlocks(OldSize, BlockSize);
161
162 if (NewBlocks > OldBlocks) {
163 uint32_t AddedBlocks = NewBlocks - OldBlocks;
164 // If we're growing, we have to allocate new Blocks.
165 std::vector<uint32_t> AddedBlockList;
166 AddedBlockList.resize(AddedBlocks);
167 if (auto EC = allocateBlocks(AddedBlocks, AddedBlockList))
168 return EC;
169 auto &CurrentBlocks = StreamData[Idx].second;
170 CurrentBlocks.insert(CurrentBlocks.end(), AddedBlockList.begin(),
171 AddedBlockList.end());
172 } else if (OldBlocks > NewBlocks) {
173 // For shrinking, free all the Blocks in the Block map, update the stream
174 // data, then shrink the directory.
175 uint32_t RemovedBlocks = OldBlocks - NewBlocks;
176 auto CurrentBlocks = ArrayRef<uint32_t>(StreamData[Idx].second);
177 auto RemovedBlockList = CurrentBlocks.drop_front(NewBlocks);
178 for (auto P : RemovedBlockList)
179 FreeBlocks[P] = true;
180 StreamData[Idx].second = CurrentBlocks.drop_back(RemovedBlocks);
181 }
182
183 StreamData[Idx].first = Size;
184 return Error::success();
185}
186
187uint32_t MsfBuilder::getNumStreams() const { return StreamData.size(); }
188
189uint32_t MsfBuilder::getStreamSize(uint32_t StreamIdx) const {
190 return StreamData[StreamIdx].first;
191}
192
193ArrayRef<uint32_t> MsfBuilder::getStreamBlocks(uint32_t StreamIdx) const {
194 return StreamData[StreamIdx].second;
195}
196
197uint32_t MsfBuilder::computeDirectoryByteSize() const {
198 // The directory has the following layout, where each item is a ulittle32_t:
199 // NumStreams
200 // StreamSizes[NumStreams]
201 // StreamBlocks[NumStreams][]
202 uint32_t Size = sizeof(ulittle32_t); // NumStreams
203 Size += StreamData.size() * sizeof(ulittle32_t); // StreamSizes
204 for (const auto &D : StreamData) {
205 uint32_t ExpectedNumBlocks = bytesToBlocks(D.first, BlockSize);
206 assert(ExpectedNumBlocks == D.second.size() &&
207 "Unexpected number of blocks");
208 Size += ExpectedNumBlocks * sizeof(ulittle32_t);
209 }
210 return Size;
211}
212
213Expected<Layout> MsfBuilder::build() {
214 Layout L;
215 L.SB = Allocator.Allocate<SuperBlock>();
216 std::memcpy(L.SB->MagicBytes, Magic, sizeof(Magic));
217 L.SB->BlockMapAddr = BlockMapAddr;
218 L.SB->BlockSize = BlockSize;
219 L.SB->NumDirectoryBytes = computeDirectoryByteSize();
Zachary Turnerfaa554b2016-07-15 22:16:56 +0000220 L.SB->Unknown0 = Unknown0;
221 L.SB->Unknown1 = Unknown1;
Zachary Turnerf52a8992016-07-15 20:43:38 +0000222
223 uint32_t NumDirectoryBlocks =
224 bytesToBlocks(L.SB->NumDirectoryBytes, BlockSize);
Zachary Turnerfaa554b2016-07-15 22:16:56 +0000225 if (NumDirectoryBlocks > DirectoryBlocks.size()) {
226 // Our hint wasn't enough to satisfy the entire directory. Allocate
227 // remaining pages.
228 std::vector<uint32_t> ExtraBlocks;
229 uint32_t NumExtraBlocks = NumDirectoryBlocks - DirectoryBlocks.size();
230 ExtraBlocks.resize(NumExtraBlocks);
231 if (auto EC = allocateBlocks(NumExtraBlocks, ExtraBlocks))
232 return std::move(EC);
233 DirectoryBlocks.insert(DirectoryBlocks.end(), ExtraBlocks.begin(),
234 ExtraBlocks.end());
235 } else if (NumDirectoryBlocks < DirectoryBlocks.size()) {
236 uint32_t NumUnnecessaryBlocks = DirectoryBlocks.size() - NumDirectoryBlocks;
237 for (auto B :
238 ArrayRef<uint32_t>(DirectoryBlocks).drop_back(NumUnnecessaryBlocks))
239 FreeBlocks[B] = true;
240 DirectoryBlocks.resize(NumDirectoryBlocks);
241 }
Zachary Turnerf52a8992016-07-15 20:43:38 +0000242
243 // Don't set the number of blocks in the file until after allocating Blocks
244 // for
245 // the directory, since the allocation might cause the file to need to grow.
246 L.SB->NumBlocks = FreeBlocks.size();
247
248 ulittle32_t *DirBlocks = Allocator.Allocate<ulittle32_t>(NumDirectoryBlocks);
249 std::uninitialized_copy_n(DirectoryBlocks.begin(), NumDirectoryBlocks,
250 DirBlocks);
251 L.DirectoryBlocks = ArrayRef<ulittle32_t>(DirBlocks, NumDirectoryBlocks);
252
253 // The stream sizes should be re-allocated as a stable pointer and the stream
254 // map should have each of its entries allocated as a separate stable pointer.
255 if (StreamData.size() > 0) {
256 ulittle32_t *Sizes = Allocator.Allocate<ulittle32_t>(StreamData.size());
257 L.StreamSizes = ArrayRef<ulittle32_t>(Sizes, StreamData.size());
258 L.StreamMap.resize(StreamData.size());
259 for (uint32_t I = 0; I < StreamData.size(); ++I) {
260 Sizes[I] = StreamData[I].first;
261 ulittle32_t *BlockList =
262 Allocator.Allocate<ulittle32_t>(StreamData[I].second.size());
263 std::uninitialized_copy_n(StreamData[I].second.begin(),
264 StreamData[I].second.size(), BlockList);
265 L.StreamMap[I] =
266 ArrayRef<ulittle32_t>(BlockList, StreamData[I].second.size());
267 }
268 }
269
270 return L;
271}