Index: include/llvm/DebugInfo/PDB/Raw/IPDBFile.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/IPDBFile.h +++ include/llvm/DebugInfo/PDB/Raw/IPDBFile.h @@ -19,16 +19,12 @@ namespace llvm { namespace pdb { -struct IPDBFile { +class IPDBFile { public: virtual ~IPDBFile() {} virtual uint32_t getBlockSize() const = 0; virtual uint32_t getBlockCount() const = 0; - virtual uint32_t getNumDirectoryBytes() const = 0; - virtual uint32_t getBlockMapIndex() const = 0; - virtual uint32_t getNumDirectoryBlocks() const = 0; - virtual uint64_t getBlockMapOffset() const = 0; virtual uint32_t getNumStreams() const = 0; virtual uint32_t getStreamByteSize(uint32_t StreamIndex) const = 0; @@ -36,8 +32,6 @@ virtual StringRef getBlockData(uint32_t BlockIndex, uint32_t NumBytes) const = 0; - - virtual ArrayRef getDirectoryBlockArray() = 0; }; } } Index: include/llvm/DebugInfo/PDB/Raw/InfoStream.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/InfoStream.h +++ include/llvm/DebugInfo/PDB/Raw/InfoStream.h @@ -21,6 +21,7 @@ namespace llvm { namespace pdb { +class PDBFile; class InfoStream { public: InfoStream(PDBFile &File); Index: include/llvm/DebugInfo/PDB/Raw/MappedBlockStream.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/MappedBlockStream.h +++ include/llvm/DebugInfo/PDB/Raw/MappedBlockStream.h @@ -21,17 +21,19 @@ namespace llvm { namespace pdb { -class PDBFile; +class IPDBFile; class MappedBlockStream : public codeview::StreamInterface { public: - MappedBlockStream(uint32_t StreamIdx, const PDBFile &File); + MappedBlockStream(uint32_t StreamIdx, const IPDBFile &File); Error readBytes(uint32_t Offset, uint32_t Size, ArrayRef &Buffer) const override; uint32_t getLength() const override { return StreamLength; } + uint32_t getNumBytesCopied() const; + private: Error readBytes(uint32_t Offset, MutableArrayRef Buffer) const; bool tryReadContiguously(uint32_t Offset, uint32_t Size, @@ -41,7 +43,7 @@ std::vector BlockList; mutable llvm::BumpPtrAllocator Pool; mutable DenseMap CacheMap; - const PDBFile &Pdb; + const IPDBFile &Pdb; }; } // end namespace pdb Index: include/llvm/DebugInfo/PDB/Raw/PDBFile.h =================================================================== --- include/llvm/DebugInfo/PDB/Raw/PDBFile.h +++ include/llvm/DebugInfo/PDB/Raw/PDBFile.h @@ -39,10 +39,10 @@ uint32_t getBlockSize() const override; uint32_t getBlockCount() const override; - uint32_t getNumDirectoryBytes() const override; - uint32_t getBlockMapIndex() const override; - uint32_t getNumDirectoryBlocks() const override; - uint64_t getBlockMapOffset() const override; + uint32_t getNumDirectoryBytes() const; + uint32_t getBlockMapIndex() const; + uint32_t getNumDirectoryBlocks() const; + uint64_t getBlockMapOffset() const; uint32_t getNumStreams() const override; uint32_t getStreamByteSize(uint32_t StreamIndex) const override; @@ -50,7 +50,7 @@ StringRef getBlockData(uint32_t BlockIndex, uint32_t NumBytes) const override; - ArrayRef getDirectoryBlockArray() override; + ArrayRef getDirectoryBlockArray(); Error parseFileHeaders(); Error parseStreamData(); Index: include/llvm/Support/Allocator.h =================================================================== --- include/llvm/Support/Allocator.h +++ include/llvm/Support/Allocator.h @@ -278,6 +278,8 @@ return TotalMemory; } + size_t getBytesAllocated() const { return BytesAllocated; } + void PrintStats() const { detail::printBumpPtrAllocatorStats(Slabs.size(), BytesAllocated, getTotalMemory()); Index: lib/DebugInfo/PDB/Raw/InfoStream.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/InfoStream.cpp +++ lib/DebugInfo/PDB/Raw/InfoStream.cpp @@ -11,6 +11,7 @@ #include "llvm/ADT/BitVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/DebugInfo/CodeView/StreamReader.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" #include "llvm/DebugInfo/PDB/Raw/RawConstants.h" #include "llvm/DebugInfo/PDB/Raw/RawError.h" Index: lib/DebugInfo/PDB/Raw/MappedBlockStream.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/MappedBlockStream.cpp +++ lib/DebugInfo/PDB/Raw/MappedBlockStream.cpp @@ -14,7 +14,8 @@ using namespace llvm; using namespace llvm::pdb; -MappedBlockStream::MappedBlockStream(uint32_t StreamIdx, const PDBFile &File) : Pdb(File) { +MappedBlockStream::MappedBlockStream(uint32_t StreamIdx, const IPDBFile &File) + : Pdb(File) { if (StreamIdx >= Pdb.getNumStreams()) { StreamLength = 0; } else { @@ -119,3 +120,7 @@ return Error::success(); } + +uint32_t MappedBlockStream::getNumBytesCopied() const { + return static_cast(Pool.getBytesAllocated()); +} Index: lib/DebugInfo/PDB/Raw/ModStream.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/ModStream.cpp +++ lib/DebugInfo/PDB/Raw/ModStream.cpp @@ -11,6 +11,7 @@ #include "llvm/DebugInfo/CodeView/StreamReader.h" #include "llvm/DebugInfo/PDB/Raw/ModInfo.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" #include "llvm/DebugInfo/PDB/Raw/RawError.h" using namespace llvm; Index: lib/DebugInfo/PDB/Raw/SymbolStream.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/SymbolStream.cpp +++ lib/DebugInfo/PDB/Raw/SymbolStream.cpp @@ -13,6 +13,7 @@ #include "llvm/DebugInfo/CodeView/StreamReader.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" #include "llvm/DebugInfo/PDB/Raw/RawConstants.h" #include "llvm/DebugInfo/PDB/Raw/RawError.h" Index: lib/DebugInfo/PDB/Raw/TpiStream.cpp =================================================================== --- lib/DebugInfo/PDB/Raw/TpiStream.cpp +++ lib/DebugInfo/PDB/Raw/TpiStream.cpp @@ -13,6 +13,7 @@ #include "llvm/DebugInfo/CodeView/StreamReader.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" #include "llvm/DebugInfo/PDB/Raw/RawConstants.h" #include "llvm/DebugInfo/PDB/Raw/RawError.h" Index: unittests/DebugInfo/PDB/CMakeLists.txt =================================================================== --- unittests/DebugInfo/PDB/CMakeLists.txt +++ unittests/DebugInfo/PDB/CMakeLists.txt @@ -3,6 +3,7 @@ ) set(DebugInfoPDBSources + MappedBlockStreamTest.cpp PDBApiTest.cpp ) Index: unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp =================================================================== --- /dev/null +++ unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp @@ -0,0 +1,161 @@ +//===- 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/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 = std::move(Err); \ + EXPECT_FALSE(static_cast(E)); \ + if (E) \ + consumeError(std::move(E)); \ + } + +#define EXPECT_ERROR(Err) \ + { \ + auto E = std::move(Err); \ + EXPECT_TRUE(static_cast(E)); \ + if (E) \ + consumeError(std::move(E)); \ + } + +class DiscontiguousFile : public IPDBFile { +public: + DiscontiguousFile() + : Blocks{0, 1, 2, 5, 4, 3, 6, 7, 8, 9}, + Data{'A', 'B', 'C', 'F', 'E', 'D', 'G', 'H', 'I', 'J'} {} + + 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 StringRef getBlockData(uint32_t BlockIndex, + uint32_t NumBytes) const override { + return StringRef(&Data[BlockIndex], NumBytes); + } + +private: + std::vector Blocks; + std::vector Data; +}; + +// Tests that a read which is entirely contained within a single block works +// and does not allocate. +TEST(MappedBlockStreamTest, ZeroCopyReadNoBreak) { + DiscontiguousFile F; + MappedBlockStream S(0, F); + StreamReader R(S); + StringRef Str; + EXPECT_NO_ERROR(R.readFixedString(Str, 1)); + EXPECT_EQ(Str, StringRef("A")); + EXPECT_EQ(0, S.getNumBytesCopied()); +} + +// 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; + MappedBlockStream S(0, F); + StreamReader R(S); + StringRef Str = "ZYXWVUTSRQPONMLKJIHGFEDCBA"; + EXPECT_NO_ERROR(R.readFixedString(Str, 1)); + EXPECT_EQ(Str, StringRef("A")); + EXPECT_EQ(0, 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; + MappedBlockStream S(0, F); + StreamReader R(S); + StringRef Str; + EXPECT_NO_ERROR(R.readFixedString(Str, 2)); + EXPECT_EQ(Str, StringRef("AB")); + EXPECT_EQ(0, S.getNumBytesCopied()); + + R.setOffset(6); + EXPECT_NO_ERROR(R.readFixedString(Str, 4)); + EXPECT_EQ(Str, StringRef("GHIJ")); + EXPECT_EQ(0, 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; + MappedBlockStream S(0, F); + StreamReader R(S); + StringRef Str; + EXPECT_NO_ERROR(R.readFixedString(Str, 10)); + EXPECT_EQ(Str, StringRef("ABCDEFGHIJ")); + EXPECT_EQ(10, 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; + MappedBlockStream S(0, F); + StreamReader R(S); + StringRef Str; + + R.setOffset(10); + EXPECT_ERROR(R.readFixedString(Str, 1)); + EXPECT_EQ(0, 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; + MappedBlockStream S(0, F); + StreamReader R(S); + StringRef Str; + + R.setOffset(6); + EXPECT_ERROR(R.readFixedString(Str, 5)); + EXPECT_EQ(0, 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; + MappedBlockStream S(0, F); + StreamReader R(S); + StringRef Str; + + EXPECT_ERROR(R.readFixedString(Str, 11)); + EXPECT_EQ(0, S.getNumBytesCopied()); +} + +} // end anonymous namespace