Index: include/llvm/ProfileData/SampleProfReader.h =================================================================== --- include/llvm/ProfileData/SampleProfReader.h +++ include/llvm/ProfileData/SampleProfReader.h @@ -267,6 +267,10 @@ static ErrorOr> create(StringRef Filename, LLVMContext &C); + /// \brief Create a sample profile reader from the supplied memory buffer. + static ErrorOr> + create(std::unique_ptr &B, LLVMContext &C); + protected: /// \brief Map every function to its associated profile. /// Index: include/llvm/ProfileData/SampleProfWriter.h =================================================================== --- include/llvm/ProfileData/SampleProfWriter.h +++ include/llvm/ProfileData/SampleProfWriter.h @@ -29,9 +29,8 @@ /// \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) {} + SampleProfileWriter(raw_ostream &OutputStream) + : OS(OutputStream) {} virtual ~SampleProfileWriter() {} /// Write sample profiles in \p S for function \p FName. @@ -59,7 +58,7 @@ /// /// Create a new writer based on the value of \p Format. static ErrorOr> - create(StringRef Filename, SampleProfileFormat Format); + create(raw_ostream &OutputStream, SampleProfileFormat Format); protected: /// \brief Write a file header for the profile file. @@ -67,14 +66,14 @@ writeHeader(const StringMap &ProfileMap) = 0; /// \brief Output stream where to emit the profile to. - raw_fd_ostream OS; + raw_ostream &OS; }; /// \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) {} + SampleProfileWriterText(raw_ostream &OutputStream) + : SampleProfileWriter(OutputStream), Indent(0) {} std::error_code write(StringRef FName, const FunctionSamples &S) override; @@ -94,8 +93,8 @@ /// \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() {} + SampleProfileWriterBinary(raw_ostream &OutputStream) + : SampleProfileWriter(OutputStream), NameTable() {} std::error_code write(StringRef F, const FunctionSamples &S) override; Index: lib/ProfileData/SampleProfReader.cpp =================================================================== --- lib/ProfileData/SampleProfReader.cpp +++ lib/ProfileData/SampleProfReader.cpp @@ -693,15 +693,27 @@ 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> +SampleProfileReader::create(std::unique_ptr &B, LLVMContext &C) { std::unique_ptr 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; Index: lib/ProfileData/SampleProfWriter.cpp =================================================================== --- lib/ProfileData/SampleProfWriter.cpp +++ lib/ProfileData/SampleProfWriter.cpp @@ -182,7 +182,7 @@ /// \brief Create a sample profile writer based on the specified format. /// -/// \param Filename The file to create. +/// \param OS The output stream to store the profile data to. /// /// \param Writer The writer to instantiate according to the specified format. /// @@ -190,14 +190,15 @@ /// /// \returns an error code indicating the status of the created writer. ErrorOr> -SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) { +SampleProfileWriter::create(raw_ostream &OS, + SampleProfileFormat Format) { std::error_code EC; std::unique_ptr 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 Index: tools/llvm-profdata/llvm-profdata.cpp =================================================================== --- tools/llvm-profdata/llvm-profdata.cpp +++ tools/llvm-profdata/llvm-profdata.cpp @@ -139,8 +139,18 @@ StringRef OutputFilename, ProfileFormat OutputFormat) { using namespace sampleprof; + + sys::fs::OpenFlags OutputFlags = OutputFormat == ProfileFormat::PF_Binary + ? sys::fs::F_None + : sys::fs::F_Text; + std::error_code EC; + std::unique_ptr OutputStream( + new raw_fd_ostream(OutputFilename, EC, OutputFlags)); + if (EC) + exitWithErrorCode(EC, OutputFilename); + auto WriterOrErr = - SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); + SampleProfileWriter::create(*OutputStream, FormatMap[OutputFormat]); if (std::error_code EC = WriterOrErr.getError()) exitWithErrorCode(EC, OutputFilename); Index: unittests/ProfileData/CMakeLists.txt =================================================================== --- unittests/ProfileData/CMakeLists.txt +++ unittests/ProfileData/CMakeLists.txt @@ -7,4 +7,5 @@ add_llvm_unittest(ProfileDataTests CoverageMappingTest.cpp InstrProfTest.cpp + SampleProfTest.cpp ) Index: unittests/ProfileData/SampleProfTest.cpp =================================================================== --- /dev/null +++ unittests/ProfileData/SampleProfTest.cpp @@ -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 + +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; + raw_string_ostream OS; + std::unique_ptr Writer; + std::unique_ptr Reader; + + SampleProfTest() + : Data(), OS(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 &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 Profiles; + Profiles[FooName] = std::move(FooSamples); + Profiles[BarName] = std::move(BarSamples); + + std::error_code EC; + EC = Writer->write(Profiles); + ASSERT_TRUE(NoError(EC)); + + OS.flush(); + + auto Profile = MemoryBuffer::getMemBufferCopy(Data); + readProfile(Profile); + + EC = Reader->read(); + ASSERT_TRUE(NoError(EC)); + + StringMap &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