1 //===- MSFBuilderTest.cpp Tests manipulation of MSF stream metadata ------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "llvm/DebugInfo/MSF/MSFBuilder.h"
10 #include "llvm/DebugInfo/MSF/MSFCommon.h"
11 #include "llvm/Testing/Support/Error.h"
13 #include "gmock/gmock-matchers.h"
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
18 using namespace llvm::msf
;
19 using namespace testing
;
22 class MSFBuilderTest
: public testing::Test
{
24 void initializeSimpleSuperBlock(msf::SuperBlock
&SB
) {
25 initializeSuperBlock(SB
);
27 SB
.NumDirectoryBytes
= 8192;
30 void initializeSuperBlock(msf::SuperBlock
&SB
) {
31 ::memset(&SB
, 0, sizeof(SB
));
33 ::memcpy(SB
.MagicBytes
, msf::Magic
, sizeof(msf::Magic
));
34 SB
.FreeBlockMapBlock
= 1;
37 SB
.NumDirectoryBytes
= 0;
38 SB
.NumBlocks
= 2; // one for the Super Block, one for the directory
41 BumpPtrAllocator Allocator
;
45 TEST_F(MSFBuilderTest
, ValidateSuperBlockAccept
) {
46 // Test that a known good super block passes validation.
48 initializeSuperBlock(SB
);
50 EXPECT_THAT_ERROR(msf::validateSuperBlock(SB
), Succeeded());
53 TEST_F(MSFBuilderTest
, ValidateSuperBlockReject
) {
54 // Test that various known problems cause a super block to be rejected.
56 initializeSimpleSuperBlock(SB
);
60 EXPECT_THAT_ERROR(msf::validateSuperBlock(SB
), Failed());
61 initializeSimpleSuperBlock(SB
);
63 // Block 0 is reserved for super block, can't be occupied by the block map
65 EXPECT_THAT_ERROR(msf::validateSuperBlock(SB
), Failed());
66 initializeSimpleSuperBlock(SB
);
68 // Block sizes have to be powers of 2.
70 EXPECT_THAT_ERROR(msf::validateSuperBlock(SB
), Failed());
71 initializeSimpleSuperBlock(SB
);
73 // The directory itself has a maximum size.
74 SB
.NumDirectoryBytes
= SB
.BlockSize
* SB
.BlockSize
/ 4;
75 EXPECT_THAT_ERROR(msf::validateSuperBlock(SB
), Succeeded());
76 SB
.NumDirectoryBytes
= SB
.NumDirectoryBytes
+ 4;
77 EXPECT_THAT_ERROR(msf::validateSuperBlock(SB
), Failed());
80 TEST_F(MSFBuilderTest
, TestUsedBlocksMarkedAsUsed
) {
81 // Test that when assigning a stream to a known list of blocks, the blocks
82 // are correctly marked as used after adding, but no other incorrect blocks
83 // are accidentally marked as used.
85 std::vector
<uint32_t> Blocks
= {4, 5, 6, 7, 8, 9, 10, 11, 12};
86 // Allocate some extra blocks at the end so we can verify that they're free
87 // after the initialization.
88 uint32_t NumBlocks
= msf::getMinimumBlockCount() + Blocks
.size() + 10;
89 auto ExpectedMsf
= MSFBuilder::create(Allocator
, 4096, NumBlocks
);
90 ASSERT_THAT_EXPECTED(ExpectedMsf
, Succeeded());
91 auto &Msf
= *ExpectedMsf
;
93 EXPECT_THAT_EXPECTED(Msf
.addStream(Blocks
.size() * 4096, Blocks
),
96 for (auto B
: Blocks
) {
97 EXPECT_FALSE(Msf
.isBlockFree(B
));
100 uint32_t FreeBlockStart
= Blocks
.back() + 1;
101 for (uint32_t I
= FreeBlockStart
; I
< NumBlocks
; ++I
) {
102 EXPECT_TRUE(Msf
.isBlockFree(I
));
106 TEST_F(MSFBuilderTest
, TestAddStreamNoDirectoryBlockIncrease
) {
107 // Test that adding a new stream correctly updates the directory. This only
108 // tests the case where the directory *DOES NOT* grow large enough that it
109 // crosses a Block boundary.
110 auto ExpectedMsf
= MSFBuilder::create(Allocator
, 4096);
111 EXPECT_THAT_EXPECTED(ExpectedMsf
, Succeeded());
112 auto &Msf
= *ExpectedMsf
;
114 auto ExpectedL1
= Msf
.generateLayout();
115 EXPECT_THAT_EXPECTED(ExpectedL1
, Succeeded());
116 MSFLayout
&L1
= *ExpectedL1
;
118 auto OldDirBlocks
= L1
.DirectoryBlocks
;
119 EXPECT_EQ(1U, OldDirBlocks
.size());
121 auto ExpectedMsf2
= MSFBuilder::create(Allocator
, 4096);
122 EXPECT_THAT_EXPECTED(ExpectedMsf2
, Succeeded());
123 auto &Msf2
= *ExpectedMsf2
;
125 EXPECT_THAT_EXPECTED(Msf2
.addStream(4000), Succeeded());
126 EXPECT_EQ(1U, Msf2
.getNumStreams());
127 EXPECT_EQ(4000U, Msf2
.getStreamSize(0));
128 auto Blocks
= Msf2
.getStreamBlocks(0);
129 EXPECT_EQ(1U, Blocks
.size());
131 auto ExpectedL2
= Msf2
.generateLayout();
132 EXPECT_THAT_EXPECTED(ExpectedL2
, Succeeded());
133 MSFLayout
&L2
= *ExpectedL2
;
134 auto NewDirBlocks
= L2
.DirectoryBlocks
;
135 EXPECT_EQ(1U, NewDirBlocks
.size());
138 TEST_F(MSFBuilderTest
, TestAddStreamWithDirectoryBlockIncrease
) {
139 // Test that adding a new stream correctly updates the directory. This only
140 // tests the case where the directory *DOES* grow large enough that it
141 // crosses a Block boundary. This is because the newly added stream occupies
142 // so many Blocks that need to be indexed in the directory that the directory
143 // crosses a Block boundary.
144 auto ExpectedMsf
= MSFBuilder::create(Allocator
, 4096);
145 EXPECT_THAT_EXPECTED(ExpectedMsf
, Succeeded());
146 auto &Msf
= *ExpectedMsf
;
148 EXPECT_THAT_EXPECTED(Msf
.addStream(4096 * 4096 / sizeof(uint32_t)),
151 auto ExpectedL1
= Msf
.generateLayout();
152 EXPECT_THAT_EXPECTED(ExpectedL1
, Succeeded());
153 MSFLayout
&L1
= *ExpectedL1
;
154 auto DirBlocks
= L1
.DirectoryBlocks
;
155 EXPECT_EQ(2U, DirBlocks
.size());
158 TEST_F(MSFBuilderTest
, TestGrowStreamNoBlockIncrease
) {
159 // Test growing an existing stream by a value that does not affect the number
160 // of blocks it occupies.
161 auto ExpectedMsf
= MSFBuilder::create(Allocator
, 4096);
162 EXPECT_THAT_EXPECTED(ExpectedMsf
, Succeeded());
163 auto &Msf
= *ExpectedMsf
;
165 EXPECT_THAT_EXPECTED(Msf
.addStream(1024), Succeeded());
166 EXPECT_EQ(1024U, Msf
.getStreamSize(0));
167 auto OldStreamBlocks
= Msf
.getStreamBlocks(0);
168 EXPECT_EQ(1U, OldStreamBlocks
.size());
170 EXPECT_THAT_ERROR(Msf
.setStreamSize(0, 2048), Succeeded());
171 EXPECT_EQ(2048U, Msf
.getStreamSize(0));
172 auto NewStreamBlocks
= Msf
.getStreamBlocks(0);
173 EXPECT_EQ(1U, NewStreamBlocks
.size());
175 EXPECT_EQ(OldStreamBlocks
, NewStreamBlocks
);
178 TEST_F(MSFBuilderTest
, TestGrowStreamWithBlockIncrease
) {
179 // Test that growing an existing stream to a value large enough that it causes
180 // the need to allocate new Blocks to the stream correctly updates the
183 auto ExpectedMsf
= MSFBuilder::create(Allocator
, 4096);
184 EXPECT_THAT_EXPECTED(ExpectedMsf
, Succeeded());
185 auto &Msf
= *ExpectedMsf
;
187 EXPECT_THAT_EXPECTED(Msf
.addStream(2048), Succeeded());
188 EXPECT_EQ(2048U, Msf
.getStreamSize(0));
189 std::vector
<uint32_t> OldStreamBlocks
= Msf
.getStreamBlocks(0);
190 EXPECT_EQ(1U, OldStreamBlocks
.size());
192 EXPECT_THAT_ERROR(Msf
.setStreamSize(0, 6144), Succeeded());
193 EXPECT_EQ(6144U, Msf
.getStreamSize(0));
194 std::vector
<uint32_t> NewStreamBlocks
= Msf
.getStreamBlocks(0);
195 EXPECT_EQ(2U, NewStreamBlocks
.size());
197 EXPECT_EQ(OldStreamBlocks
[0], NewStreamBlocks
[0]);
198 EXPECT_NE(NewStreamBlocks
[0], NewStreamBlocks
[1]);
201 TEST_F(MSFBuilderTest
, TestShrinkStreamNoBlockDecrease
) {
202 // Test that shrinking an existing stream by a value that does not affect the
203 // number of Blocks it occupies makes no changes to stream's block list.
204 auto ExpectedMsf
= MSFBuilder::create(Allocator
, 4096);
205 EXPECT_THAT_EXPECTED(ExpectedMsf
, Succeeded());
206 auto &Msf
= *ExpectedMsf
;
208 EXPECT_THAT_EXPECTED(Msf
.addStream(2048), Succeeded());
209 EXPECT_EQ(2048U, Msf
.getStreamSize(0));
210 std::vector
<uint32_t> OldStreamBlocks
= Msf
.getStreamBlocks(0);
211 EXPECT_EQ(1U, OldStreamBlocks
.size());
213 EXPECT_THAT_ERROR(Msf
.setStreamSize(0, 1024), Succeeded());
214 EXPECT_EQ(1024U, Msf
.getStreamSize(0));
215 std::vector
<uint32_t> NewStreamBlocks
= Msf
.getStreamBlocks(0);
216 EXPECT_EQ(1U, NewStreamBlocks
.size());
218 EXPECT_EQ(OldStreamBlocks
, NewStreamBlocks
);
221 TEST_F(MSFBuilderTest
, TestShrinkStreamWithBlockDecrease
) {
222 // Test that shrinking an existing stream to a value large enough that it
223 // causes the need to deallocate new Blocks to the stream correctly updates
224 // the stream's block list.
225 auto ExpectedMsf
= MSFBuilder::create(Allocator
, 4096);
226 EXPECT_THAT_EXPECTED(ExpectedMsf
, Succeeded());
227 auto &Msf
= *ExpectedMsf
;
229 EXPECT_THAT_EXPECTED(Msf
.addStream(6144), Succeeded());
230 EXPECT_EQ(6144U, Msf
.getStreamSize(0));
231 std::vector
<uint32_t> OldStreamBlocks
= Msf
.getStreamBlocks(0);
232 EXPECT_EQ(2U, OldStreamBlocks
.size());
234 EXPECT_THAT_ERROR(Msf
.setStreamSize(0, 2048), Succeeded());
235 EXPECT_EQ(2048U, Msf
.getStreamSize(0));
236 std::vector
<uint32_t> NewStreamBlocks
= Msf
.getStreamBlocks(0);
237 EXPECT_EQ(1U, NewStreamBlocks
.size());
239 EXPECT_EQ(OldStreamBlocks
[0], NewStreamBlocks
[0]);
242 TEST_F(MSFBuilderTest
, TestRejectReusedStreamBlock
) {
243 // Test that attempting to add a stream and assigning a block that is already
244 // in use by another stream fails.
245 auto ExpectedMsf
= MSFBuilder::create(Allocator
, 4096);
246 EXPECT_THAT_EXPECTED(ExpectedMsf
, Succeeded());
247 auto &Msf
= *ExpectedMsf
;
249 EXPECT_THAT_EXPECTED(Msf
.addStream(6144), Succeeded());
251 std::vector
<uint32_t> Blocks
= {2, 3};
252 EXPECT_THAT_EXPECTED(Msf
.addStream(6144, Blocks
), Failed());
255 TEST_F(MSFBuilderTest
, TestBlockCountsWhenAddingStreams
) {
256 // Test that when adding multiple streams, the number of used and free Blocks
257 // allocated to the MSF file are as expected.
258 auto ExpectedMsf
= MSFBuilder::create(Allocator
, 4096);
259 EXPECT_THAT_EXPECTED(ExpectedMsf
, Succeeded());
260 auto &Msf
= *ExpectedMsf
;
262 // one for the super block, one for the directory block map
263 uint32_t NumUsedBlocks
= Msf
.getNumUsedBlocks();
264 EXPECT_EQ(msf::getMinimumBlockCount(), NumUsedBlocks
);
265 EXPECT_EQ(0U, Msf
.getNumFreeBlocks());
267 const uint32_t StreamSizes
[] = {4000, 6193, 189723};
268 for (int I
= 0; I
< 3; ++I
) {
269 EXPECT_THAT_EXPECTED(Msf
.addStream(StreamSizes
[I
]), Succeeded());
270 NumUsedBlocks
+= bytesToBlocks(StreamSizes
[I
], 4096);
271 EXPECT_EQ(NumUsedBlocks
, Msf
.getNumUsedBlocks());
272 EXPECT_EQ(0U, Msf
.getNumFreeBlocks());
276 TEST_F(MSFBuilderTest
, BuildMsfLayout
) {
277 // Test that we can generate an MSFLayout structure from a valid layout
279 auto ExpectedMsf
= MSFBuilder::create(Allocator
, 4096);
280 EXPECT_THAT_EXPECTED(ExpectedMsf
, Succeeded());
281 auto &Msf
= *ExpectedMsf
;
283 const uint32_t StreamSizes
[] = {4000, 6193, 189723};
284 uint32_t ExpectedNumBlocks
= msf::getMinimumBlockCount();
285 for (int I
= 0; I
< 3; ++I
) {
286 EXPECT_THAT_EXPECTED(Msf
.addStream(StreamSizes
[I
]), Succeeded());
287 ExpectedNumBlocks
+= bytesToBlocks(StreamSizes
[I
], 4096);
289 ++ExpectedNumBlocks
; // The directory itself should use 1 block
291 auto ExpectedLayout
= Msf
.generateLayout();
292 EXPECT_THAT_EXPECTED(ExpectedLayout
, Succeeded());
293 MSFLayout
&L
= *ExpectedLayout
;
294 EXPECT_EQ(4096U, L
.SB
->BlockSize
);
295 EXPECT_EQ(ExpectedNumBlocks
, L
.SB
->NumBlocks
);
297 EXPECT_EQ(1U, L
.DirectoryBlocks
.size());
299 EXPECT_EQ(3U, L
.StreamMap
.size());
300 EXPECT_EQ(3U, L
.StreamSizes
.size());
301 for (int I
= 0; I
< 3; ++I
) {
302 EXPECT_EQ(StreamSizes
[I
], L
.StreamSizes
[I
]);
303 uint32_t ExpectedNumBlocks
= bytesToBlocks(StreamSizes
[I
], 4096);
304 EXPECT_EQ(ExpectedNumBlocks
, L
.StreamMap
[I
].size());
308 TEST_F(MSFBuilderTest
, UseDirectoryBlockHint
) {
309 Expected
<MSFBuilder
> ExpectedMsf
= MSFBuilder::create(
310 Allocator
, 4096, msf::getMinimumBlockCount() + 1, false);
311 EXPECT_THAT_EXPECTED(ExpectedMsf
, Succeeded());
312 auto &Msf
= *ExpectedMsf
;
314 uint32_t B
= msf::getFirstUnreservedBlock();
315 EXPECT_THAT_ERROR(Msf
.setDirectoryBlocksHint({B
+ 1}), Succeeded());
316 EXPECT_THAT_EXPECTED(Msf
.addStream(2048, {B
+ 2}), Succeeded());
318 auto ExpectedLayout
= Msf
.generateLayout();
319 EXPECT_THAT_EXPECTED(ExpectedLayout
, Succeeded());
320 MSFLayout
&L
= *ExpectedLayout
;
321 EXPECT_EQ(msf::getMinimumBlockCount() + 2, L
.SB
->NumBlocks
);
322 EXPECT_EQ(1U, L
.DirectoryBlocks
.size());
323 EXPECT_EQ(1U, L
.StreamMap
[0].size());
325 EXPECT_EQ(B
+ 1, L
.DirectoryBlocks
[0]);
326 EXPECT_EQ(B
+ 2, L
.StreamMap
[0].front());
329 TEST_F(MSFBuilderTest
, DirectoryBlockHintInsufficient
) {
330 Expected
<MSFBuilder
> ExpectedMsf
=
331 MSFBuilder::create(Allocator
, 4096, msf::getMinimumBlockCount() + 2);
332 EXPECT_THAT_EXPECTED(ExpectedMsf
, Succeeded());
333 auto &Msf
= *ExpectedMsf
;
334 uint32_t B
= msf::getFirstUnreservedBlock();
335 EXPECT_THAT_ERROR(Msf
.setDirectoryBlocksHint({B
+ 1}), Succeeded());
337 uint32_t Size
= 4096 * 4096 / 4;
338 EXPECT_THAT_EXPECTED(Msf
.addStream(Size
), Succeeded());
340 auto ExpectedLayout
= Msf
.generateLayout();
341 EXPECT_THAT_EXPECTED(ExpectedLayout
, Succeeded());
342 MSFLayout
&L
= *ExpectedLayout
;
343 EXPECT_EQ(2U, L
.DirectoryBlocks
.size());
344 EXPECT_EQ(B
+ 1, L
.DirectoryBlocks
[0]);
347 TEST_F(MSFBuilderTest
, DirectoryBlockHintOverestimated
) {
348 Expected
<MSFBuilder
> ExpectedMsf
=
349 MSFBuilder::create(Allocator
, 4096, msf::getMinimumBlockCount() + 2);
350 EXPECT_THAT_EXPECTED(ExpectedMsf
, Succeeded());
351 auto &Msf
= *ExpectedMsf
;
353 uint32_t B
= msf::getFirstUnreservedBlock();
354 EXPECT_THAT_ERROR(Msf
.setDirectoryBlocksHint({B
+ 1, B
+ 2}), Succeeded());
356 ASSERT_THAT_EXPECTED(Msf
.addStream(2048), Succeeded());
358 auto ExpectedLayout
= Msf
.generateLayout();
359 ASSERT_THAT_EXPECTED(ExpectedLayout
, Succeeded());
360 MSFLayout
&L
= *ExpectedLayout
;
361 EXPECT_EQ(1U, L
.DirectoryBlocks
.size());
362 EXPECT_EQ(B
+ 1, L
.DirectoryBlocks
[0]);
365 TEST_F(MSFBuilderTest
, StreamDoesntUseFpmBlocks
) {
366 Expected
<MSFBuilder
> ExpectedMsf
= MSFBuilder::create(Allocator
, 4096);
367 ASSERT_THAT_EXPECTED(ExpectedMsf
, Succeeded());
368 auto &Msf
= *ExpectedMsf
;
370 // A block is 4096 bytes, and every 4096 blocks we have 2 reserved FPM blocks.
371 // By creating add a stream that spans 4096*4096*3 bytes, we ensure that we
372 // cross over a couple of reserved FPM blocks, and that none of them are
373 // allocated to the stream.
374 constexpr uint32_t StreamSize
= 4096 * 4096 * 3;
375 Expected
<uint32_t> SN
= Msf
.addStream(StreamSize
);
376 ASSERT_THAT_EXPECTED(SN
, Succeeded());
378 auto ExpectedLayout
= Msf
.generateLayout();
379 ASSERT_THAT_EXPECTED(ExpectedLayout
, Succeeded());
380 MSFLayout
&L
= *ExpectedLayout
;
381 auto BlocksRef
= L
.StreamMap
[*SN
];
382 std::vector
<uint32_t> Blocks(BlocksRef
.begin(), BlocksRef
.end());
383 EXPECT_EQ(StreamSize
, L
.StreamSizes
[*SN
]);
385 for (uint32_t I
= 0; I
<= 3; ++I
) {
386 // Pages from both FPMs are always allocated.
387 EXPECT_FALSE(L
.FreePageMap
.test(2 + I
* 4096));
388 EXPECT_FALSE(L
.FreePageMap
.test(1 + I
* 4096));
391 for (uint32_t I
= 1; I
<= 3; ++I
) {
392 EXPECT_THAT(Blocks
, Not(Contains(1 + I
* 4096)));
393 EXPECT_THAT(Blocks
, Not(Contains(2 + I
* 4096)));