[pdb/lld] Write a valid FPM.
The PDB reserves certain blocks for the FPM that describe which
blocks in the file are allocated and which are free. We weren't
filling that out at all, and in some cases we were even stomping
it with incorrect data. This patch writes a correct FPM.
Differential Revision: https://reviews.llvm.org/D36235
llvm-svn: 309896
diff --git a/llvm/unittests/DebugInfo/MSF/CMakeLists.txt b/llvm/unittests/DebugInfo/MSF/CMakeLists.txt
index 3f5acc2..25e01117 100644
--- a/llvm/unittests/DebugInfo/MSF/CMakeLists.txt
+++ b/llvm/unittests/DebugInfo/MSF/CMakeLists.txt
@@ -5,6 +5,7 @@
set(DebugInfoMSFSources
MappedBlockStreamTest.cpp
MSFBuilderTest.cpp
+ MSFCommonTest.cpp
)
add_llvm_unittest(DebugInfoMSFTests
diff --git a/llvm/unittests/DebugInfo/MSF/MSFBuilderTest.cpp b/llvm/unittests/DebugInfo/MSF/MSFBuilderTest.cpp
index 4791c98..a91ac8d4 100644
--- a/llvm/unittests/DebugInfo/MSF/MSFBuilderTest.cpp
+++ b/llvm/unittests/DebugInfo/MSF/MSFBuilderTest.cpp
@@ -11,10 +11,13 @@
#include "llvm/DebugInfo/MSF/MSFCommon.h"
#include "llvm/Testing/Support/Error.h"
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace llvm::msf;
+using namespace testing;
namespace {
class MSFBuilderTest : public testing::Test {
@@ -359,3 +362,36 @@
EXPECT_EQ(1U, L.DirectoryBlocks.size());
EXPECT_EQ(B + 1, L.DirectoryBlocks[0]);
}
+
+TEST_F(MSFBuilderTest, StreamDoesntUseFpmBlocks) {
+ Expected<MSFBuilder> ExpectedMsf = MSFBuilder::create(Allocator, 4096);
+ ASSERT_THAT_EXPECTED(ExpectedMsf, Succeeded());
+ auto &Msf = *ExpectedMsf;
+
+ // A block is 4096 bytes, and every 4096 blocks we have 2 reserved FPM blocks.
+ // By creating add a stream that spans 4096*4096*3 bytes, we ensure that we
+ // cross over a couple of reserved FPM blocks, and that none of them are
+ // allocated to the stream.
+ constexpr uint32_t StreamSize = 4096 * 4096 * 3;
+ Expected<uint32_t> SN = Msf.addStream(StreamSize);
+ ASSERT_THAT_EXPECTED(SN, Succeeded());
+
+ auto ExpectedLayout = Msf.build();
+ ASSERT_THAT_EXPECTED(ExpectedLayout, Succeeded());
+ MSFLayout &L = *ExpectedLayout;
+ auto BlocksRef = L.StreamMap[*SN];
+ std::vector<uint32_t> Blocks(BlocksRef.begin(), BlocksRef.end());
+ EXPECT_EQ(StreamSize, L.StreamSizes[*SN]);
+
+ for (uint32_t I = 0; I <= 3; ++I) {
+ // Pages from the regular FPM are allocated, while pages from the alt fpm
+ // are free.
+ EXPECT_FALSE(L.FreePageMap.test(1 + I * 4096));
+ EXPECT_TRUE(L.FreePageMap.test(2 + I * 4096));
+ }
+
+ for (uint32_t I = 1; I <= 3; ++I) {
+ EXPECT_THAT(Blocks, Not(Contains(1 + I * 4096)));
+ EXPECT_THAT(Blocks, Not(Contains(2 + I * 4096)));
+ }
+}
diff --git a/llvm/unittests/DebugInfo/MSF/MSFCommonTest.cpp b/llvm/unittests/DebugInfo/MSF/MSFCommonTest.cpp
new file mode 100644
index 0000000..144f5b1
--- /dev/null
+++ b/llvm/unittests/DebugInfo/MSF/MSFCommonTest.cpp
@@ -0,0 +1,104 @@
+//===- MSFBuilderTest.cpp Tests manipulation of MSF stream metadata ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/MSF/MSFCommon.h"
+#include "llvm/Testing/Support/Error.h"
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::msf;
+
+TEST(MSFCommonTest, BytesToBlocks) {
+ EXPECT_EQ(0ULL, bytesToBlocks(0, 4096));
+ EXPECT_EQ(1ULL, bytesToBlocks(12, 4096));
+ EXPECT_EQ(1ULL, bytesToBlocks(4096, 4096));
+ EXPECT_EQ(2ULL, bytesToBlocks(4097, 4096));
+ EXPECT_EQ(2ULL, bytesToBlocks(600, 512));
+}
+
+TEST(MSFCommonTest, FpmIntervals) {
+ SuperBlock SB;
+ SB.FreeBlockMapBlock = 1;
+ SB.BlockSize = 4096;
+
+ MSFLayout L;
+ L.SB = &SB;
+
+ SB.NumBlocks = 12;
+ EXPECT_EQ(1u, getNumFpmIntervals(L, false));
+ SB.NumBlocks = SB.BlockSize;
+ EXPECT_EQ(1u, getNumFpmIntervals(L, false));
+ SB.NumBlocks = SB.BlockSize + 1;
+ EXPECT_EQ(1u, getNumFpmIntervals(L, false));
+ SB.NumBlocks = SB.BlockSize * 8;
+ EXPECT_EQ(1u, getNumFpmIntervals(L, false));
+ SB.NumBlocks = SB.BlockSize * 8 + 1;
+ EXPECT_EQ(2u, getNumFpmIntervals(L, false));
+
+ SB.NumBlocks = 12;
+ EXPECT_EQ(1u, getNumFpmIntervals(L, true));
+ SB.NumBlocks = SB.BlockSize;
+ EXPECT_EQ(1u, getNumFpmIntervals(L, true));
+ SB.NumBlocks = SB.BlockSize + 1;
+ EXPECT_EQ(2u, getNumFpmIntervals(L, true));
+ SB.NumBlocks = SB.BlockSize * 8;
+ EXPECT_EQ(8u, getNumFpmIntervals(L, true));
+ SB.NumBlocks = SB.BlockSize * 8 + 1;
+ EXPECT_EQ(9u, getNumFpmIntervals(L, true));
+}
+
+TEST(MSFCommonTest, FpmStreamLayout) {
+ SuperBlock SB;
+ MSFLayout L;
+ L.SB = &SB;
+ SB.FreeBlockMapBlock = 1;
+
+ // Each FPM block has 4096 bytes for a maximum of 4096*8 allocation states.
+ SB.BlockSize = 4096;
+
+ // 1. When we're not including unused FPM data, the length of the FPM stream
+ // should be only long enough to contain 1 bit for each block.
+
+ // 1a. When the PDB has <= 4096*8 blocks, there should only be one FPM block.
+ SB.NumBlocks = 8000;
+ MSFStreamLayout SL = getFpmStreamLayout(L, false, false);
+ EXPECT_EQ(1000u, SL.Length);
+ EXPECT_EQ(1u, SL.Blocks.size());
+ EXPECT_EQ(SB.FreeBlockMapBlock, SL.Blocks.front());
+
+ SL = getFpmStreamLayout(L, false, true);
+ EXPECT_EQ(1000u, SL.Length);
+ EXPECT_EQ(1u, SL.Blocks.size());
+ EXPECT_EQ(3u - SB.FreeBlockMapBlock, SL.Blocks.front());
+
+ // 1b. When the PDB has > 4096*8 blocks, there should be multiple FPM blocks.
+ SB.NumBlocks = SB.BlockSize * 8 + 1;
+ SL = getFpmStreamLayout(L, false, false);
+ EXPECT_EQ(SB.BlockSize + 1, SL.Length);
+ EXPECT_EQ(2u, SL.Blocks.size());
+ EXPECT_EQ(SB.FreeBlockMapBlock, SL.Blocks[0]);
+ EXPECT_EQ(SB.FreeBlockMapBlock + SB.BlockSize, SL.Blocks[1]);
+
+ SL = getFpmStreamLayout(L, false, true);
+ EXPECT_EQ(SB.BlockSize + 1, SL.Length);
+ EXPECT_EQ(2u, SL.Blocks.size());
+ EXPECT_EQ(3u - SB.FreeBlockMapBlock, SL.Blocks[0]);
+ EXPECT_EQ(3u - SB.FreeBlockMapBlock + SB.BlockSize, SL.Blocks[1]);
+
+ // 2. When we are including unused FPM data, there should be one FPM block
+ // at every BlockSize interval in the file, even if entire FPM blocks are
+ // unused.
+ SB.NumBlocks = SB.BlockSize * 8 + 1;
+ SL = getFpmStreamLayout(L, true, false);
+ EXPECT_EQ(SB.BlockSize * 9, SL.Length);
+ EXPECT_EQ(9u, SL.Blocks.size());
+ for (int I = 0; I < 9; ++I)
+ EXPECT_EQ(I * SB.BlockSize + SB.FreeBlockMapBlock, SL.Blocks[I]);
+}
diff --git a/llvm/unittests/DebugInfo/MSF/MappedBlockStreamTest.cpp b/llvm/unittests/DebugInfo/MSF/MappedBlockStreamTest.cpp
index 94cd347..94c4898 100644
--- a/llvm/unittests/DebugInfo/MSF/MappedBlockStreamTest.cpp
+++ b/llvm/unittests/DebugInfo/MSF/MappedBlockStreamTest.cpp
@@ -16,6 +16,7 @@
#include "llvm/Support/BinaryStreamWriter.h"
#include "llvm/Testing/Support/Error.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <unordered_map>
@@ -494,5 +495,59 @@
EXPECT_EQ(Str[0], Str[1]);
}
+} // namespace
+
+MATCHER_P3(BlockIsFilledWith, Layout, BlockIndex, Byte, "succeeded") {
+ uint64_t Offset = msf::blockToOffset(BlockIndex, Layout.SB->BlockSize);
+ ArrayRef<uint8_t> BufferRef = makeArrayRef(arg);
+ BufferRef = BufferRef.slice(Offset, Layout.SB->BlockSize);
+ return llvm::all_of(BufferRef, [this](uint8_t B) { return B == Byte; });
+}
+
+namespace {
+TEST(MappedBlockStreamTest, CreateFpmStream) {
+ BumpPtrAllocator Allocator;
+ SuperBlock SB;
+ MSFLayout L;
+ L.SB = &SB;
+
+ SB.FreeBlockMapBlock = 1;
+ SB.BlockSize = 4096;
+
+ constexpr uint32_t NumFileBlocks = 4096 * 4;
+
+ std::vector<uint8_t> MsfBuffer(NumFileBlocks * SB.BlockSize);
+ MutableBinaryByteStream MsfStream(MsfBuffer, llvm::support::little);
+
+ SB.NumBlocks = NumFileBlocks;
+ auto FpmStream =
+ WritableMappedBlockStream::createFpmStream(L, MsfStream, Allocator);
+ // 4096 * 4 / 8 = 2048 bytes of FPM data is needed to describe 4096 * 4
+ // blocks. This translates to 1 FPM block.
+ EXPECT_EQ(2048u, FpmStream->getLength());
+ EXPECT_EQ(1u, FpmStream->getStreamLayout().Blocks.size());
+ EXPECT_EQ(1u, FpmStream->getStreamLayout().Blocks[0]);
+ // All blocks from FPM1 should be 1 initialized, and all blocks from FPM2
+ // should be 0 initialized (since we requested the main FPM, not the alt FPM)
+ for (int I = 0; I < 4; ++I) {
+ EXPECT_THAT(MsfBuffer, BlockIsFilledWith(L, 1 + I * SB.BlockSize, 0xFF));
+ EXPECT_THAT(MsfBuffer, BlockIsFilledWith(L, 2 + I * SB.BlockSize, 0));
+ }
+
+ ::memset(MsfBuffer.data(), 0, MsfBuffer.size());
+ FpmStream =
+ WritableMappedBlockStream::createFpmStream(L, MsfStream, Allocator, true);
+ // 4096 * 4 / 8 = 2048 bytes of FPM data is needed to describe 4096 * 4
+ // blocks. This translates to 1 FPM block.
+ EXPECT_EQ(2048u, FpmStream->getLength());
+ EXPECT_EQ(1u, FpmStream->getStreamLayout().Blocks.size());
+ EXPECT_EQ(2u, FpmStream->getStreamLayout().Blocks[0]);
+ // All blocks from FPM2 should be 1 initialized, and all blocks from FPM1
+ // should be 0 initialized (since we requested the alt FPM, not the main FPM)
+ for (int I = 0; I < 4; ++I) {
+ EXPECT_THAT(MsfBuffer, BlockIsFilledWith(L, 1 + I * SB.BlockSize, 0));
+ EXPECT_THAT(MsfBuffer, BlockIsFilledWith(L, 2 + I * SB.BlockSize, 0xFF));
+ }
+}
} // end anonymous namespace