From 50b0dc9e5b9d9a92b283cc6567283d1c02cf594f Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Wed, 2 Aug 2017 22:31:39 +0000 Subject: [PATCH] [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 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@309896 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/DebugInfo/MSF/MSFCommon.h | 19 ++-- .../llvm/DebugInfo/MSF/MappedBlockStream.h | 2 +- .../DebugInfo/PDB/Native/PDBFileBuilder.h | 2 + include/llvm/Support/MathExtras.h | 5 + lib/DebugInfo/MSF/MSFBuilder.cpp | 46 +++++++- lib/DebugInfo/MSF/MSFCommon.cpp | 19 +++- lib/DebugInfo/MSF/MappedBlockStream.cpp | 25 ++++- lib/DebugInfo/PDB/Native/PDBFile.cpp | 3 +- lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp | 28 +++++ test/DebugInfo/PDB/write-fpm.test | 11 ++ unittests/DebugInfo/MSF/CMakeLists.txt | 1 + unittests/DebugInfo/MSF/MSFBuilderTest.cpp | 36 ++++++ unittests/DebugInfo/MSF/MSFCommonTest.cpp | 104 ++++++++++++++++++ .../DebugInfo/MSF/MappedBlockStreamTest.cpp | 55 +++++++++ 14 files changed, 336 insertions(+), 20 deletions(-) create mode 100644 test/DebugInfo/PDB/write-fpm.test create mode 100644 unittests/DebugInfo/MSF/MSFCommonTest.cpp diff --git a/include/llvm/DebugInfo/MSF/MSFCommon.h b/include/llvm/DebugInfo/MSF/MSFCommon.h index 458de415ee1..f28415d4e60 100644 --- a/include/llvm/DebugInfo/MSF/MSFCommon.h +++ b/include/llvm/DebugInfo/MSF/MSFCommon.h @@ -74,7 +74,9 @@ public: /// \brief Determine the layout of the FPM stream, given the MSF layout. An FPM /// stream spans 1 or more blocks, each at equally spaced intervals throughout /// the file. -MSFStreamLayout getFpmStreamLayout(const MSFLayout &Msf); +MSFStreamLayout getFpmStreamLayout(const MSFLayout &Msf, + bool IncludeUnusedFpmData = false, + bool AltFpm = false); inline bool isValidBlockSize(uint32_t Size) { switch (Size) { @@ -95,7 +97,7 @@ inline uint32_t getMinimumBlockCount() { return 4; } inline uint32_t getFirstUnreservedBlock() { return 3; } inline uint64_t bytesToBlocks(uint64_t NumBytes, uint64_t BlockSize) { - return alignTo(NumBytes, BlockSize) / BlockSize; + return divideCeil(NumBytes, BlockSize); } inline uint64_t blockToOffset(uint64_t BlockNumber, uint64_t BlockSize) { @@ -106,13 +108,14 @@ inline uint32_t getFpmIntervalLength(const MSFLayout &L) { return L.SB->BlockSize; } -inline uint32_t getNumFpmIntervals(const MSFLayout &L) { - uint32_t Length = getFpmIntervalLength(L); - return alignTo(L.SB->NumBlocks, Length) / Length; -} +inline uint32_t getNumFpmIntervals(const MSFLayout &L, + bool IncludeUnusedFpmData = false) { + if (IncludeUnusedFpmData) + return divideCeil(L.SB->NumBlocks, L.SB->BlockSize); -inline uint32_t getFullFpmByteSize(const MSFLayout &L) { - return alignTo(L.SB->NumBlocks, 8) / 8; + // We want the minimum number of intervals required, where each interval can + // represent BlockSize * 8 blocks. + return divideCeil(L.SB->NumBlocks, 8 * L.SB->BlockSize); } Error validateSuperBlock(const SuperBlock &SB); diff --git a/include/llvm/DebugInfo/MSF/MappedBlockStream.h b/include/llvm/DebugInfo/MSF/MappedBlockStream.h index 6cb07d26836..f65e52922da 100644 --- a/include/llvm/DebugInfo/MSF/MappedBlockStream.h +++ b/include/llvm/DebugInfo/MSF/MappedBlockStream.h @@ -122,7 +122,7 @@ public: static std::unique_ptr createFpmStream(const MSFLayout &Layout, WritableBinaryStreamRef MsfData, - BumpPtrAllocator &Allocator); + BumpPtrAllocator &Allocator, bool AltFpm = false); support::endianness getEndian() const override { return support::little; diff --git a/include/llvm/DebugInfo/PDB/Native/PDBFileBuilder.h b/include/llvm/DebugInfo/PDB/Native/PDBFileBuilder.h index 68d9d306de6..684fd0eff09 100644 --- a/include/llvm/DebugInfo/PDB/Native/PDBFileBuilder.h +++ b/include/llvm/DebugInfo/PDB/Native/PDBFileBuilder.h @@ -61,6 +61,8 @@ public: private: Expected finalizeMsfLayout(); + void commitFpm(WritableBinaryStream &MsfBuffer, const msf::MSFLayout &Layout); + BumpPtrAllocator &Allocator; std::unique_ptr Msf; diff --git a/include/llvm/Support/MathExtras.h b/include/llvm/Support/MathExtras.h index db01e99334a..a37a16784e2 100644 --- a/include/llvm/Support/MathExtras.h +++ b/include/llvm/Support/MathExtras.h @@ -687,6 +687,11 @@ template constexpr inline uint64_t alignTo(uint64_t Value) { return (Value + Align - 1) / Align * Align; } +/// Returns the integer ceil(Numerator / Denominator). +inline uint64_t divideCeil(uint64_t Numerator, uint64_t Denominator) { + return alignTo(Numerator, Denominator) / Denominator; +} + /// \c alignTo for contexts where a constant expression is required. /// \sa alignTo /// diff --git a/lib/DebugInfo/MSF/MSFBuilder.cpp b/lib/DebugInfo/MSF/MSFBuilder.cpp index 0f4f785abf5..de5704ff665 100644 --- a/lib/DebugInfo/MSF/MSFBuilder.cpp +++ b/lib/DebugInfo/MSF/MSFBuilder.cpp @@ -107,7 +107,23 @@ Error MSFBuilder::allocateBlocks(uint32_t NumBlocks, return make_error(msf_error_code::insufficient_buffer, "There are no free Blocks in the file"); uint32_t AllocBlocks = NumBlocks - NumFreeBlocks; - FreeBlocks.resize(AllocBlocks + FreeBlocks.size(), true); + uint32_t OldBlockCount = FreeBlocks.size(); + uint32_t NewBlockCount = AllocBlocks + OldBlockCount; + uint32_t NextFpmBlock = alignTo(OldBlockCount, BlockSize) + 1; + FreeBlocks.resize(NewBlockCount, true); + // If we crossed over an fpm page, we actually need to allocate 2 extra + // blocks for each FPM group crossed and mark both blocks from the group as + // used. We may not actually use them since there are many more FPM blocks + // present than are required to represent all blocks in a given PDB, but we + // need to make sure they aren't allocated to a stream or something else. + // At the end when committing the PDB, we'll go through and mark the + // extraneous ones unused. + while (NextFpmBlock < NewBlockCount) { + NewBlockCount += 2; + FreeBlocks.resize(NewBlockCount, true); + FreeBlocks.reset(NextFpmBlock, NextFpmBlock + 2); + NextFpmBlock += BlockSize; + } } int I = 0; @@ -229,6 +245,19 @@ uint32_t MSFBuilder::computeDirectoryByteSize() const { return Size; } +static void finalizeFpmBlockStatus(uint32_t B, ArrayRef &FpmBlocks, + BitVector &Fpm) { + if (FpmBlocks.empty() || FpmBlocks.front() != B) { + Fpm.set(B); + return; + } + + // If the next block in the actual layout is this block, it should *not* be + // free. + assert(!Fpm.test(B)); + FpmBlocks = FpmBlocks.drop_front(); +} + Expected MSFBuilder::build() { SuperBlock *SB = Allocator.Allocate(); MSFLayout L; @@ -287,5 +316,20 @@ Expected MSFBuilder::build() { } } + // FPM blocks occur in pairs at every `BlockLength` interval. While blocks of + // this form are reserved for FPM blocks, not all blocks of this form will + // actually be needed for FPM data because there are more blocks of this form + // than are required to represent a PDB file with a given number of blocks. + // So we need to find out which blocks are *actually* going to be real FPM + // blocks, then mark the reset of the reserved blocks as unallocated. + MSFStreamLayout FpmLayout = msf::getFpmStreamLayout(L, true); + auto FpmBlocks = makeArrayRef(FpmLayout.Blocks); + for (uint32_t B = kFreePageMap0Block; B < SB->NumBlocks; + B += msf::getFpmIntervalLength(L)) { + finalizeFpmBlockStatus(B, FpmBlocks, FreeBlocks); + finalizeFpmBlockStatus(B + 1, FpmBlocks, FreeBlocks); + } + L.FreePageMap = FreeBlocks; + return L; } diff --git a/lib/DebugInfo/MSF/MSFCommon.cpp b/lib/DebugInfo/MSF/MSFCommon.cpp index 484e1845ce2..d7e1dcf31a3 100644 --- a/lib/DebugInfo/MSF/MSFCommon.cpp +++ b/lib/DebugInfo/MSF/MSFCommon.cpp @@ -60,17 +60,26 @@ Error llvm::msf::validateSuperBlock(const SuperBlock &SB) { return Error::success(); } -MSFStreamLayout llvm::msf::getFpmStreamLayout(const MSFLayout &Msf) { +MSFStreamLayout llvm::msf::getFpmStreamLayout(const MSFLayout &Msf, + bool IncludeUnusedFpmData, + bool AltFpm) { MSFStreamLayout FL; - uint32_t NumFpmIntervals = getNumFpmIntervals(Msf); + uint32_t NumFpmIntervals = getNumFpmIntervals(Msf, IncludeUnusedFpmData); support::ulittle32_t FpmBlock = Msf.SB->FreeBlockMapBlock; assert(FpmBlock == 1 || FpmBlock == 2); - while (NumFpmIntervals > 0) { + if (AltFpm) { + // If they requested the alternate FPM, then 2 becomes 1 and 1 becomes 2. + FpmBlock = 3U - FpmBlock; + } + for (uint32_t I = 0; I < NumFpmIntervals; ++I) { FL.Blocks.push_back(FpmBlock); FpmBlock += msf::getFpmIntervalLength(Msf); - --NumFpmIntervals; } - FL.Length = getFullFpmByteSize(Msf); + + if (IncludeUnusedFpmData) + FL.Length = NumFpmIntervals * Msf.SB->BlockSize; + else + FL.Length = divideCeil(Msf.SB->NumBlocks, 8); return FL; } diff --git a/lib/DebugInfo/MSF/MappedBlockStream.cpp b/lib/DebugInfo/MSF/MappedBlockStream.cpp index 2a200edeaf7..f28f944131b 100644 --- a/lib/DebugInfo/MSF/MappedBlockStream.cpp +++ b/lib/DebugInfo/MSF/MappedBlockStream.cpp @@ -11,6 +11,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include "llvm/Support/MathExtras.h" @@ -347,9 +348,27 @@ WritableMappedBlockStream::createDirectoryStream( std::unique_ptr WritableMappedBlockStream::createFpmStream(const MSFLayout &Layout, WritableBinaryStreamRef MsfData, - BumpPtrAllocator &Allocator) { - MSFStreamLayout SL(getFpmStreamLayout(Layout)); - return createStream(Layout.SB->BlockSize, SL, MsfData, Allocator); + BumpPtrAllocator &Allocator, + bool AltFpm) { + // We only want to give the user a stream containing the bytes of the FPM that + // are actually valid, but we want to initialize all of the bytes, even those + // that come from reserved FPM blocks where the entire block is unused. To do + // this, we first create the full layout, which gives us a stream with all + // bytes and all blocks, and initialize everything to 0xFF (all blocks in the + // file are unused). Then we create the minimal layout (which contains only a + // subset of the bytes previously initialized), and return that to the user. + MSFStreamLayout MinLayout(getFpmStreamLayout(Layout, false, AltFpm)); + + MSFStreamLayout FullLayout(getFpmStreamLayout(Layout, true, AltFpm)); + auto Result = + createStream(Layout.SB->BlockSize, FullLayout, MsfData, Allocator); + if (!Result) + return Result; + std::vector InitData(Layout.SB->BlockSize, 0xFF); + BinaryStreamWriter Initializer(*Result); + while (Initializer.bytesRemaining() > 0) + cantFail(Initializer.writeBytes(InitData)); + return createStream(Layout.SB->BlockSize, MinLayout, MsfData, Allocator); } Error WritableMappedBlockStream::readBytes(uint32_t Offset, uint32_t Size, diff --git a/lib/DebugInfo/PDB/Native/PDBFile.cpp b/lib/DebugInfo/PDB/Native/PDBFile.cpp index e0bb460d21c..de460d03e6b 100644 --- a/lib/DebugInfo/PDB/Native/PDBFile.cpp +++ b/lib/DebugInfo/PDB/Native/PDBFile.cpp @@ -150,8 +150,7 @@ Error PDBFile::parseFileHeaders() { MappedBlockStream::createFpmStream(ContainerLayout, *Buffer, Allocator); BinaryStreamReader FpmReader(*FpmStream); ArrayRef FpmBytes; - if (auto EC = FpmReader.readBytes(FpmBytes, - msf::getFullFpmByteSize(ContainerLayout))) + if (auto EC = FpmReader.readBytes(FpmBytes, FpmReader.bytesRemaining())) return EC; uint32_t BlocksRemaining = getBlockCount(); uint32_t BI = 0; diff --git a/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp b/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp index a8b80ac6d78..09e86bf0c13 100644 --- a/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp +++ b/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp @@ -155,6 +155,31 @@ Expected PDBFileBuilder::getNamedStreamIndex(StringRef Name) const { return SN; } +void PDBFileBuilder::commitFpm(WritableBinaryStream &MsfBuffer, + const MSFLayout &Layout) { + auto FpmStream = + WritableMappedBlockStream::createFpmStream(Layout, MsfBuffer, Allocator); + + // We only need to create the alt fpm stream so that it gets initialized. + WritableMappedBlockStream::createFpmStream(Layout, MsfBuffer, Allocator, + true); + + uint32_t BI = 0; + BinaryStreamWriter FpmWriter(*FpmStream); + while (BI < Layout.SB->NumBlocks) { + uint8_t ThisByte = 0; + for (uint32_t I = 0; I < 8; ++I) { + bool IsFree = + (BI < Layout.SB->NumBlocks) ? Layout.FreePageMap.test(BI) : true; + uint8_t Mask = uint8_t(IsFree) << I; + ThisByte |= Mask; + ++BI; + } + cantFail(FpmWriter.writeObject(ThisByte)); + } + assert(FpmWriter.bytesRemaining() == 0); +} + Error PDBFileBuilder::commit(StringRef Filename) { assert(!Filename.empty()); auto ExpectedLayout = finalizeMsfLayout(); @@ -173,6 +198,9 @@ Error PDBFileBuilder::commit(StringRef Filename) { if (auto EC = Writer.writeObject(*Layout.SB)) return EC; + + commitFpm(Buffer, Layout); + uint32_t BlockMapOffset = msf::blockToOffset(Layout.SB->BlockMapAddr, Layout.SB->BlockSize); Writer.setOffset(BlockMapOffset); diff --git a/test/DebugInfo/PDB/write-fpm.test b/test/DebugInfo/PDB/write-fpm.test new file mode 100644 index 00000000000..6e2c67d009a --- /dev/null +++ b/test/DebugInfo/PDB/write-fpm.test @@ -0,0 +1,11 @@ +; RUN: llvm-pdbutil yaml2pdb -pdb=%t.pdb %p/Inputs/one-symbol.yaml +; RUN: llvm-pdbutil bytes -fpm %t.pdb | FileCheck %s + + +CHECK: Free Page Map +CHECK-NEXT: ============================================================ +CHECK-NEXT: Block 1 ( +CHECK-NEXT: 1000: 04F8FFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................................| +CHECK-NEXT: 1020: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................................| +CHECK: 1FE0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................................| +CHECK: ) diff --git a/unittests/DebugInfo/MSF/CMakeLists.txt b/unittests/DebugInfo/MSF/CMakeLists.txt index 3f5acc2d2ff..25e011178cd 100644 --- a/unittests/DebugInfo/MSF/CMakeLists.txt +++ b/unittests/DebugInfo/MSF/CMakeLists.txt @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS set(DebugInfoMSFSources MappedBlockStreamTest.cpp MSFBuilderTest.cpp + MSFCommonTest.cpp ) add_llvm_unittest(DebugInfoMSFTests diff --git a/unittests/DebugInfo/MSF/MSFBuilderTest.cpp b/unittests/DebugInfo/MSF/MSFBuilderTest.cpp index 4791c982fd8..a91ac8d443f 100644 --- a/unittests/DebugInfo/MSF/MSFBuilderTest.cpp +++ b/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 @@ TEST_F(MSFBuilderTest, DirectoryBlockHintOverestimated) { EXPECT_EQ(1U, L.DirectoryBlocks.size()); EXPECT_EQ(B + 1, L.DirectoryBlocks[0]); } + +TEST_F(MSFBuilderTest, StreamDoesntUseFpmBlocks) { + Expected 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 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 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/unittests/DebugInfo/MSF/MSFCommonTest.cpp b/unittests/DebugInfo/MSF/MSFCommonTest.cpp new file mode 100644 index 00000000000..144f5b113fb --- /dev/null +++ b/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/unittests/DebugInfo/MSF/MappedBlockStreamTest.cpp b/unittests/DebugInfo/MSF/MappedBlockStreamTest.cpp index 94cd347d09a..94c4898551d 100644 --- a/unittests/DebugInfo/MSF/MappedBlockStreamTest.cpp +++ b/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 @@ -494,5 +495,59 @@ TEST(MappedBlockStreamTest, DataLivesAfterStreamDestruction) { EXPECT_EQ(Str[0], Str[1]); } +} // namespace + +MATCHER_P3(BlockIsFilledWith, Layout, BlockIndex, Byte, "succeeded") { + uint64_t Offset = msf::blockToOffset(BlockIndex, Layout.SB->BlockSize); + ArrayRef 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 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 -- 2.50.1