Re-apply "InstrProf: Add unit tests for the profile reader and writer"
authorJustin Bogner <mail@justinbogner.com>
Wed, 18 Feb 2015 01:58:17 +0000 (01:58 +0000)
committerJustin Bogner <mail@justinbogner.com>
Wed, 18 Feb 2015 01:58:17 +0000 (01:58 +0000)
Have the InstrProfWriter return a MemoryBuffer instead of a
std::string. This fixes the alignment issues the reader would hit, and
it's a more appropriate type for this anyway.

I've also removed an ugly helper function that's not needed since
we're allowing initializer lists now, and updated some error code
checks based on MSVC's issues with r229473.

This reverts r229483, reapplying r229478.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@229602 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/ProfileData/InstrProfReader.h
include/llvm/ProfileData/InstrProfWriter.h
lib/ProfileData/InstrProfReader.cpp
lib/ProfileData/InstrProfWriter.cpp
unittests/ProfileData/CMakeLists.txt
unittests/ProfileData/InstrProfTest.cpp [new file with mode: 0644]

index 5dd1d4f..63a6ac6 100644 (file)
@@ -95,6 +95,9 @@ public:
   /// Factory method to create an appropriately typed reader for the given
   /// instrprof file.
   static ErrorOr<std::unique_ptr<InstrProfReader>> create(std::string Path);
+
+  static ErrorOr<std::unique_ptr<InstrProfReader>>
+  create(std::unique_ptr<MemoryBuffer> Buffer);
 };
 
 /// Reader for the simple text based instrprof format.
@@ -294,6 +297,9 @@ public:
   /// Factory method to create an indexed reader.
   static ErrorOr<std::unique_ptr<IndexedInstrProfReader>>
   create(std::string Path);
+
+  static ErrorOr<std::unique_ptr<IndexedInstrProfReader>>
+  create(std::unique_ptr<MemoryBuffer> Buffer);
 };
 
 } // end namespace llvm
index a23c567..ce0bb52 100644 (file)
@@ -20,6 +20,7 @@
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ProfileData/InstrProf.h"
 #include "llvm/Support/DataTypes.h"
+#include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/raw_ostream.h"
 #include <vector>
 
@@ -41,8 +42,13 @@ public:
   std::error_code addFunctionCounts(StringRef FunctionName,
                                     uint64_t FunctionHash,
                                     ArrayRef<uint64_t> 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::unique_ptr<MemoryBuffer> writeBuffer();
+
+private:
+  std::pair<uint64_t, uint64_t> writeImpl(raw_ostream &OS);
 };
 
 } // end namespace llvm
