InstrProf: Add unit tests for the profile reader and writer
authorJustin Bogner <mail@justinbogner.com>
Mon, 16 Feb 2015 23:27:48 +0000 (23:27 +0000)
committerJustin Bogner <mail@justinbogner.com>
Mon, 16 Feb 2015 23:27:48 +0000 (23:27 +0000)
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
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 5dd1d4f9dd748af0a0c3b61f6b9497da3d388fc6..63a6ac671f2bd3609df73eb17cb20a995043a8e1 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 a23c56772a2f14e1dd2edbc2de958a7008fad065..48836e151245ba5cdc0a223fa05088274be2411e 100644 (file)
@@ -41,8 +41,12 @@ 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::string writeString();
+private:
+  std::pair<uint64_t, uint64_t> writeImpl(raw_ostream &OS);
 };
 
 } // end namespace llvm
index d13f27c3113e1951082f8a2b21b2308c8f9d70a9..01e199dcf0ec3b80f6393f72face18bb6c25d0b2 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 d4cde2e195de24ccfd6d6b15aeb9fecb8f8a1ec8..2c72efe3faa92fcd79667e4fef38afdb449f8b07 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,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<uint64_t>(HashTableStart);
+  using namespace support;
+  OS.seek(TableStart.first);
+  endian::Writer<little>(OS).write<uint64_t>(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<uint64_t, little>(TableStart.second);
+  Result.replace(TableStart.first, sizeof(uint64_t), (const char *)&Bytes,
+                 sizeof(uint64_t));
+
+  return Result;
 }
index 3251ff415022119858e26d1cac24942ff5d027a0..79137c9510aeeabb6a728fb0dffa1113f81bbb6f 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..f6fd790
--- /dev/null
@@ -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<uint64_t> 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