From 77ae39453c58a2dada74d0839c2a03fe65e30591 Mon Sep 17 00:00:00 2001 From: Justin Bogner Date: Mon, 16 Feb 2015 23:27:48 +0000 Subject: [PATCH] InstrProf: Add unit tests for the profile reader and writer This required some minor API to be added to these types to avoid needing temp files. Also, I've used initializer lists in the tests, as MSVC 2013 claims to support them. I'll redo this without them if the bots complain. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@229455 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/ProfileData/InstrProfReader.h | 6 ++ include/llvm/ProfileData/InstrProfWriter.h | 6 +- lib/ProfileData/InstrProfReader.cpp | 29 ++++--- lib/ProfileData/InstrProfWriter.cpp | 30 +++++++- unittests/ProfileData/CMakeLists.txt | 1 + unittests/ProfileData/InstrProfTest.cpp | 88 ++++++++++++++++++++++ 6 files changed, 145 insertions(+), 15 deletions(-) create mode 100644 unittests/ProfileData/InstrProfTest.cpp diff --git a/include/llvm/ProfileData/InstrProfReader.h b/include/llvm/ProfileData/InstrProfReader.h index 5dd1d4f9dd7..63a6ac671f2 100644 --- a/include/llvm/ProfileData/InstrProfReader.h +++ b/include/llvm/ProfileData/InstrProfReader.h @@ -95,6 +95,9 @@ public: /// Factory method to create an appropriately typed reader for the given /// instrprof file. static ErrorOr> create(std::string Path); + + static ErrorOr> + create(std::unique_ptr Buffer); }; /// Reader for the simple text based instrprof format. @@ -294,6 +297,9 @@ public: /// Factory method to create an indexed reader. static ErrorOr> create(std::string Path); + + static ErrorOr> + create(std::unique_ptr Buffer); }; } // end namespace llvm diff --git a/include/llvm/ProfileData/InstrProfWriter.h b/include/llvm/ProfileData/InstrProfWriter.h index a23c56772a2..48836e15124 100644 --- a/include/llvm/ProfileData/InstrProfWriter.h +++ b/include/llvm/ProfileData/InstrProfWriter.h @@ -41,8 +41,12 @@ public: std::error_code addFunctionCounts(StringRef FunctionName, uint64_t FunctionHash, ArrayRef Counters); - /// Ensure that all data is written to disk. + /// Write the profile to \c OS void write(raw_fd_ostream &OS); + /// Write the profile, returning the raw data. For testing. + std::string writeString(); +private: + std::pair writeImpl(raw_ostream &OS); }; } // end namespace llvm diff --git a/lib/ProfileData/InstrProfReader.cpp b/lib/ProfileData/InstrProfReader.cpp index d13f27c3113..01e199dcf0e 100644 --- a/lib/ProfileData/InstrProfReader.cpp +++ b/lib/ProfileData/InstrProfReader.cpp @@ -25,12 +25,7 @@ setupMemoryBuffer(std::string Path) { MemoryBuffer::getFileOrSTDIN(Path); if (std::error_code EC = BufferOrErr.getError()) return EC; - auto Buffer = std::move(BufferOrErr.get()); - - // Sanity check the file. - if (Buffer->getBufferSize() > std::numeric_limits::max()) - return instrprof_error::too_large; - return std::move(Buffer); + return std::move(BufferOrErr.get()); } static std::error_code initializeReader(InstrProfReader &Reader) { @@ -43,10 +38,16 @@ InstrProfReader::create(std::string Path) { auto BufferOrError = setupMemoryBuffer(Path); if (std::error_code EC = BufferOrError.getError()) return EC; + return InstrProfReader::create(std::move(BufferOrError.get())); +} - auto Buffer = std::move(BufferOrError.get()); - std::unique_ptr Result; +ErrorOr> +InstrProfReader::create(std::unique_ptr Buffer) { + // Sanity check the buffer. + if (Buffer->getBufferSize() > std::numeric_limits::max()) + return instrprof_error::too_large; + std::unique_ptr Result; // Create the reader. if (IndexedInstrProfReader::hasFormat(*Buffer)) Result.reset(new IndexedInstrProfReader(std::move(Buffer))); @@ -70,14 +71,20 @@ IndexedInstrProfReader::create(std::string Path) { auto BufferOrError = setupMemoryBuffer(Path); if (std::error_code EC = BufferOrError.getError()) return EC; + return IndexedInstrProfReader::create(std::move(BufferOrError.get())); +} + - auto Buffer = std::move(BufferOrError.get()); - std::unique_ptr Result; +ErrorOr> +IndexedInstrProfReader::create(std::unique_ptr Buffer) { + // Sanity check the buffer. + if (Buffer->getBufferSize() > std::numeric_limits::max()) + return instrprof_error::too_large; // Create the reader. if (!IndexedInstrProfReader::hasFormat(*Buffer)) return instrprof_error::bad_magic; - Result.reset(new IndexedInstrProfReader(std::move(Buffer))); + auto Result = llvm::make_unique(std::move(Buffer)); // Initialize the reader and return the result. if (std::error_code EC = initializeReader(*Result)) diff --git a/lib/ProfileData/InstrProfWriter.cpp b/lib/ProfileData/InstrProfWriter.cpp index d4cde2e195d..2c72efe3faa 100644 --- a/lib/ProfileData/InstrProfWriter.cpp +++ b/lib/ProfileData/InstrProfWriter.cpp @@ -106,7 +106,7 @@ InstrProfWriter::addFunctionCounts(StringRef FunctionName, return instrprof_error::success; } -void InstrProfWriter::write(raw_fd_ostream &OS) { +std::pair InstrProfWriter::writeImpl(raw_ostream &OS) { OnDiskChainedHashTableGenerator Generator; // Populate the hash table generator. @@ -128,7 +128,31 @@ void InstrProfWriter::write(raw_fd_ostream &OS) { // Write the hash table. uint64_t HashTableStart = Generator.Emit(OS); + return std::make_pair(HashTableStartLoc, HashTableStart); +} + +void InstrProfWriter::write(raw_fd_ostream &OS) { + // Write the hash table. + auto TableStart = writeImpl(OS); + // Go back and fill in the hash table start. - OS.seek(HashTableStartLoc); - LE.write(HashTableStart); + using namespace support; + OS.seek(TableStart.first); + endian::Writer(OS).write(TableStart.second); +} + +std::string InstrProfWriter::writeString() { + std::string Result; + llvm::raw_string_ostream OS(Result); + // Write the hash table. + auto TableStart = writeImpl(OS); + OS.flush(); + + // Go back and fill in the hash table start. + using namespace support; + uint64_t Bytes = endian::byte_swap(TableStart.second); + Result.replace(TableStart.first, sizeof(uint64_t), (const char *)&Bytes, + sizeof(uint64_t)); + + return Result; } diff --git a/unittests/ProfileData/CMakeLists.txt b/unittests/ProfileData/CMakeLists.txt index 3251ff41502..79137c9510a 100644 --- a/unittests/ProfileData/CMakeLists.txt +++ b/unittests/ProfileData/CMakeLists.txt @@ -6,4 +6,5 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(ProfileDataTests CoverageMappingTest.cpp + InstrProfTest.cpp ) diff --git a/unittests/ProfileData/InstrProfTest.cpp b/unittests/ProfileData/InstrProfTest.cpp new file mode 100644 index 00000000000..f6fd790dccb --- /dev/null +++ b/unittests/ProfileData/InstrProfTest.cpp @@ -0,0 +1,88 @@ +//===- unittest/ProfileData/InstrProfTest.cpp -------------------------------=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ProfileData/InstrProfReader.h" +#include "llvm/ProfileData/InstrProfWriter.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +TEST(InstrProfTest, write_and_read_empty_profile) { + InstrProfWriter Writer; + std::string Profile = Writer.writeString(); + auto ReaderOrErr = + IndexedInstrProfReader::create(MemoryBuffer::getMemBuffer(Profile)); + ASSERT_EQ(std::error_code(), ReaderOrErr.getError()); + auto Reader = std::move(ReaderOrErr.get()); + ASSERT_TRUE(Reader->begin() == Reader->end()); +} + +TEST(InstrProfTest, write_and_read_one_function) { + InstrProfWriter Writer; + Writer.addFunctionCounts("foo", 0x1234, {1, 2, 3, 4}); + std::string Profile = Writer.writeString(); + auto ReaderOrErr = + IndexedInstrProfReader::create(MemoryBuffer::getMemBuffer(Profile)); + ASSERT_EQ(std::error_code(), ReaderOrErr.getError()); + auto Reader = std::move(ReaderOrErr.get()); + + auto I = Reader->begin(), E = Reader->end(); + ASSERT_TRUE(I != E); + ASSERT_EQ(StringRef("foo"), I->Name); + ASSERT_EQ(0x1234U, I->Hash); + ASSERT_EQ(4U, I->Counts.size()); + ASSERT_EQ(1U, I->Counts[0]); + ASSERT_EQ(2U, I->Counts[1]); + ASSERT_EQ(3U, I->Counts[2]); + ASSERT_EQ(4U, I->Counts[3]); + ASSERT_TRUE(++I == E); +} + +TEST(InstrProfTest, get_function_counts) { + InstrProfWriter Writer; + Writer.addFunctionCounts("foo", 0x1234, {1, 2}); + std::string Profile = Writer.writeString(); + auto ReaderOrErr = + IndexedInstrProfReader::create(MemoryBuffer::getMemBuffer(Profile)); + ASSERT_EQ(std::error_code(), ReaderOrErr.getError()); + auto Reader = std::move(ReaderOrErr.get()); + + std::vector Counts; + std::error_code EC; + + EC = Reader->getFunctionCounts("foo", 0x1234, Counts); + ASSERT_EQ(instrprof_error::success, EC); + ASSERT_EQ(2U, Counts.size()); + ASSERT_EQ(1U, Counts[0]); + ASSERT_EQ(2U, Counts[1]); + + EC = Reader->getFunctionCounts("foo", 0x5678, Counts); + ASSERT_EQ(instrprof_error::hash_mismatch, EC); + + EC = Reader->getFunctionCounts("bar", 0x1234, Counts); + ASSERT_EQ(instrprof_error::unknown_function, EC); +} + +TEST(InstrProfTest, get_max_function_count) { + InstrProfWriter Writer; + Writer.addFunctionCounts("foo", 0x1234, {1ULL << 31, 2}); + Writer.addFunctionCounts("bar", 0, {1ULL << 63}); + Writer.addFunctionCounts("baz", 0x5678, {0, 0, 0, 0}); + std::string Profile = Writer.writeString(); + auto ReaderOrErr = + IndexedInstrProfReader::create(MemoryBuffer::getMemBuffer(Profile)); + ASSERT_EQ(std::error_code(), ReaderOrErr.getError()); + auto Reader = std::move(ReaderOrErr.get()); + + ASSERT_EQ(1ULL << 63, Reader->getMaximumFunctionCount()); +} + +} // end anonymous namespace -- 2.34.1