index d13f27c..01e199d 100644 (file)
@@ -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<unsigned>::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<InstrProfReader> Result;
+ErrorOr<std::unique_ptr<InstrProfReader>>
+InstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer) {
+  // Sanity check the buffer.
+  if (Buffer->getBufferSize() > std::numeric_limits<unsigned>::max())
+    return instrprof_error::too_large;
 
+  std::unique_ptr<InstrProfReader> 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<IndexedInstrProfReader> Result;
+ErrorOr<std::unique_ptr<IndexedInstrProfReader>>
+IndexedInstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer) {
+  // Sanity check the buffer.
+  if (Buffer->getBufferSize() > std::numeric_limits<unsigned>::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<IndexedInstrProfReader>(std::move(Buffer));
 
   // Initialize the reader and return the result.
   if (std::error_code EC = initializeReader(*Result))
index d4cde2e..2188543 100644 (file)
@@ -106,7 +106,7 @@ InstrProfWriter::addFunctionCounts(StringRef FunctionName,
   return instrprof_error::success;
 }
 
-void InstrProfWriter::write(raw_fd_ostream &OS) {
+std::pair<uint64_t, uint64_t> InstrProfWriter::writeImpl(raw_ostream &OS) {
   OnDiskChainedHashTableGenerator<InstrProfRecordTrait> Generator;
 
   // Populate the hash table generator.
@@ -128,7 +128,32 @@ 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<uint64_t>(HashTableStart);
+  using namespace support;
+  OS.seek(TableStart.first);
+  endian::Writer<little>(OS).write<uint64_t>(TableStart.second);
+}
+
+std::unique_ptr<MemoryBuffer> InstrProfWriter::writeBuffer() {
+  std::string Data;
+  llvm::raw_string_ostream OS(Data);
+  // 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<uint64_t, little>(TableStart.second);
+  Data.replace(TableStart.first, sizeof(uint64_t), (const char *)&Bytes,
+               sizeof(uint64_t));
+
+  // Return this in an aligned memory buffer.
+  return MemoryBuffer::getMemBufferCopy(Data);
 }
index 3251ff4..79137c9 100644 (file)
@@ -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 (file)
index 0000000..26ea0e4
--- /dev/null
@@ -0,0 +1,98 @@
+//===- 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"
+
+#include <cstdarg>
+
+using namespace llvm;
+
+static ::testing::AssertionResult NoError(std::error_code EC) {
+  if (!EC)
+    return ::testing::AssertionSuccess();
+  return ::testing::AssertionFailure() << "error " << EC.value()
+                                       << ": " << EC.message();
+}
+
+static ::testing::AssertionResult ErrorEquals(std::error_code Expected,
+                                              std::error_code Found) {
+  if (Expected == Found)
+    return ::testing::AssertionSuccess();
+  return ::testing::AssertionFailure() << "error " << Found.value()
+                                       << ": " << Found.message();
+}
+
+namespace {
+
+struct InstrProfTest : ::testing::Test {
+  InstrProfWriter Writer;
+  std::unique_ptr<IndexedInstrProfReader> Reader;
+
+  void readProfile(std::unique_ptr<MemoryBuffer> Profile) {
+    auto ReaderOrErr = IndexedInstrProfReader::create(std::move(Profile));
+    ASSERT_TRUE(NoError(ReaderOrErr.getError()));
+    Reader = std::move(ReaderOrErr.get());
+  }
+};
+
+TEST_F(InstrProfTest, write_and_read_empty_profile) {
+  auto Profile = Writer.writeBuffer();
+  readProfile(std::move(Profile));
+  ASSERT_TRUE(Reader->begin() == Reader->end());
+}
+
+TEST_F(InstrProfTest, write_and_read_one_function) {
+  Writer.addFunctionCounts("foo", 0x1234, {1, 2, 3, 4});
+  auto Profile = Writer.writeBuffer();
+  readProfile(std::move(Profile));
+
+  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_F(InstrProfTest, get_function_counts) {
+  Writer.addFunctionCounts("foo", 0x1234, {1, 2});
+  auto Profile = Writer.writeBuffer();
+  readProfile(std::move(Profile));
+
+  std::vector<uint64_t> Counts;
+  ASSERT_TRUE(NoError(Reader->getFunctionCounts("foo", 0x1234, Counts)));
+  ASSERT_EQ(2U, Counts.size());
+  ASSERT_EQ(1U, Counts[0]);
+  ASSERT_EQ(2U, Counts[1]);
+
+  std::error_code EC;
+  EC = Reader->getFunctionCounts("foo", 0x5678, Counts);
+  ASSERT_TRUE(ErrorEquals(instrprof_error::hash_mismatch, EC));
+
+  EC = Reader->getFunctionCounts("bar", 0x1234, Counts);
+  ASSERT_TRUE(ErrorEquals(instrprof_error::unknown_function, EC));
+}
+
+TEST_F(InstrProfTest, get_max_function_count) {
+  Writer.addFunctionCounts("foo", 0x1234, {1ULL << 31, 2});
+  Writer.addFunctionCounts("bar", 0, {1ULL << 63});
+  Writer.addFunctionCounts("baz", 0x5678, {0, 0, 0, 0});
+  auto Profile = Writer.writeBuffer();
+  readProfile(std::move(Profile));
+
+  ASSERT_EQ(1ULL << 63, Reader->getMaximumFunctionCount());
+}
+
+} // end anonymous namespace