//===- llvm/unittest/DebugInfo/PDB/MappedBlockStreamTest.cpp --------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include "llvm/DebugInfo/CodeView/StreamReader.h" #include "llvm/DebugInfo/CodeView/StreamRef.h" #include "llvm/DebugInfo/PDB/Raw/IPDBFile.h" #include "llvm/DebugInfo/PDB/Raw/IPDBStreamData.h" #include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h" #include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h" #include "gtest/gtest.h" using namespace llvm; using namespace llvm::codeview; using namespace llvm::pdb; namespace { #define EXPECT_NO_ERROR(Err) \ { \ auto E = Err; \ EXPECT_FALSE(static_cast(E)); \ if (E) \ consumeError(std::move(E)); \ } #define EXPECT_ERROR(Err) \ { \ auto E = Err; \ EXPECT_TRUE(static_cast(E)); \ if (E) \ consumeError(std::move(E)); \ } static const uint32_t BlocksAry[] = {0, 1, 2, 5, 4, 3, 6, 7, 8, 9}; static const char DataAry[] = {'A', 'B', 'C', 'F', 'E', 'D', 'G', 'H', 'I', 'J'}; class DiscontiguousFile : public IPDBFile { public: DiscontiguousFile() : Blocks(std::begin(BlocksAry), std::end(BlocksAry)), Data(std::begin(DataAry), std::end(DataAry)) {} virtual uint32_t getBlockSize() const override { return 1; } virtual uint32_t getBlockCount() const override { return 10; } virtual uint32_t getNumStreams() const override { return 1; } virtual uint32_t getStreamByteSize(uint32_t StreamIndex) const override { return getBlockCount() * getBlockSize(); } virtual ArrayRef getStreamBlockList(uint32_t StreamIndex) const override { if (StreamIndex != 0) return ArrayRef(); return Blocks; } virtual ArrayRef getBlockData(uint32_t BlockIndex, uint32_t NumBytes) const override { return ArrayRef(&Data[BlockIndex], NumBytes); } private: std::vector Blocks; std::vector Data; }; class MappedBlockStreamImpl : public MappedBlockStream { public: MappedBlockStreamImpl(std::unique_ptr Data, const IPDBFile &File) : MappedBlockStream(std::move(Data), File) {} }; // Tests that a read which is entirely contained within a single block works // and does not allocate. TEST(MappedBlockStreamTest, ReadBeyondEndOfStreamRef) { DiscontiguousFile F; MappedBlockStreamImpl S(llvm::make_unique(0, F), F); StreamReader R(S); StreamRef SR; EXPECT_NO_ERROR(R.readStreamRef(SR, 0U)); ArrayRef Buffer; EXPECT_ERROR(SR.readBytes(0U, 1U, Buffer)); EXPECT_NO_ERROR(R.readStreamRef(SR, 1U)); EXPECT_ERROR(SR.readBytes(1U, 1U, Buffer)); } // Tests that a read which outputs into a full destination buffer works and // does not fail due to the length of the output buffer. TEST(MappedBlockStreamTest, ReadOntoNonEmptyBuffer) { DiscontiguousFile F; MappedBlockStreamImpl S(llvm::make_unique(0, F), F); StreamReader R(S); StringRef Str = "ZYXWVUTSRQPONMLKJIHGFEDCBA"; EXPECT_NO_ERROR(R.readFixedString(Str, 1)); EXPECT_EQ(Str, StringRef("A")); EXPECT_EQ(0U, S.getNumBytesCopied()); } // Tests that a read which crosses a block boundary, but where the subsequent // blocks are still contiguous in memory to the previous block works and does // not allocate memory. TEST(MappedBlockStreamTest, ZeroCopyReadContiguousBreak) { DiscontiguousFile F; MappedBlockStreamImpl S(llvm::make_unique(0, F), F); StreamReader R(S); StringRef Str; EXPECT_NO_ERROR(R.readFixedString(Str, 2)); EXPECT_EQ(Str, StringRef("AB")); EXPECT_EQ(0U, S.getNumBytesCopied()); R.setOffset(6); EXPECT_NO_ERROR(R.readFixedString(Str, 4)); EXPECT_EQ(Str, StringRef("GHIJ")); EXPECT_EQ(0U, S.getNumBytesCopied()); } // Tests that a read which crosses a block boundary and cannot be referenced // contiguously works and allocates only the precise amount of bytes // requested. TEST(MappedBlockStreamTest, CopyReadNonContiguousBreak) { DiscontiguousFile F; MappedBlockStreamImpl S(llvm::make_unique(0, F), F); StreamReader R(S); StringRef Str; EXPECT_NO_ERROR(R.readFixedString(Str, 10)); EXPECT_EQ(Str, StringRef("ABCDEFGHIJ")); EXPECT_EQ(10U, S.getNumBytesCopied()); } // Test that an out of bounds read which doesn't cross a block boundary // fails and allocates no memory. TEST(MappedBlockStreamTest, InvalidReadSizeNoBreak) { DiscontiguousFile F; MappedBlockStreamImpl S(llvm::make_unique(0, F), F); StreamReader R(S); StringRef Str; R.setOffset(10); EXPECT_ERROR(R.readFixedString(Str, 1)); EXPECT_EQ(0U, S.getNumBytesCopied()); } // Test that an out of bounds read which crosses a contiguous block boundary // fails and allocates no memory. TEST(MappedBlockStreamTest, InvalidReadSizeContiguousBreak) { DiscontiguousFile F; MappedBlockStreamImpl S(llvm::make_unique(0, F), F); StreamReader R(S); StringRef Str; R.setOffset(6); EXPECT_ERROR(R.readFixedString(Str, 5)); EXPECT_EQ(0U, S.getNumBytesCopied()); } // Test that an out of bounds read which crosses a discontiguous block // boundary fails and allocates no memory. TEST(MappedBlockStreamTest, InvalidReadSizeNonContiguousBreak) { DiscontiguousFile F; MappedBlockStreamImpl S(llvm::make_unique(0, F), F); StreamReader R(S); StringRef Str; EXPECT_ERROR(R.readFixedString(Str, 11)); EXPECT_EQ(0U, S.getNumBytesCopied()); } // Tests that a read which is entirely contained within a single block but // beyond the end of a StreamRef fails. TEST(MappedBlockStreamTest, ZeroCopyReadNoBreak) { DiscontiguousFile F; MappedBlockStreamImpl S(llvm::make_unique(0, F), F); StreamReader R(S); StringRef Str; EXPECT_NO_ERROR(R.readFixedString(Str, 1)); EXPECT_EQ(Str, StringRef("A")); EXPECT_EQ(0U, S.getNumBytesCopied()); } } // end anonymous namespace