Skip to content

Commit

Permalink
[ProfileData] Add unit test infrastructure for sample profile reader/…
Browse files Browse the repository at this point in the history
…writer

Summary:
Adds support for in-memory round-trip of sample profile data along with basic
round trip unit tests. This will also make it easier to include unit tests for
future changes to sample profiling.

Reviewers: davidxl, dnovillo, silvas

Subscribers: llvm-commits

Differential Revision: http://reviews.llvm.org/D15211

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@255264 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
copperlist committed Dec 10, 2015
1 parent 7845ed0 commit 46cf0f0
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 23 deletions.
4 changes: 4 additions & 0 deletions include/llvm/ProfileData/SampleProfReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,10 @@ class SampleProfileReader {
static ErrorOr<std::unique_ptr<SampleProfileReader>>
create(StringRef Filename, LLVMContext &C);

/// \brief Create a sample profile reader from the supplied memory buffer.
static ErrorOr<std::unique_ptr<SampleProfileReader>>
create(std::unique_ptr<MemoryBuffer> &B, LLVMContext &C);

protected:
/// \brief Map every function to its associated profile.
///
Expand Down
37 changes: 26 additions & 11 deletions include/llvm/ProfileData/SampleProfWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ enum SampleProfileFormat { SPF_None = 0, SPF_Text, SPF_Binary, SPF_GCC };
/// \brief Sample-based profile writer. Base class.
class SampleProfileWriter {
public:
SampleProfileWriter(StringRef Filename, std::error_code &EC,
sys::fs::OpenFlags Flags)
: OS(Filename, EC, Flags) {}
virtual ~SampleProfileWriter() {}

/// Write sample profiles in \p S for function \p FName.
Expand All @@ -55,30 +52,40 @@ class SampleProfileWriter {
return sampleprof_error::success;
}

raw_ostream &getOutputStream() { return *OutputStream; }

/// Profile writer factory.
///
/// Create a new writer based on the value of \p Format.
/// Create a new file writer based on the value of \p Format.
static ErrorOr<std::unique_ptr<SampleProfileWriter>>
create(StringRef Filename, SampleProfileFormat Format);

/// Create a new stream writer based on the value of \p Format.
/// For testing.
static ErrorOr<std::unique_ptr<SampleProfileWriter>>
create(std::unique_ptr<raw_ostream> &OS, SampleProfileFormat Format);

protected:
SampleProfileWriter(std::unique_ptr<raw_ostream> &OS)
: OutputStream(std::move(OS)) {}

/// \brief Write a file header for the profile file.
virtual std::error_code
writeHeader(const StringMap<FunctionSamples> &ProfileMap) = 0;

/// \brief Output stream where to emit the profile to.
raw_fd_ostream OS;
std::unique_ptr<raw_ostream> OutputStream;
};

/// \brief Sample-based profile writer (text format).
class SampleProfileWriterText : public SampleProfileWriter {
public:
SampleProfileWriterText(StringRef F, std::error_code &EC)
: SampleProfileWriter(F, EC, sys::fs::F_Text), Indent(0) {}

std::error_code write(StringRef FName, const FunctionSamples &S) override;

protected:
SampleProfileWriterText(std::unique_ptr<raw_ostream> &OS)
: SampleProfileWriter(OS), Indent(0) {}

std::error_code
writeHeader(const StringMap<FunctionSamples> &ProfileMap) override {
return sampleprof_error::success;
Expand All @@ -89,17 +96,21 @@ class SampleProfileWriterText : public SampleProfileWriter {
///
/// This is used when printing inlined callees.
unsigned Indent;

friend ErrorOr<std::unique_ptr<SampleProfileWriter>>
SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
SampleProfileFormat Format);
};

/// \brief Sample-based profile writer (binary format).
class SampleProfileWriterBinary : public SampleProfileWriter {
public:
SampleProfileWriterBinary(StringRef F, std::error_code &EC)
: SampleProfileWriter(F, EC, sys::fs::F_None), NameTable() {}

std::error_code write(StringRef F, const FunctionSamples &S) override;

protected:
SampleProfileWriterBinary(std::unique_ptr<raw_ostream> &OS)
: SampleProfileWriter(OS), NameTable() {}

std::error_code
writeHeader(const StringMap<FunctionSamples> &ProfileMap) override;
std::error_code writeNameIdx(StringRef FName);
Expand All @@ -110,6 +121,10 @@ class SampleProfileWriterBinary : public SampleProfileWriter {
void addNames(const FunctionSamples &S);

MapVector<StringRef, uint32_t> NameTable;

friend ErrorOr<std::unique_ptr<SampleProfileWriter>>
SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
SampleProfileFormat Format);
};

} // End namespace sampleprof
Expand Down
26 changes: 19 additions & 7 deletions lib/ProfileData/SampleProfReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -693,15 +693,27 @@ SampleProfileReader::create(StringRef Filename, LLVMContext &C) {
auto BufferOrError = setupMemoryBuffer(Filename);
if (std::error_code EC = BufferOrError.getError())
return EC;
return create(BufferOrError.get(), C);
}

auto Buffer = std::move(BufferOrError.get());
/// \brief Create a sample profile reader based on the format of the input data.
///
/// \param B The memory buffer to create the reader from (assumes ownership).
///
/// \param Reader The reader to instantiate according to \p Filename's format.
///
/// \param C The LLVM context to use to emit diagnostics.
///
/// \returns an error code indicating the status of the created reader.
ErrorOr<std::unique_ptr<SampleProfileReader>>
SampleProfileReader::create(std::unique_ptr<MemoryBuffer> &B, LLVMContext &C) {
std::unique_ptr<SampleProfileReader> Reader;
if (SampleProfileReaderBinary::hasFormat(*Buffer))
Reader.reset(new SampleProfileReaderBinary(std::move(Buffer), C));
else if (SampleProfileReaderGCC::hasFormat(*Buffer))
Reader.reset(new SampleProfileReaderGCC(std::move(Buffer), C));
else if (SampleProfileReaderText::hasFormat(*Buffer))
Reader.reset(new SampleProfileReaderText(std::move(Buffer), C));
if (SampleProfileReaderBinary::hasFormat(*B))
Reader.reset(new SampleProfileReaderBinary(std::move(B), C));
else if (SampleProfileReaderGCC::hasFormat(*B))
Reader.reset(new SampleProfileReaderGCC(std::move(B), C));
else if (SampleProfileReaderText::hasFormat(*B))
Reader.reset(new SampleProfileReaderText(std::move(B), C));
else
return sampleprof_error::unrecognized_format;

Expand Down
40 changes: 35 additions & 5 deletions lib/ProfileData/SampleProfWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ using namespace llvm;
/// it needs to be parsed by the SampleProfileReaderText class.
std::error_code SampleProfileWriterText::write(StringRef FName,
const FunctionSamples &S) {
auto &OS = *OutputStream;

OS << FName << ":" << S.getTotalSamples();
if (Indent == 0)
OS << ":" << S.getHeadSamples();
Expand Down Expand Up @@ -84,7 +86,7 @@ std::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName) {
const auto &ret = NameTable.find(FName);
if (ret == NameTable.end())
return sampleprof_error::truncated_name_table;
encodeULEB128(ret->second, OS);
encodeULEB128(ret->second, *OutputStream);
return sampleprof_error::success;
}

Expand Down Expand Up @@ -112,6 +114,8 @@ void SampleProfileWriterBinary::addNames(const FunctionSamples &S) {

std::error_code SampleProfileWriterBinary::writeHeader(
const StringMap<FunctionSamples> &ProfileMap) {
auto &OS = *OutputStream;

// Write file magic identifier.
encodeULEB128(SPMagic(), OS);
encodeULEB128(SPVersion(), OS);
Expand All @@ -134,6 +138,8 @@ std::error_code SampleProfileWriterBinary::writeHeader(

std::error_code SampleProfileWriterBinary::writeBody(StringRef FName,
const FunctionSamples &S) {
auto &OS = *OutputStream;

if (std::error_code EC = writeNameIdx(FName))
return EC;

Expand Down Expand Up @@ -176,11 +182,11 @@ std::error_code SampleProfileWriterBinary::writeBody(StringRef FName,
/// \returns true if the samples were written successfully, false otherwise.
std::error_code SampleProfileWriterBinary::write(StringRef FName,
const FunctionSamples &S) {
encodeULEB128(S.getHeadSamples(), OS);
encodeULEB128(S.getHeadSamples(), *OutputStream);
return writeBody(FName, S);
}

/// \brief Create a sample profile writer based on the specified format.
/// \brief Create a sample profile file writer based on the specified format.
///
/// \param Filename The file to create.
///
Expand All @@ -192,12 +198,36 @@ std::error_code SampleProfileWriterBinary::write(StringRef FName,
ErrorOr<std::unique_ptr<SampleProfileWriter>>
SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) {
std::error_code EC;
std::unique_ptr<raw_ostream> OS;
if (Format == SPF_Binary)
OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::F_None));
else
OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::F_Text));
if (EC)
return EC;

return create(OS, Format);
}

/// \brief Create a sample profile stream writer based on the specified format.
///
/// \param OS The output stream to store the profile data to.
///
/// \param Writer The writer to instantiate according to the specified format.
///
/// \param Format Encoding format for the profile file.
///
/// \returns an error code indicating the status of the created writer.
ErrorOr<std::unique_ptr<SampleProfileWriter>>
SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
SampleProfileFormat Format) {
std::error_code EC;
std::unique_ptr<SampleProfileWriter> Writer;

if (Format == SPF_Binary)
Writer.reset(new SampleProfileWriterBinary(Filename, EC));
Writer.reset(new SampleProfileWriterBinary(OS));
else if (Format == SPF_Text)
Writer.reset(new SampleProfileWriterText(Filename, EC));
Writer.reset(new SampleProfileWriterText(OS));
else if (Format == SPF_GCC)
EC = sampleprof_error::unsupported_writing_format;
else
Expand Down
1 change: 1 addition & 0 deletions unittests/ProfileData/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ set(LLVM_LINK_COMPONENTS
add_llvm_unittest(ProfileDataTests
CoverageMappingTest.cpp
InstrProfTest.cpp
SampleProfTest.cpp
)
102 changes: 102 additions & 0 deletions unittests/ProfileData/SampleProfTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//===- unittest/ProfileData/SampleProfTest.cpp -------------------*- C++
//-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/ProfileData/SampleProfReader.h"
#include "llvm/ProfileData/SampleProfWriter.h"
#include "gtest/gtest.h"

#include <cstdarg>

using namespace llvm;
using namespace sampleprof;

static ::testing::AssertionResult NoError(std::error_code EC) {
if (!EC)
return ::testing::AssertionSuccess();
return ::testing::AssertionFailure() << "error " << EC.value() << ": "
<< EC.message();
}

namespace {

struct SampleProfTest : ::testing::Test {
std::string Data;
std::unique_ptr<raw_ostream> OS;
std::unique_ptr<SampleProfileWriter> Writer;
std::unique_ptr<SampleProfileReader> Reader;

SampleProfTest()
: Data(), OS(new raw_string_ostream(Data)), Writer(), Reader() {}

void createWriter(SampleProfileFormat Format) {
auto WriterOrErr = SampleProfileWriter::create(OS, Format);
ASSERT_TRUE(NoError(WriterOrErr.getError()));
Writer = std::move(WriterOrErr.get());
}

void readProfile(std::unique_ptr<MemoryBuffer> &Profile) {
auto ReaderOrErr = SampleProfileReader::create(Profile, getGlobalContext());
ASSERT_TRUE(NoError(ReaderOrErr.getError()));
Reader = std::move(ReaderOrErr.get());
}

void testRoundTrip(SampleProfileFormat Format) {
createWriter(Format);

StringRef FooName("_Z3fooi");
FunctionSamples FooSamples;
FooSamples.addTotalSamples(7711);
FooSamples.addHeadSamples(610);
FooSamples.addBodySamples(1, 0, 610);

StringRef BarName("_Z3bari");
FunctionSamples BarSamples;
BarSamples.addTotalSamples(20301);
BarSamples.addHeadSamples(1437);
BarSamples.addBodySamples(1, 0, 1437);

StringMap<FunctionSamples> Profiles;
Profiles[FooName] = std::move(FooSamples);
Profiles[BarName] = std::move(BarSamples);

std::error_code EC;
EC = Writer->write(Profiles);
ASSERT_TRUE(NoError(EC));

Writer->getOutputStream().flush();

auto Profile = MemoryBuffer::getMemBufferCopy(Data);
readProfile(Profile);

EC = Reader->read();
ASSERT_TRUE(NoError(EC));

StringMap<FunctionSamples> &ReadProfiles = Reader->getProfiles();
ASSERT_EQ(2u, ReadProfiles.size());

FunctionSamples &ReadFooSamples = ReadProfiles[FooName];
ASSERT_EQ(7711u, ReadFooSamples.getTotalSamples());
ASSERT_EQ(610u, ReadFooSamples.getHeadSamples());

FunctionSamples &ReadBarSamples = ReadProfiles[BarName];
ASSERT_EQ(20301u, ReadBarSamples.getTotalSamples());
ASSERT_EQ(1437u, ReadBarSamples.getHeadSamples());
}
};

TEST_F(SampleProfTest, roundtrip_text_profile) {
testRoundTrip(SampleProfileFormat::SPF_Text);
}

TEST_F(SampleProfTest, roundtrip_binary_profile) {
testRoundTrip(SampleProfileFormat::SPF_Binary);
}

} // end anonymous namespace

0 comments on commit 46cf0f0

Please sign in to comment.