llvm.org GIT mirror llvm / 32cf292
[pdb] Introduce MsfBuilder for laying out PDB files. Reviewed by: ruiu Differential Revision: https://reviews.llvm.org/D22308 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@275611 91177308-0d34-0410-b5e6-96231b3b80d8 Zachary Turner 4 years ago
9 changed file(s) with 854 addition(s) and 17 deletion(s). Raw diff Collapse all Expand all
0 //===- MSFBuilder.h - MSF Directory & Metadata Builder ----------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef LLVM_DEBUGINFO_PDB_RAW_MSFBUILDER_H
10 #define LLVM_DEBUGINFO_PDB_RAW_MSFBUILDER_H
11
12 #include "llvm/ADT/ArrayRef.h"
13 #include "llvm/ADT/BitVector.h"
14
15 #include "llvm/DebugInfo/PDB/Raw/MsfCommon.h"
16 #include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
17
18 #include "llvm/Support/Allocator.h"
19 #include "llvm/Support/Endian.h"
20 #include "llvm/Support/Error.h"
21
22 #include
23 #include
24
25 namespace llvm {
26 namespace pdb {
27 class MsfBuilder {
28 public:
29 /// \brief Create a new `MsfBuilder`.
30 ///
31 /// \param BlockSize The internal block size used by the PDB file. See
32 /// isValidBlockSize() for a list of valid block sizes.
33 ///
34 /// \param MinBlockCount Causes the builder to reserve up front space for
35 /// at least `MinBlockCount` blocks. This is useful when using `MsfBuilder`
36 /// to read an existing PDB that you want to write back out later. The
37 /// original PDB file's SuperBlock contains the exact number of blocks used
38 /// by the file, so is a good hint as to how many blocks the new PDB file
39 /// will contain. Furthermore, it is actually necessary in this case. To
40 /// preserve stability of the file's layout, it is helpful to try to keep
41 /// all streams mapped to their original block numbers. To ensure that this
42 /// is possible, space for all blocks must be allocated beforehand so that
43 /// streams can be assigned to them.
44 ///
45 /// \param CanGrow If true, any operation which results in an attempt to
46 /// locate a free block when all available blocks have been exhausted will
47 /// allocate a new block, thereby growing the size of the final PDB file.
48 /// When false, any such attempt will result in an error. This is especially
49 /// useful in testing scenarios when you know your test isn't going to do
50 /// anything to increase the size of the file, so having an Error returned if
51 /// it were to happen would catch a programming error
52 ///
53 /// \returns an llvm::Error representing whether the operation succeeded or
54 /// failed. Currently the only way this can fail is if an invalid block size
55 /// is specified, or `MinBlockCount` does not leave enough room for the
56 /// mandatory reserved blocks required by an MSF file.
57 static Expected create(BumpPtrAllocator &Allocator,
58 uint32_t BlockSize,
59 uint32_t MinBlockCount = 0,
60 bool CanGrow = true);
61
62 /// Request the block map to be at a specific block address. This is useful
63 /// when editing a PDB and you want the layout to be as stable as possible.
64 Error setBlockMapAddr(uint32_t Addr);
65
66 /// Add a stream to the MSF file with the given size, occupying the given
67 /// list of blocks. This is useful when reading a PDB file and you want a
68 /// particular stream to occupy the original set of blocks. If the given
69 /// blocks are already allocated, or if the number of blocks specified is
70 /// incorrect for the given stream size, this function will return an Error.
71 Error addStream(uint32_t Size, ArrayRef Blocks);
72
73 /// Add a stream to the MSF file with the given size, occupying any available
74 /// blocks that the builder decides to use. This is useful when building a
75 /// new PDB file from scratch and you don't care what blocks a stream occupies
76 /// but you just want it to work.
77 Error addStream(uint32_t Size);
78
79 /// Update the size of an existing stream. This will allocate or deallocate
80 /// blocks as needed to match the requested size. This can fail if `CanGrow`
81 /// was set to false when initializing the `MsfBuilder`.
82 Error setStreamSize(uint32_t Idx, uint32_t Size);
83
84 /// Get the total number of streams in the MSF layout. This should return 1
85 /// for every call to `addStream`.
86 uint32_t getNumStreams() const;
87
88 /// Get the size of a stream by index.
89 uint32_t getStreamSize(uint32_t StreamIdx) const;
90
91 /// Get the list of blocks allocated to a particular stream.
92 ArrayRef getStreamBlocks(uint32_t StreamIdx) const;
93
94 /// Get the total number of blocks that will be allocated to actual data in
95 /// this MSF file.
96 uint32_t getNumUsedBlocks() const;
97
98 /// Get the total number of blocks that exist in the MSF file but are not
99 /// allocated to any valid data.
100 uint32_t getNumFreeBlocks() const;
101
102 /// Get the total number of blocks in the MSF file. In practice this is equal
103 /// to `getNumUsedBlocks() + getNumFreeBlocks()`.
104 uint32_t getTotalBlockCount() const;
105
106 /// Check whether a particular block is allocated or free.
107 bool isBlockFree(uint32_t Idx) const;
108
109 /// Finalize the layout and build the headers and structures that describe the
110 /// MSF layout and can be written directly to the MSF file.
111 Expected build();
112
113 private:
114 MsfBuilder(uint32_t BlockSize, uint32_t MinBlockCount, bool CanGrow,
115 BumpPtrAllocator &Allocator);
116
117 Error allocateBlocks(uint32_t NumBlocks, MutableArrayRef Blocks);
118 uint32_t computeDirectoryByteSize() const;
119
120 typedef std::vector BlockList;
121
122 BumpPtrAllocator &Allocator;
123
124 bool IsGrowable;
125 uint32_t BlockSize;
126 uint32_t MininumBlocks;
127 uint32_t BlockMapAddr;
128 BitVector FreeBlocks;
129 std::vector DirectoryBlocks;
130 std::vector> StreamData;
131 };
132 }
133 }
134
135 #endif
0 //===- MsfCommon.h - Common types and functions for MSF files ---*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef LLVM_DEBUGINFO_PDB_RAW_MSFCOMMON_H
10 #define LLVM_DEBUGINFO_PDB_RAW_MSFCOMMON_H
11
12 #include "llvm/ADT/ArrayRef.h"
13
14 #include "llvm/Support/Endian.h"
15 #include "llvm/Support/Error.h"
16 #include "llvm/Support/MathExtras.h"
17
18 #include
19
20 namespace llvm {
21 namespace pdb {
22 namespace msf {
23 static const char Magic[] = {'M', 'i', 'c', 'r', 'o', 's', 'o', 'f',
24 't', ' ', 'C', '/', 'C', '+', '+', ' ',
25 'M', 'S', 'F', ' ', '7', '.', '0', '0',
26 '\r', '\n', '\x1a', 'D', 'S', '\0', '\0', '\0'};
27
28 // The superblock is overlaid at the beginning of the file (offset 0).
29 // It starts with a magic header and is followed by information which
30 // describes the layout of the file system.
31 struct SuperBlock {
32 char MagicBytes[sizeof(Magic)];
33 // The file system is split into a variable number of fixed size elements.
34 // These elements are referred to as blocks. The size of a block may vary
35 // from system to system.
36 support::ulittle32_t BlockSize;
37 // This field's purpose is not yet known.
38 support::ulittle32_t Unknown0;
39 // This contains the number of blocks resident in the file system. In
40 // practice, NumBlocks * BlockSize is equivalent to the size of the PDB
41 // file.
42 support::ulittle32_t NumBlocks;
43 // This contains the number of bytes which make up the directory.
44 support::ulittle32_t NumDirectoryBytes;
45 // This field's purpose is not yet known.
46 support::ulittle32_t Unknown1;
47 // This contains the block # of the block map.
48 support::ulittle32_t BlockMapAddr;
49 };
50
51 struct Layout {
52 SuperBlock *SB;
53 ArrayRef DirectoryBlocks;
54 ArrayRef StreamSizes;
55 std::vector> StreamMap;
56 };
57
58 inline bool isValidBlockSize(uint32_t Size) {
59 switch (Size) {
60 case 512:
61 case 1024:
62 case 2048:
63 case 4096:
64 return true;
65 }
66 return false;
67 }
68
69 inline uint64_t bytesToBlocks(uint64_t NumBytes, uint64_t BlockSize) {
70 return alignTo(NumBytes, BlockSize) / BlockSize;
71 }
72
73 inline uint64_t blockToOffset(uint64_t BlockNumber, uint64_t BlockSize) {
74 return BlockNumber * BlockSize;
75 }
76
77 Error validateSuperBlock(const SuperBlock &SB);
78 }
79 }
80 }
81
82 #endif
3737 Raw/MappedBlockStream.cpp
3838 Raw/ModInfo.cpp
3939 Raw/ModStream.cpp
40 Raw/MsfBuilder.cpp
41 Raw/MsfCommon.cpp
4042 Raw/NameHashTable.cpp
4143 Raw/NameMap.cpp
4244 Raw/PDBFile.cpp
0 //===- MSFBuilder.cpp - MSF Directory & Metadata Builder --------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "llvm/DebugInfo/PDB/Raw/MsfBuilder.h"
10 #include "llvm/DebugInfo/PDB/Raw/RawError.h"
11
12 using namespace llvm;
13 using namespace llvm::pdb;
14 using namespace llvm::pdb::msf;
15 using namespace llvm::support;
16
17 namespace {
18 const uint32_t kSuperBlockBlock = 0;
19 const uint32_t kDefaultBlockMapAddr = 1;
20 }
21
22 MsfBuilder::MsfBuilder(uint32_t BlockSize, uint32_t MinBlockCount, bool CanGrow,
23 BumpPtrAllocator &Allocator)
24 : Allocator(Allocator), BlockSize(BlockSize), MininumBlocks(MinBlockCount),
25 IsGrowable(CanGrow), BlockMapAddr(kDefaultBlockMapAddr),
26 FreeBlocks(MinBlockCount + 2U, true) {
27 FreeBlocks[kSuperBlockBlock] = false;
28 FreeBlocks[BlockMapAddr] = false;
29 }
30
31 Expected MsfBuilder::create(BumpPtrAllocator &Allocator,
32 uint32_t BlockSize,
33 uint32_t MinBlockCount, bool CanGrow) {
34 if (!msf::isValidBlockSize(BlockSize))
35 return make_error(raw_error_code::unspecified,
36 "The requested block size is unsupported");
37
38 return MsfBuilder(BlockSize, MinBlockCount, CanGrow, Allocator);
39 }
40
41 Error MsfBuilder::setBlockMapAddr(uint32_t Addr) {
42 if (Addr == BlockMapAddr)
43 return Error::success();
44
45 if (Addr >= FreeBlocks.size()) {
46 if (!IsGrowable)
47 return make_error(raw_error_code::unspecified,
48 "Cannot grow the number of blocks");
49 FreeBlocks.resize(Addr + 1);
50 }
51
52 if (!isBlockFree(Addr))
53 return make_error(raw_error_code::unspecified,
54 "Attempt to reuse an allocated block");
55 FreeBlocks[BlockMapAddr] = true;
56 FreeBlocks[Addr] = false;
57 BlockMapAddr = Addr;
58 return Error::success();
59 }
60
61 Error MsfBuilder::allocateBlocks(uint32_t NumBlocks,
62 MutableArrayRef Blocks) {
63 if (NumBlocks == 0)
64 return Error::success();
65
66 uint32_t NumFreeBlocks = FreeBlocks.count();
67 if (NumFreeBlocks < NumBlocks) {
68 if (!IsGrowable)
69 return make_error(raw_error_code::unspecified,
70 "There are no free Blocks in the file");
71 uint32_t AllocBlocks = NumBlocks - NumFreeBlocks;
72 FreeBlocks.resize(AllocBlocks + FreeBlocks.size(), true);
73 }
74
75 int I = 0;
76 int Block = FreeBlocks.find_first();
77 do {
78 assert(Block != -1 && "We ran out of Blocks!");
79
80 uint32_t NextBlock = static_cast(Block);
81 Blocks[I++] = NextBlock;
82 FreeBlocks.reset(NextBlock);
83 Block = FreeBlocks.find_next(Block);
84 } while (--NumBlocks > 0);
85 return Error::success();
86 }
87
88 uint32_t MsfBuilder::getNumUsedBlocks() const {
89 return getTotalBlockCount() - getNumFreeBlocks();
90 }
91
92 uint32_t MsfBuilder::getNumFreeBlocks() const { return FreeBlocks.count(); }
93
94 uint32_t MsfBuilder::getTotalBlockCount() const { return FreeBlocks.size(); }
95
96 bool MsfBuilder::isBlockFree(uint32_t Idx) const { return FreeBlocks[Idx]; }
97
98 Error MsfBuilder::addStream(uint32_t Size, ArrayRef Blocks) {
99 // Add a new stream mapped to the specified blocks. Verify that the specified
100 // blocks are both necessary and sufficient for holding the requested number
101 // of bytes, and verify that all requested blocks are free.
102 uint32_t ReqBlocks = bytesToBlocks(Size, BlockSize);
103 if (ReqBlocks != Blocks.size())
104 return make_error(
105 raw_error_code::unspecified,
106 "Incorrect number of blocks for requested stream size");
107 for (auto Block : Blocks) {
108 if (Block >= FreeBlocks.size())
109 FreeBlocks.resize(Block + 1, true);
110
111 if (!FreeBlocks.test(Block))
112 return make_error(
113 raw_error_code::unspecified,
114 "Attempt to re-use an already allocated block");
115 }
116 // Mark all the blocks occupied by the new stream as not free.
117 for (auto Block : Blocks) {
118 FreeBlocks.reset(Block);
119 }
120 StreamData.push_back(std::make_pair(Size, Blocks));
121 return Error::success();
122 }
123
124 Error MsfBuilder::addStream(uint32_t Size) {
125 uint32_t ReqBlocks = bytesToBlocks(Size, BlockSize);
126 std::vector NewBlocks;
127 NewBlocks.resize(ReqBlocks);
128 if (auto EC = allocateBlocks(ReqBlocks, NewBlocks))
129 return EC;
130 StreamData.push_back(std::make_pair(Size, NewBlocks));
131 return Error::success();
132 }
133
134 Error MsfBuilder::setStreamSize(uint32_t Idx, uint32_t Size) {
135 uint32_t OldSize = getStreamSize(Idx);
136 if (OldSize == Size)
137 return Error::success();
138
139 uint32_t NewBlocks = bytesToBlocks(Size, BlockSize);
140 uint32_t OldBlocks = bytesToBlocks(OldSize, BlockSize);
141
142 if (NewBlocks > OldBlocks) {
143 uint32_t AddedBlocks = NewBlocks - OldBlocks;
144 // If we're growing, we have to allocate new Blocks.
145 std::vector AddedBlockList;
146 AddedBlockList.resize(AddedBlocks);
147 if (auto EC = allocateBlocks(AddedBlocks, AddedBlockList))
148 return EC;
149 auto &CurrentBlocks = StreamData[Idx].second;
150 CurrentBlocks.insert(CurrentBlocks.end(), AddedBlockList.begin(),
151 AddedBlockList.end());
152 } else if (OldBlocks > NewBlocks) {
153 // For shrinking, free all the Blocks in the Block map, update the stream
154 // data, then shrink the directory.
155 uint32_t RemovedBlocks = OldBlocks - NewBlocks;
156 auto CurrentBlocks = ArrayRef(StreamData[Idx].second);
157 auto RemovedBlockList = CurrentBlocks.drop_front(NewBlocks);
158 for (auto P : RemovedBlockList)
159 FreeBlocks[P] = true;
160 StreamData[Idx].second = CurrentBlocks.drop_back(RemovedBlocks);
161 }
162
163 StreamData[Idx].first = Size;
164 return Error::success();
165 }
166
167 uint32_t MsfBuilder::getNumStreams() const { return StreamData.size(); }
168
169 uint32_t MsfBuilder::getStreamSize(uint32_t StreamIdx) const {
170 return StreamData[StreamIdx].first;
171 }
172
173 ArrayRef MsfBuilder::getStreamBlocks(uint32_t StreamIdx) const {
174 return StreamData[StreamIdx].second;
175 }
176
177 uint32_t MsfBuilder::computeDirectoryByteSize() const {
178 // The directory has the following layout, where each item is a ulittle32_t:
179 // NumStreams
180 // StreamSizes[NumStreams]
181 // StreamBlocks[NumStreams][]
182 uint32_t Size = sizeof(ulittle32_t); // NumStreams
183 Size += StreamData.size() * sizeof(ulittle32_t); // StreamSizes
184 for (const auto &D : StreamData) {
185 uint32_t ExpectedNumBlocks = bytesToBlocks(D.first, BlockSize);
186 assert(ExpectedNumBlocks == D.second.size() &&
187 "Unexpected number of blocks");
188 Size += ExpectedNumBlocks * sizeof(ulittle32_t);
189 }
190 return Size;
191 }
192
193 Expected MsfBuilder::build() {
194 Layout L;
195 L.SB = Allocator.Allocate();
196 std::memcpy(L.SB->MagicBytes, Magic, sizeof(Magic));
197 L.SB->BlockMapAddr = BlockMapAddr;
198 L.SB->BlockSize = BlockSize;
199 L.SB->NumDirectoryBytes = computeDirectoryByteSize();
200 L.SB->Unknown0 = 0;
201 L.SB->Unknown1 = 0;
202
203 uint32_t NumDirectoryBlocks =
204 bytesToBlocks(L.SB->NumDirectoryBytes, BlockSize);
205 // The directory blocks should be re-allocated as a stable pointer.
206 std::vector DirectoryBlocks;
207 DirectoryBlocks.resize(NumDirectoryBlocks);
208 if (auto EC = allocateBlocks(NumDirectoryBlocks, DirectoryBlocks))
209 return std::move(EC);
210
211 // Don't set the number of blocks in the file until after allocating Blocks
212 // for
213 // the directory, since the allocation might cause the file to need to grow.
214 L.SB->NumBlocks = FreeBlocks.size();
215
216 ulittle32_t *DirBlocks = Allocator.Allocate(NumDirectoryBlocks);
217 std::uninitialized_copy_n(DirectoryBlocks.begin(), NumDirectoryBlocks,
218 DirBlocks);
219 L.DirectoryBlocks = ArrayRef(DirBlocks, NumDirectoryBlocks);
220
221 // The stream sizes should be re-allocated as a stable pointer and the stream
222 // map should have each of its entries allocated as a separate stable pointer.
223 if (StreamData.size() > 0) {
224 ulittle32_t *Sizes = Allocator.Allocate(StreamData.size());
225 L.StreamSizes = ArrayRef(Sizes, StreamData.size());
226 L.StreamMap.resize(StreamData.size());
227 for (uint32_t I = 0; I < StreamData.size(); ++I) {
228 Sizes[I] = StreamData[I].first;
229 ulittle32_t *BlockList =
230 Allocator.Allocate(StreamData[I].second.size());
231 std::uninitialized_copy_n(StreamData[I].second.begin(),
232 StreamData[I].second.size(), BlockList);
233 L.StreamMap[I] =
234 ArrayRef(BlockList, StreamData[I].second.size());
235 }
236 }
237
238 return L;
239 }
0 //===- MsfCommon.cpp - Common types and functions for MSF files -*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "llvm/DebugInfo/PDB/Raw/MsfCommon.h"
10 #include "llvm/DebugInfo/PDB/Raw/RawError.h"
11
12 using namespace llvm;
13 using namespace llvm::pdb::msf;
14
15 Error llvm::pdb::msf::validateSuperBlock(const SuperBlock &SB) {
16 // Check the magic bytes.
17 if (std::memcmp(SB.MagicBytes, Magic, sizeof(Magic)) != 0)
18 return make_error(raw_error_code::corrupt_file,
19 "MSF magic header doesn't match");
20
21 if (!isValidBlockSize(SB.BlockSize))
22 return make_error(raw_error_code::corrupt_file,
23 "Unsupported block size.");
24
25 // We don't support directories whose sizes aren't a multiple of four bytes.
26 if (SB.NumDirectoryBytes % sizeof(support::ulittle32_t) != 0)
27 return make_error(raw_error_code::corrupt_file,
28 "Directory size is not multiple of 4.");
29
30 // The number of blocks which comprise the directory is a simple function of
31 // the number of bytes it contains.
32 uint64_t NumDirectoryBlocks =
33 bytesToBlocks(SB.NumDirectoryBytes, SB.BlockSize);
34
35 // The directory, as we understand it, is a block which consists of a list of
36 // block numbers. It is unclear what would happen if the number of blocks
37 // couldn't fit on a single block.
38 if (NumDirectoryBlocks > SB.BlockSize / sizeof(support::ulittle32_t))
39 return make_error(raw_error_code::corrupt_file,
40 "Too many directory blocks.");
41
42 if (SB.BlockMapAddr == 0)
43 return make_error(raw_error_code::corrupt_file,
44 "Block 0 is reserved");
45
46 return Error::success();
47 }
44
55 set(DebugInfoPDBSources
66 MappedBlockStreamTest.cpp
7 MsfBuilderTest.cpp
78 PDBApiTest.cpp
89 )
910
0 //===- ErrorChecking.h - Helpers for verifying llvm::Errors -----*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef LLVM_UNITTESTS_DEBUGINFO_PDB_ERRORCHECKING_H
10 #define LLVM_UNITTESTS_DEBUGINFO_PDB_ERRORCHECKING_H
11
12 #define EXPECT_NO_ERROR(Err) \
13 { \
14 auto E = Err; \
15 EXPECT_FALSE(static_cast(E)); \
16 if (E) \
17 consumeError(std::move(E)); \
18 }
19
20 #define EXPECT_ERROR(Err) \
21 { \
22 auto E = Err; \
23 EXPECT_TRUE(static_cast(E)); \
24 if (E) \
25 consumeError(std::move(E)); \
26 }
27
28 #define EXPECT_EXPECTED(Exp) \
29 { \
30 auto E = Exp.takeError(); \
31 EXPECT_FALSE(static_cast(E)); \
32 if (E) { \
33 consumeError(std::move(E)); \
34 return; \
35 } \
36 }
37
38 #define EXPECT_UNEXPECTED(Exp) EXPECT_ERROR(Err)
39
40 #endif
66 //
77 //===----------------------------------------------------------------------===//
88
9 #include
9 #include "ErrorChecking.h"
1010
1111 #include "llvm/DebugInfo/CodeView/ByteStream.h"
1212 #include "llvm/DebugInfo/CodeView/StreamReader.h"
1818 #include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h"
1919 #include "gtest/gtest.h"
2020
21 #include
22
2123 using namespace llvm;
2224 using namespace llvm::codeview;
2325 using namespace llvm::pdb;
2426
2527 namespace {
26
27 #define EXPECT_NO_ERROR(Err) \
28 { \
29 auto E = Err; \
30 EXPECT_FALSE(static_cast(E)); \
31 if (E) \
32 consumeError(std::move(E)); \
33 }
34
35 #define EXPECT_ERROR(Err) \
36 { \
37 auto E = Err; \
38 EXPECT_TRUE(static_cast(E)); \
39 if (E) \
40 consumeError(std::move(E)); \
41 }
4228
4329 static const uint32_t BlocksAry[] = {0, 1, 2, 5, 4, 3, 6, 7, 8, 9};
4430 static uint8_t DataAry[] = {'A', 'B', 'C', 'F', 'E', 'D', 'G', 'H', 'I', 'J'};
0 //===- MsfBuilderTest.cpp Tests manipulation of MSF stream metadata ------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "ErrorChecking.h"
10
11 #include "llvm/DebugInfo/PDB/Raw/MsfBuilder.h"
12 #include "llvm/DebugInfo/PDB/Raw/MsfCommon.h"
13
14 #include "gtest/gtest.h"
15
16 using namespace llvm;
17 using namespace llvm::pdb;
18 using namespace llvm::pdb::msf;
19
20 namespace {
21 class MsfBuilderTest : public testing::Test {
22 protected:
23 void initializeSimpleSuperBlock(msf::SuperBlock &SB) {
24 initializeSuperBlock(SB);
25 SB.NumBlocks = 1000;
26 SB.NumDirectoryBytes = 8192;
27 }
28
29 void initializeSuperBlock(msf::SuperBlock &SB) {
30 ::memset(&SB, 0, sizeof(SB));
31
32 ::memcpy(SB.MagicBytes, msf::Magic, sizeof(msf::Magic));
33 SB.BlockMapAddr = 1;
34 SB.BlockSize = 4096;
35 SB.NumDirectoryBytes = 0;
36 SB.NumBlocks = 2; // one for the Super Block, one for the directory
37 }
38
39 BumpPtrAllocator Allocator;
40 };
41 }
42
43 TEST_F(MsfBuilderTest, ValidateSuperBlockAccept) {
44 // Test that a known good super block passes validation.
45 SuperBlock SB;
46 initializeSuperBlock(SB);
47
48 EXPECT_NO_ERROR(msf::validateSuperBlock(SB));
49 }
50
51 TEST_F(MsfBuilderTest, ValidateSuperBlockReject) {
52 // Test that various known problems cause a super block to be rejected.
53 SuperBlock SB;
54 initializeSimpleSuperBlock(SB);
55
56 // Mismatched magic
57 SB.MagicBytes[0] = 8;
58 EXPECT_ERROR(msf::validateSuperBlock(SB));
59 initializeSimpleSuperBlock(SB);
60
61 // Block 0 is reserved for super block, can't be occupied by the block map
62 SB.BlockMapAddr = 0;
63 EXPECT_ERROR(msf::validateSuperBlock(SB));
64 initializeSimpleSuperBlock(SB);
65
66 // Block sizes have to be powers of 2.
67 SB.BlockSize = 3120;
68 EXPECT_ERROR(msf::validateSuperBlock(SB));
69 initializeSimpleSuperBlock(SB);
70
71 // The directory itself has a maximum size.
72 SB.NumDirectoryBytes = SB.BlockSize * SB.BlockSize / 4;
73 EXPECT_NO_ERROR(msf::validateSuperBlock(SB));
74 SB.NumDirectoryBytes = SB.NumDirectoryBytes + 4;
75 EXPECT_ERROR(msf::validateSuperBlock(SB));
76 }
77
78 TEST_F(MsfBuilderTest, TestUsedBlocksMarkedAsUsed) {
79 // Test that when assigning a stream to a known list of blocks, the blocks
80 // are correctly marked as used after adding, but no other incorrect blocks
81 // are accidentally marked as used.
82
83 // Allocate some extra blocks at the end so we can verify that they're free
84 // after the initialization.
85 std::vector Blocks = {2, 3, 4, 5, 6, 7, 8, 9, 10};
86 auto ExpectedMsf = MsfBuilder::create(Allocator, 4096, Blocks.size() + 10);
87 EXPECT_EXPECTED(ExpectedMsf);
88 auto &Msf = *ExpectedMsf;
89
90 EXPECT_NO_ERROR(Msf.addStream(Blocks.size() * 4096, Blocks));
91
92 for (auto B : Blocks) {
93 EXPECT_FALSE(Msf.isBlockFree(B));
94 }
95 for (int I = 11; I < 21; ++I) {
96 EXPECT_TRUE(Msf.isBlockFree(I));
97 }
98 }
99
100 TEST_F(MsfBuilderTest, TestAddStreamNoDirectoryBlockIncrease) {
101 // Test that adding a new stream correctly updates the directory. This only
102 // tests the case where the directory *DOES NOT* grow large enough that it
103 // crosses a Block boundary.
104 auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
105 EXPECT_EXPECTED(ExpectedMsf);
106 auto &Msf = *ExpectedMsf;
107
108 auto ExpectedL1 = Msf.build();
109 EXPECT_EXPECTED(ExpectedL1);
110 Layout &L1 = *ExpectedL1;
111
112 auto OldDirBlocks = L1.DirectoryBlocks;
113 EXPECT_EQ(1U, OldDirBlocks.size());
114
115 auto ExpectedMsf2 = MsfBuilder::create(Allocator, 4096);
116 EXPECT_EXPECTED(ExpectedMsf2);
117 auto &Msf2 = *ExpectedMsf2;
118
119 EXPECT_NO_ERROR(Msf2.addStream(4000));
120 EXPECT_EQ(1U, Msf2.getNumStreams());
121 EXPECT_EQ(4000U, Msf2.getStreamSize(0));
122 auto Blocks = Msf2.getStreamBlocks(0);
123 EXPECT_EQ(1U, Blocks.size());
124
125 auto ExpectedL2 = Msf2.build();
126 EXPECT_EXPECTED(ExpectedL2);
127 Layout &L2 = *ExpectedL2;
128 auto NewDirBlocks = L2.DirectoryBlocks;
129 EXPECT_EQ(1U, NewDirBlocks.size());
130 }
131
132 TEST_F(MsfBuilderTest, TestAddStreamWithDirectoryBlockIncrease) {
133 // Test that adding a new stream correctly updates the directory. This only
134 // tests the case where the directory *DOES* grow large enough that it
135 // crosses a Block boundary. This is because the newly added stream occupies
136 // so many Blocks that need to be indexed in the directory that the directory
137 // crosses a Block boundary.
138 auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
139 EXPECT_EXPECTED(ExpectedMsf);
140 auto &Msf = *ExpectedMsf;
141
142 EXPECT_NO_ERROR(Msf.addStream(4096 * 4096 / sizeof(uint32_t)));
143
144 auto ExpectedL1 = Msf.build();
145 EXPECT_EXPECTED(ExpectedL1);
146 Layout &L1 = *ExpectedL1;
147 auto DirBlocks = L1.DirectoryBlocks;
148 EXPECT_EQ(2U, DirBlocks.size());
149 }
150
151 TEST_F(MsfBuilderTest, TestGrowStreamNoBlockIncrease) {
152 // Test growing an existing stream by a value that does not affect the number
153 // of blocks it occupies.
154 auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
155 EXPECT_EXPECTED(ExpectedMsf);
156 auto &Msf = *ExpectedMsf;
157
158 EXPECT_NO_ERROR(Msf.addStream(1024));
159 EXPECT_EQ(1024U, Msf.getStreamSize(0));
160 auto OldStreamBlocks = Msf.getStreamBlocks(0);
161 EXPECT_EQ(1U, OldStreamBlocks.size());
162
163 EXPECT_NO_ERROR(Msf.setStreamSize(0, 2048));
164 EXPECT_EQ(2048U, Msf.getStreamSize(0));
165 auto NewStreamBlocks = Msf.getStreamBlocks(0);
166 EXPECT_EQ(1U, NewStreamBlocks.size());
167
168 EXPECT_EQ(OldStreamBlocks, NewStreamBlocks);
169 }
170
171 TEST_F(MsfBuilderTest, TestGrowStreamWithBlockIncrease) {
172 // Test that growing an existing stream to a value large enough that it causes
173 // the need to allocate new Blocks to the stream correctly updates the
174 // stream's
175 // block list.
176 auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
177 EXPECT_EXPECTED(ExpectedMsf);
178 auto &Msf = *ExpectedMsf;
179
180 EXPECT_NO_ERROR(Msf.addStream(2048));
181 EXPECT_EQ(2048U, Msf.getStreamSize(0));
182 std::vector OldStreamBlocks = Msf.getStreamBlocks(0);
183 EXPECT_EQ(1U, OldStreamBlocks.size());
184
185 EXPECT_NO_ERROR(Msf.setStreamSize(0, 6144));
186 EXPECT_EQ(6144U, Msf.getStreamSize(0));
187 std::vector NewStreamBlocks = Msf.getStreamBlocks(0);
188 EXPECT_EQ(2U, NewStreamBlocks.size());
189
190 EXPECT_EQ(OldStreamBlocks[0], NewStreamBlocks[0]);
191 EXPECT_NE(NewStreamBlocks[0], NewStreamBlocks[1]);
192 }
193
194 TEST_F(MsfBuilderTest, TestShrinkStreamNoBlockDecrease) {
195 // Test that shrinking an existing stream by a value that does not affect the
196 // number of Blocks it occupies makes no changes to stream's block list.
197 auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
198 EXPECT_EXPECTED(ExpectedMsf);
199 auto &Msf = *ExpectedMsf;
200
201 EXPECT_NO_ERROR(Msf.addStream(2048));
202 EXPECT_EQ(2048U, Msf.getStreamSize(0));
203 std::vector OldStreamBlocks = Msf.getStreamBlocks(0);
204 EXPECT_EQ(1U, OldStreamBlocks.size());
205
206 EXPECT_NO_ERROR(Msf.setStreamSize(0, 1024));
207 EXPECT_EQ(1024U, Msf.getStreamSize(0));
208 std::vector NewStreamBlocks = Msf.getStreamBlocks(0);
209 EXPECT_EQ(1U, NewStreamBlocks.size());
210
211 EXPECT_EQ(OldStreamBlocks, NewStreamBlocks);
212 }
213
214 TEST_F(MsfBuilderTest, TestShrinkStreamWithBlockDecrease) {
215 // Test that shrinking an existing stream to a value large enough that it
216 // causes the need to deallocate new Blocks to the stream correctly updates
217 // the stream's block list.
218 auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
219 EXPECT_EXPECTED(ExpectedMsf);
220 auto &Msf = *ExpectedMsf;
221
222 EXPECT_NO_ERROR(Msf.addStream(6144));
223 EXPECT_EQ(6144U, Msf.getStreamSize(0));
224 std::vector OldStreamBlocks = Msf.getStreamBlocks(0);
225 EXPECT_EQ(2U, OldStreamBlocks.size());
226
227 EXPECT_NO_ERROR(Msf.setStreamSize(0, 2048));
228 EXPECT_EQ(2048U, Msf.getStreamSize(0));
229 std::vector NewStreamBlocks = Msf.getStreamBlocks(0);
230 EXPECT_EQ(1U, NewStreamBlocks.size());
231
232 EXPECT_EQ(OldStreamBlocks[0], NewStreamBlocks[0]);
233 }
234
235 TEST_F(MsfBuilderTest, TestRejectReusedStreamBlock) {
236 // Test that attempting to add a stream and assigning a block that is already
237 // in use by another stream fails.
238 auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
239 EXPECT_EXPECTED(ExpectedMsf);
240 auto &Msf = *ExpectedMsf;
241
242 EXPECT_NO_ERROR(Msf.addStream(6144));
243
244 std::vector Blocks = {2, 3};
245 EXPECT_ERROR(Msf.addStream(6144, Blocks));
246 }
247
248 TEST_F(MsfBuilderTest, TestBlockCountsWhenAddingStreams) {
249 // Test that when adding multiple streams, the number of used and free Blocks
250 // allocated to the MSF file are as expected.
251 auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
252 EXPECT_EXPECTED(ExpectedMsf);
253 auto &Msf = *ExpectedMsf;
254
255 // one for the super block, one for the directory block map
256 uint32_t NumUsedBlocks = Msf.getNumUsedBlocks();
257 EXPECT_EQ(2U, NumUsedBlocks);
258 EXPECT_EQ(0U, Msf.getNumFreeBlocks());
259
260 const uint32_t StreamSizes[] = {4000, 6193, 189723};
261 for (int I = 0; I < 3; ++I) {
262 EXPECT_NO_ERROR(Msf.addStream(StreamSizes[I]));
263 NumUsedBlocks += bytesToBlocks(StreamSizes[I], 4096);
264 EXPECT_EQ(NumUsedBlocks, Msf.getNumUsedBlocks());
265 EXPECT_EQ(0U, Msf.getNumFreeBlocks());
266 }
267 }
268
269 TEST_F(MsfBuilderTest, TestBuildMsfLayout) {
270 // Test that we can generate an Msf Layout structure from a valid layout
271 // specification.
272 auto ExpectedMsf = MsfBuilder::create(Allocator, 4096);
273 EXPECT_EXPECTED(ExpectedMsf);
274 auto &Msf = *ExpectedMsf;
275
276 const uint32_t StreamSizes[] = {4000, 6193, 189723};
277 uint32_t ExpectedNumBlocks = 2;
278 for (int I = 0; I < 3; ++I) {
279 EXPECT_NO_ERROR(Msf.addStream(StreamSizes[I]));
280 ExpectedNumBlocks += bytesToBlocks(StreamSizes[I], 4096);
281 }
282 ++ExpectedNumBlocks; // The directory itself should use 1 block
283
284 auto ExpectedLayout = Msf.build();
285 EXPECT_EXPECTED(ExpectedLayout);
286 Layout &L = *ExpectedLayout;
287 EXPECT_EQ(4096U, L.SB->BlockSize);
288 EXPECT_EQ(ExpectedNumBlocks, L.SB->NumBlocks);
289
290 EXPECT_EQ(1U, L.DirectoryBlocks.size());
291
292 EXPECT_EQ(3U, L.StreamMap.size());
293 EXPECT_EQ(3U, L.StreamSizes.size());
294 for (int I = 0; I < 3; ++I) {
295 EXPECT_EQ(StreamSizes[I], L.StreamSizes[I]);
296 uint32_t ExpectedNumBlocks = bytesToBlocks(StreamSizes[I], 4096);
297 EXPECT_EQ(ExpectedNumBlocks, L.StreamMap[I].size());
298 }
299 }