blob: 1c9749ad4092618b0ac911ce50e6e816927fe226 [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)
25 : Allocator(Allocator), BlockSize(BlockSize), MininumBlocks(MinBlockCount),
26 IsGrowable(CanGrow), BlockMapAddr(kDefaultBlockMapAddr),
27 FreeBlocks(MinBlockCount + 2U, true) {
28 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
62Error MsfBuilder::allocateBlocks(uint32_t NumBlocks,
63 MutableArrayRef<uint32_t> Blocks) {
64 if (NumBlocks == 0)
65 return Error::success();
66
67 uint32_t NumFreeBlocks = FreeBlocks.count();
68 if (NumFreeBlocks < NumBlocks) {
69 if (!IsGrowable)
70 return make_error<RawError>(raw_error_code::unspecified,
71 "There are no free Blocks in the file");
72 uint32_t AllocBlocks = NumBlocks - NumFreeBlocks;
73 FreeBlocks.resize(AllocBlocks + FreeBlocks.size(), true);
74 }
75
76 int I = 0;
77 int Block = FreeBlocks.find_first();
78 do {
79 assert(Block != -1 && "We ran out of Blocks!");
80
81 uint32_t NextBlock = static_cast<uint32_t>(Block);
82 Blocks[I++] = NextBlock;
83 FreeBlocks.reset(NextBlock);
84 Block = FreeBlocks.find_next(Block);
85 } while (--NumBlocks > 0);
86 return Error::success();
87}
88
89uint32_t MsfBuilder::getNumUsedBlocks() const {
90 return getTotalBlockCount() - getNumFreeBlocks();
91}
92
93uint32_t MsfBuilder::getNumFreeBlocks() const { return FreeBlocks.count(); }
94
95uint32_t MsfBuilder::getTotalBlockCount() const { return FreeBlocks.size(); }
96
97bool MsfBuilder::isBlockFree(uint32_t Idx) const { return FreeBlocks[Idx]; }
98
99Error MsfBuilder::addStream(uint32_t Size, ArrayRef<uint32_t> Blocks) {
100 // Add a new stream mapped to the specified blocks. Verify that the specified
101 // blocks are both necessary and sufficient for holding the requested number
102 // of bytes, and verify that all requested blocks are free.
103 uint32_t ReqBlocks = bytesToBlocks(Size, BlockSize);
104 if (ReqBlocks != Blocks.size())
105 return make_error<RawError>(
106 raw_error_code::unspecified,
107 "Incorrect number of blocks for requested stream size");
108 for (auto Block : Blocks) {
109 if (Block >= FreeBlocks.size())
110 FreeBlocks.resize(Block + 1, true);
111
112 if (!FreeBlocks.test(Block))
113 return make_error<RawError>(
114 raw_error_code::unspecified,
115 "Attempt to re-use an already allocated block");
116 }
117 // Mark all the blocks occupied by the new stream as not free.
118 for (auto Block : Blocks) {
119 FreeBlocks.reset(Block);
120 }
121 StreamData.push_back(std::make_pair(Size, Blocks));
122 return Error::success();
123}
124
125Error MsfBuilder::addStream(uint32_t Size) {
126 uint32_t ReqBlocks = bytesToBlocks(Size, BlockSize);
127 std::vector<uint32_t> NewBlocks;
128 NewBlocks.resize(ReqBlocks);
129 if (auto EC = allocateBlocks(ReqBlocks, NewBlocks))
130 return EC;
131 StreamData.push_back(std::make_pair(Size, NewBlocks));
132 return Error::success();
133}
134
135Error MsfBuilder::setStreamSize(uint32_t Idx, uint32_t Size) {
136 uint32_t OldSize = getStreamSize(Idx);
137 if (OldSize == Size)
138 return Error::success();
139
140 uint32_t NewBlocks = bytesToBlocks(Size, BlockSize);
141 uint32_t OldBlocks = bytesToBlocks(OldSize, BlockSize);
142
143 if (NewBlocks > OldBlocks) {
144 uint32_t AddedBlocks = NewBlocks - OldBlocks;
145 // If we're growing, we have to allocate new Blocks.
146 std::vector<uint32_t> AddedBlockList;
147 AddedBlockList.resize(AddedBlocks);
148 if (auto EC = allocateBlocks(AddedBlocks, AddedBlockList))
149 return EC;
150 auto &CurrentBlocks = StreamData[Idx].second;
151 CurrentBlocks.insert(CurrentBlocks.end(), AddedBlockList.begin(),
152 AddedBlockList.end());
153 } else if (OldBlocks > NewBlocks) {
154 // For shrinking, free all the Blocks in the Block map, update the stream
155 // data, then shrink the directory.
156 uint32_t RemovedBlocks = OldBlocks - NewBlocks;
157 auto CurrentBlocks = ArrayRef<uint32_t>(StreamData[Idx].second);
158 auto RemovedBlockList = CurrentBlocks.drop_front(NewBlocks);
159 for (auto P : RemovedBlockList)
160 FreeBlocks[P] = true;
161 StreamData[Idx].second = CurrentBlocks.drop_back(RemovedBlocks);
162 }
163
164 StreamData[Idx].first = Size;
165 return Error::success();
166}
167
168uint32_t MsfBuilder::getNumStreams() const { return StreamData.size(); }
169
170uint32_t MsfBuilder::getStreamSize(uint32_t StreamIdx) const {
171 return StreamData[StreamIdx].first;
172}
173
174ArrayRef<uint32_t> MsfBuilder::getStreamBlocks(uint32_t StreamIdx) const {
175 return StreamData[StreamIdx].second;
176}
177
178uint32_t MsfBuilder::computeDirectoryByteSize() const {
179 // The directory has the following layout, where each item is a ulittle32_t:
180 // NumStreams
181 // StreamSizes[NumStreams]
182 // StreamBlocks[NumStreams][]
183 uint32_t Size = sizeof(ulittle32_t); // NumStreams
184 Size += StreamData.size() * sizeof(ulittle32_t); // StreamSizes
185 for (const auto &D : StreamData) {
186 uint32_t ExpectedNumBlocks = bytesToBlocks(D.first, BlockSize);
187 assert(ExpectedNumBlocks == D.second.size() &&
188 "Unexpected number of blocks");
189 Size += ExpectedNumBlocks * sizeof(ulittle32_t);
190 }
191 return Size;
192}
193
194Expected<Layout> MsfBuilder::build() {
195 Layout L;
196 L.SB = Allocator.Allocate<SuperBlock>();
197 std::memcpy(L.SB->MagicBytes, Magic, sizeof(Magic));
198 L.SB->BlockMapAddr = BlockMapAddr;
199 L.SB->BlockSize = BlockSize;
200 L.SB->NumDirectoryBytes = computeDirectoryByteSize();
201 L.SB->Unknown0 = 0;
202 L.SB->Unknown1 = 0;
203
204 uint32_t NumDirectoryBlocks =
205 bytesToBlocks(L.SB->NumDirectoryBytes, BlockSize);
206 // The directory blocks should be re-allocated as a stable pointer.
207 std::vector<uint32_t> DirectoryBlocks;
208 DirectoryBlocks.resize(NumDirectoryBlocks);
209 if (auto EC = allocateBlocks(NumDirectoryBlocks, DirectoryBlocks))
210 return std::move(EC);
211
212 // Don't set the number of blocks in the file until after allocating Blocks
213 // for
214 // the directory, since the allocation might cause the file to need to grow.
215 L.SB->NumBlocks = FreeBlocks.size();
216
217 ulittle32_t *DirBlocks = Allocator.Allocate<ulittle32_t>(NumDirectoryBlocks);
218 std::uninitialized_copy_n(DirectoryBlocks.begin(), NumDirectoryBlocks,
219 DirBlocks);
220 L.DirectoryBlocks = ArrayRef<ulittle32_t>(DirBlocks, NumDirectoryBlocks);
221
222 // The stream sizes should be re-allocated as a stable pointer and the stream
223 // map should have each of its entries allocated as a separate stable pointer.
224 if (StreamData.size() > 0) {
225 ulittle32_t *Sizes = Allocator.Allocate<ulittle32_t>(StreamData.size());
226 L.StreamSizes = ArrayRef<ulittle32_t>(Sizes, StreamData.size());
227 L.StreamMap.resize(StreamData.size());
228 for (uint32_t I = 0; I < StreamData.size(); ++I) {
229 Sizes[I] = StreamData[I].first;
230 ulittle32_t *BlockList =
231 Allocator.Allocate<ulittle32_t>(StreamData[I].second.size());
232 std::uninitialized_copy_n(StreamData[I].second.begin(),
233 StreamData[I].second.size(), BlockList);
234 L.StreamMap[I] =
235 ArrayRef<ulittle32_t>(BlockList, StreamData[I].second.size());
236 }
237 }
238
239 return L;
240}