[pdb] Add unit tests for PDB MappedBlockStream and zero copy

Differential Revision: http://reviews.llvm.org/D20837
Reviewed By: ruiu

llvm-svn: 271346
This commit is contained in:
Zachary Turner 2016-05-31 22:41:52 +00:00
parent f9acacaa92
commit 90b8b8db2e
12 changed files with 186 additions and 16 deletions

View File

@ -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 @@ public:
virtual StringRef getBlockData(uint32_t BlockIndex,
uint32_t NumBytes) const = 0;
virtual ArrayRef<support::ulittle32_t> getDirectoryBlockArray() = 0;
};
}
}

View File

@ -21,6 +21,7 @@
namespace llvm {
namespace pdb {
class PDBFile;
class InfoStream {
public:
InfoStream(PDBFile &File);

View File

@ -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<uint8_t> &Buffer) const override;
uint32_t getLength() const override { return StreamLength; }
uint32_t getNumBytesCopied() const;
private:
Error readBytes(uint32_t Offset, MutableArrayRef<uint8_t> Buffer) const;
bool tryReadContiguously(uint32_t Offset, uint32_t Size,
@ -41,7 +43,7 @@ private:
std::vector<uint32_t> BlockList;
mutable llvm::BumpPtrAllocator Pool;
mutable DenseMap<uint32_t, uint8_t *> CacheMap;
const PDBFile &Pdb;
const IPDBFile &Pdb;
};
} // end namespace pdb

View File

@ -39,10 +39,10 @@ public:
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 @@ public:
StringRef getBlockData(uint32_t BlockIndex, uint32_t NumBytes) const override;
ArrayRef<support::ulittle32_t> getDirectoryBlockArray() override;
ArrayRef<support::ulittle32_t> getDirectoryBlockArray();
Error parseFileHeaders();
Error parseStreamData();

View File

@ -278,6 +278,8 @@ public:
return TotalMemory;
}
size_t getBytesAllocated() const { return BytesAllocated; }
void PrintStats() const {
detail::printBumpPtrAllocatorStats(Slabs.size(), BytesAllocated,
getTotalMemory());

View File

@ -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"

View File

@ -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 @@ Error MappedBlockStream::readBytes(uint32_t Offset,
return Error::success();
}
uint32_t MappedBlockStream::getNumBytesCopied() const {
return static_cast<uint32_t>(Pool.getBytesAllocated());
}

View File

@ -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;

View File

@ -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"

View File

@ -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"

View File

@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS
)
set(DebugInfoPDBSources
MappedBlockStreamTest.cpp
PDBApiTest.cpp
)

View File

@ -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 <unordered_map>
#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<bool>(E)); \
if (E) \
consumeError(std::move(E)); \
}
#define EXPECT_ERROR(Err) \
{ \
auto E = std::move(Err); \
EXPECT_TRUE(static_cast<bool>(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<uint32_t>
getStreamBlockList(uint32_t StreamIndex) const override {
if (StreamIndex != 0)
return ArrayRef<uint32_t>();
return Blocks;
}
virtual StringRef getBlockData(uint32_t BlockIndex,
uint32_t NumBytes) const override {
return StringRef(&Data[BlockIndex], NumBytes);
}
private:
std::vector<uint32_t> Blocks;
std::vector<char> 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