ProfileData: Introduce the InstrProfReader interface and a text reader
authorJustin Bogner <mail@justinbogner.com>
Fri, 21 Mar 2014 17:24:48 +0000 (17:24 +0000)
committerJustin Bogner <mail@justinbogner.com>
Fri, 21 Mar 2014 17:24:48 +0000 (17:24 +0000)
This introduces the ProfileData library and updates llvm-profdata to
use this library for reading profiles. InstrProfReader is an abstract
base class that will be subclassed for both the raw instrprof data
from compiler-rt and the efficient instrprof format that will be used
for PGO.

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

15 files changed:
include/llvm/ProfileData/InstrProf.h [new file with mode: 0644]
include/llvm/ProfileData/InstrProfReader.h [new file with mode: 0644]
lib/CMakeLists.txt
lib/LLVMBuild.txt
lib/Makefile
lib/ProfileData/CMakeLists.txt [new file with mode: 0644]
lib/ProfileData/InstrProf.cpp [new file with mode: 0644]
lib/ProfileData/InstrProfReader.cpp [new file with mode: 0644]
lib/ProfileData/LLVMBuild.txt [new file with mode: 0644]
lib/ProfileData/Makefile [new file with mode: 0644]
test/tools/llvm-profdata/errors.test
tools/llvm-profdata/CMakeLists.txt
tools/llvm-profdata/LLVMBuild.txt
tools/llvm-profdata/Makefile
tools/llvm-profdata/llvm-profdata.cpp

diff --git a/include/llvm/ProfileData/InstrProf.h b/include/llvm/ProfileData/InstrProf.h
new file mode 100644 (file)
index 0000000..21cc170
--- /dev/null
@@ -0,0 +1,52 @@
+//=-- InstrProf.h - Instrumented profiling format support ---------*- C++ -*-=//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Instrumentation-based profiling data is generated by instrumented
+// binaries through library functions in compiler-rt, and read by the clang
+// frontend to feed PGO.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_PROFILEDATA_INSTRPROF_H__
+#define LLVM_PROFILEDATA_INSTRPROF_H__
+
+#include "llvm/Support/system_error.h"
+
+namespace llvm {
+
+const error_category &instrprof_category();
+
+struct instrprof_error {
+  enum ErrorType {
+    success = 0,
+    eof,
+    bad_magic,
+    unsupported_version,
+    too_large,
+    truncated,
+    malformed,
+    unknown_function
+  };
+  ErrorType V;
+
+  instrprof_error(ErrorType V) : V(V) {}
+  operator ErrorType() const { return V; }
+};
+
+inline error_code make_error_code(instrprof_error E) {
+  return error_code(static_cast<int>(E), instrprof_category());
+}
+
+template <> struct is_error_code_enum<instrprof_error> : std::true_type {};
+template <> struct is_error_code_enum<instrprof_error::ErrorType>
+  : std::true_type {};
+
+} // end namespace llvm
+
+#endif // LLVM_PROFILEDATA_INSTRPROF_H__
diff --git a/include/llvm/ProfileData/InstrProfReader.h b/include/llvm/ProfileData/InstrProfReader.h
new file mode 100644 (file)
index 0000000..a45318b
--- /dev/null
@@ -0,0 +1,119 @@
+//=-- InstrProfReader.h - Instrumented profiling readers ----------*- C++ -*-=//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains support for reading profiling data for instrumentation
+// based PGO and coverage.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_PROFILEDATA_INSTRPROF_READER_H__
+#define LLVM_PROFILEDATA_INSTRPROF_READER_H__
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ProfileData/InstrProf.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+#include <iterator>
+
+namespace llvm {
+
+class InstrProfReader;
+
+/// Profiling information for a single function.
+struct InstrProfRecord {
+  StringRef Name;
+  uint64_t Hash;
+  ArrayRef<uint64_t> Counts;
+};
+
+/// A file format agnostic iterator over profiling data.
+class InstrProfIterator : public std::iterator<std::input_iterator_tag,
+                                               InstrProfRecord> {
+  InstrProfReader *Reader;
+  InstrProfRecord Record;
+
+  void Increment();
+public:
+  InstrProfIterator() : Reader(nullptr) {}
+  InstrProfIterator(InstrProfReader *Reader) : Reader(Reader) { Increment(); }
+
+  InstrProfIterator &operator++() { Increment(); return *this; }
+  bool operator==(const InstrProfIterator &RHS) { return Reader == RHS.Reader; }
+  bool operator!=(const InstrProfIterator &RHS) { return Reader != RHS.Reader; }
+  InstrProfRecord &operator*() { return Record; }
+  InstrProfRecord *operator->() { return &Record; }
+};
+
+/// Base class and interface for reading profiling data of any known instrprof
+/// format. Provides an iterator over InstrProfRecords.
+class InstrProfReader {
+  error_code LastError;
+public:
+  InstrProfReader() : LastError(instrprof_error::success) {}
+  virtual ~InstrProfReader() {}
+
+  /// Read a single record.
+  virtual error_code readNextRecord(InstrProfRecord &Record) = 0;
+  /// Iterator over profile data.
+  InstrProfIterator begin() { return InstrProfIterator(this); }
+  InstrProfIterator end() { return InstrProfIterator(); }
+
+  /// Set the current error_code and return same.
+  error_code error(error_code EC) {
+    LastError = EC;
+    return EC;
+  }
+  /// Clear the current error code and return a successful one.
+  error_code success() { return error(instrprof_error::success); }
+
+  /// Return true if the reader has finished reading the profile data.
+  bool isEOF() { return LastError == instrprof_error::eof; }
+  /// Return true if the reader encountered an error reading profiling data.
+  bool hasError() { return LastError && !isEOF(); }
+  /// Get the current error code.
+  error_code getError() { return LastError; }
+
+  /// Factory method to create an appropriately typed reader for the given
+  /// instrprof file.
+  static error_code create(std::string Path,
+                           std::unique_ptr<InstrProfReader> &Result);
+};
+
+/// Reader for the simple text based instrprof format.
+///
+/// This format is a simple text format that's suitable for test data. Records
+/// are separated by one or more blank lines, and record fields are separated by
+/// new lines.
+///
+/// Each record consists of a function name, a function hash, a number of
+/// counters, and then each counter value, in that order.
+class TextInstrProfReader : public InstrProfReader {
+private:
+  /// The profile data file contents.
+  std::unique_ptr<MemoryBuffer> DataBuffer;
+  /// Iterator over the profile data.
+  line_iterator Line;
+  /// The current set of counter values.
+  std::vector<uint64_t> Counts;
+
+  TextInstrProfReader(const TextInstrProfReader &) LLVM_DELETED_FUNCTION;
+  TextInstrProfReader &operator=(const TextInstrProfReader &)
+    LLVM_DELETED_FUNCTION;
+public:
+  TextInstrProfReader(std::unique_ptr<MemoryBuffer> &DataBuffer_)
+      : DataBuffer(DataBuffer_.release()), Line(*DataBuffer, '#') {}
+
+  /// Read a single record.
+  error_code readNextRecord(InstrProfRecord &Record) override;
+};
+
+} // end namespace llvm
+
+#endif // LLVM_PROFILEDATA_INSTRPROF_READER_H__
index 9367f553134208550f1a80153decdf4b83bf9d55..fab1c8747b9fbd59cc6dcb7eec4b8fff8186b7ba 100644 (file)
@@ -16,3 +16,4 @@ add_subdirectory(ExecutionEngine)
 add_subdirectory(Target)
 add_subdirectory(AsmParser)
 add_subdirectory(LineEditor)
 add_subdirectory(Target)
 add_subdirectory(AsmParser)
 add_subdirectory(LineEditor)
+add_subdirectory(ProfileData)
index a0984d410c699d7fb3092746d4d233c557cfbd90..ad5b22b914eb61a55b7c3b095009a5070baef73c 100644 (file)
@@ -16,7 +16,7 @@
 ;===------------------------------------------------------------------------===;
 
 [common]
 ;===------------------------------------------------------------------------===;
 
 [common]
-subdirectories = Analysis AsmParser Bitcode CodeGen DebugInfo ExecutionEngine LineEditor Linker IR IRReader LTO MC Object Option Support TableGen Target Transforms
+subdirectories = Analysis AsmParser Bitcode CodeGen DebugInfo ExecutionEngine LineEditor Linker IR IRReader LTO MC Object Option ProfileData Support TableGen Target Transforms
 
 [component_0]
 type = Group
 
 [component_0]
 type = Group
index a97f71aded08062e1e247e03e06d26e6aff83304..0ddf917599f1d0e800d203ca365698a3cd7943f9 100644 (file)
@@ -12,6 +12,6 @@ include $(LEVEL)/Makefile.config
 
 PARALLEL_DIRS := IR AsmParser Bitcode Analysis Transforms CodeGen Target \
                  ExecutionEngine Linker LTO MC Object Option DebugInfo   \
 
 PARALLEL_DIRS := IR AsmParser Bitcode Analysis Transforms CodeGen Target \
                  ExecutionEngine Linker LTO MC Object Option DebugInfo   \
-                 IRReader LineEditor
+                 IRReader LineEditor ProfileData
 
 include $(LEVEL)/Makefile.common
 
 include $(LEVEL)/Makefile.common
diff --git a/lib/ProfileData/CMakeLists.txt b/lib/ProfileData/CMakeLists.txt
new file mode 100644 (file)
index 0000000..cef3a53
--- /dev/null
@@ -0,0 +1,7 @@
+add_llvm_library(LLVMProfileData
+  InstrProf.cpp
+  InstrProfReader.cpp
+
+  LINK_LIBS
+  LLVMSupport
+)
diff --git a/lib/ProfileData/InstrProf.cpp b/lib/ProfileData/InstrProf.cpp
new file mode 100644 (file)
index 0000000..329abf8
--- /dev/null
@@ -0,0 +1,56 @@
+//=-- InstrProf.cpp - Instrumented profiling format support -----------------=//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains support for clang's instrumentation based PGO and
+// coverage.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ProfileData/InstrProf.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace llvm;
+
+namespace {
+class InstrProfErrorCategoryType : public error_category {
+  const char *name() const override { return "llvm.instrprof"; }
+  std::string message(int IE) const override {
+    instrprof_error::ErrorType E = static_cast<instrprof_error::ErrorType>(IE);
+    switch (E) {
+    case instrprof_error::success:
+      return "Success";
+    case instrprof_error::eof:
+      return "End of File";
+    case instrprof_error::bad_magic:
+      return "Invalid file format (bad magic)";
+    case instrprof_error::unsupported_version:
+      return "Unsupported format version";
+    case instrprof_error::too_large:
+      return "Too much profile data";
+    case instrprof_error::truncated:
+      return "Truncated profile data";
+    case instrprof_error::malformed:
+      return "Malformed profile data";
+    case instrprof_error::unknown_function:
+      return "No profile data available for function";
+    }
+    llvm_unreachable("A value of instrprof_error has no message.");
+  }
+  error_condition default_error_condition(int EV) const {
+    if (EV == instrprof_error::success)
+      return errc::success;
+    return errc::invalid_argument;
+  }
+};
+}
+
+const error_category &llvm::instrprof_category() {
+  static InstrProfErrorCategoryType C;
+  return C;
+}
diff --git a/lib/ProfileData/InstrProfReader.cpp b/lib/ProfileData/InstrProfReader.cpp
new file mode 100644 (file)
index 0000000..8b36000
--- /dev/null
@@ -0,0 +1,84 @@
+//=-- InstrProfReader.cpp - Instrumented profiling reader -------------------=//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains support for reading profiling data for clang's
+// instrumentation based PGO and coverage.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ProfileData/InstrProfReader.h"
+#include "llvm/ProfileData/InstrProf.h"
+#include "llvm/Support/Endian.h"
+
+#include <cassert>
+
+using namespace llvm;
+
+error_code InstrProfReader::create(std::string Path,
+                                   std::unique_ptr<InstrProfReader> &Result) {
+  std::unique_ptr<MemoryBuffer> Buffer;
+  if (error_code EC = MemoryBuffer::getFileOrSTDIN(Path, Buffer))
+    return EC;
+
+  // Sanity check the file.
+  if (Buffer->getBufferSize() > std::numeric_limits<unsigned>::max())
+    return instrprof_error::too_large;
+
+  // FIXME: This needs to determine which format the file is and construct the
+  // correct subclass.
+  Result.reset(new TextInstrProfReader(Buffer));
+
+  return instrprof_error::success;
+}
+
+void InstrProfIterator::Increment() {
+  if (Reader->readNextRecord(Record))
+    *this = InstrProfIterator();
+}
+
+error_code TextInstrProfReader::readNextRecord(InstrProfRecord &Record) {
+  // Skip empty lines.
+  while (!Line.is_at_end() && Line->empty())
+    ++Line;
+  // If we hit EOF while looking for a name, we're done.
+  if (Line.is_at_end())
+    return error(instrprof_error::eof);
+
+  // Read the function name.
+  Record.Name = *Line++;
+
+  // Read the function hash.
+  if (Line.is_at_end())
+    return error(instrprof_error::truncated);
+  if ((Line++)->getAsInteger(10, Record.Hash))
+    return error(instrprof_error::malformed);
+
+  // Read the number of counters.
+  uint64_t NumCounters;
+  if (Line.is_at_end())
+    return error(instrprof_error::truncated);
+  if ((Line++)->getAsInteger(10, NumCounters))
+    return error(instrprof_error::malformed);
+
+  // Read each counter and fill our internal storage with the values.
+  Counts.clear();
+  Counts.reserve(NumCounters);
+  for (uint64_t I = 0; I < NumCounters; ++I) {
+    if (Line.is_at_end())
+      return error(instrprof_error::truncated);
+    uint64_t Count;
+    if ((Line++)->getAsInteger(10, Count))
+      return error(instrprof_error::malformed);
+    Counts.push_back(Count);
+  }
+  // Give the record a reference to our internal counter storage.
+  Record.Counts = Counts;
+
+  return success();
+}
diff --git a/lib/ProfileData/LLVMBuild.txt b/lib/ProfileData/LLVMBuild.txt
new file mode 100644 (file)
index 0000000..0a8cbe3
--- /dev/null
@@ -0,0 +1,22 @@
+;===- ./lib/ProfileData/LLVMBuild.txt --------------------------*- Conf -*--===;
+;
+;                     The LLVM Compiler Infrastructure
+;
+; This file is distributed under the University of Illinois Open Source
+; License. See LICENSE.TXT for details.
+;
+;===------------------------------------------------------------------------===;
+;
+; This is an LLVMBuild description file for the components in this subdirectory.
+;
+; For more information on the LLVMBuild system, please see:
+;
+;   http://llvm.org/docs/LLVMBuild.html
+;
+;===------------------------------------------------------------------------===;
+
+[component_0]
+type = Library
+name = ProfileData
+parent = Libraries
+required_libraries = Support
diff --git a/lib/ProfileData/Makefile b/lib/ProfileData/Makefile
new file mode 100644 (file)
index 0000000..2674361
--- /dev/null
@@ -0,0 +1,14 @@
+##===- lib/ProfileData/Makefile ----------------------------*- Makefile -*-===##
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+LEVEL = ../..
+LIBRARYNAME = LLVMProfileData
+BUILD_ARCHIVE := 1
+
+include $(LEVEL)/Makefile.common
index 219d88dd41ea2b7c73d637cd3dda583215eef788..afac7a4dec1ec1a2b91da11d0ac8d6b7015ef52c 100644 (file)
@@ -1,19 +1,19 @@
 RUN: not llvm-profdata merge %p/Inputs/empty.profdata %p/Inputs/foo3-1.profdata 2>&1 | FileCheck %s --check-prefix=LENGTH
 RUN: not llvm-profdata merge %p/Inputs/foo3-1.profdata %p/Inputs/foo3bar3-1.profdata 2>&1 | FileCheck %s --check-prefix=LENGTH
 RUN: not llvm-profdata merge %p/Inputs/foo4-1.profdata %p/Inputs/empty.profdata 2>&1 | FileCheck %s --check-prefix=LENGTH
 RUN: not llvm-profdata merge %p/Inputs/empty.profdata %p/Inputs/foo3-1.profdata 2>&1 | FileCheck %s --check-prefix=LENGTH
 RUN: not llvm-profdata merge %p/Inputs/foo3-1.profdata %p/Inputs/foo3bar3-1.profdata 2>&1 | FileCheck %s --check-prefix=LENGTH
 RUN: not llvm-profdata merge %p/Inputs/foo4-1.profdata %p/Inputs/empty.profdata 2>&1 | FileCheck %s --check-prefix=LENGTH
-LENGTH: error: {{.*}}: truncated file
+LENGTH: error: Number of instrumented functions differ
 
 RUN: not llvm-profdata merge %p/Inputs/foo3-1.profdata %p/Inputs/bar3-1.profdata 2>&1 | FileCheck %s --check-prefix=NAME
 
 RUN: not llvm-profdata merge %p/Inputs/foo3-1.profdata %p/Inputs/bar3-1.profdata 2>&1 | FileCheck %s --check-prefix=NAME
-NAME: error: {{.*}}: function name mismatch
+NAME: error: Function name mismatch, foo != bar
 
 RUN: not llvm-profdata merge %p/Inputs/foo3-1.profdata %p/Inputs/foo4-1.profdata 2>&1 | FileCheck %s --check-prefix=HASH
 
 RUN: not llvm-profdata merge %p/Inputs/foo3-1.profdata %p/Inputs/foo4-1.profdata 2>&1 | FileCheck %s --check-prefix=HASH
-HASH: error: {{.*}}: function hash mismatch
+HASH: error: Function hash mismatch for foo
 
 RUN: not llvm-profdata merge %p/Inputs/overflow.profdata %p/Inputs/overflow.profdata 2>&1 | FileCheck %s --check-prefix=OVERFLOW
 
 RUN: not llvm-profdata merge %p/Inputs/overflow.profdata %p/Inputs/overflow.profdata 2>&1 | FileCheck %s --check-prefix=OVERFLOW
-OVERFLOW: error: {{.*}}: counter overflow
+OVERFLOW: error: Counter overflow for overflow
 
 RUN: not llvm-profdata merge %p/Inputs/invalid-count-later.profdata %p/Inputs/invalid-count-later.profdata 2>&1 | FileCheck %s --check-prefix=INVALID-COUNT-LATER
 
 RUN: not llvm-profdata merge %p/Inputs/invalid-count-later.profdata %p/Inputs/invalid-count-later.profdata 2>&1 | FileCheck %s --check-prefix=INVALID-COUNT-LATER
-INVALID-COUNT-LATER: error: {{.*}}: invalid counter
+INVALID-COUNT-LATER: error: {{.*}}: Malformed profile data
 
 RUN: not llvm-profdata merge %p/Inputs/bad-hash.profdata %p/Inputs/bad-hash.profdata 2>&1 | FileCheck %s --check-prefix=BAD-HASH
 
 RUN: not llvm-profdata merge %p/Inputs/bad-hash.profdata %p/Inputs/bad-hash.profdata 2>&1 | FileCheck %s --check-prefix=BAD-HASH
-BAD-HASH: error: {{.*}}: bad function hash
+BAD-HASH: error: {{.*}}: Malformed profile data
index 4b1357d87e11a74ad014a83bda285b3e3e306168..4c64cfa1fa241d7c710459efcb1636dac468858d 100644 (file)
@@ -1,4 +1,4 @@
-set(LLVM_LINK_COMPONENTS core support )
+set(LLVM_LINK_COMPONENTS core profiledata support )
 
 add_llvm_tool(llvm-profdata
   llvm-profdata.cpp
 
 add_llvm_tool(llvm-profdata
   llvm-profdata.cpp
index fc9e46919979959016c66846026713de40cb9a5c..bbc1ea6fceb9cfcabed8a6c7e62500e5698397ba 100644 (file)
@@ -19,4 +19,4 @@
 type = Tool
 name = llvm-profdata
 parent = Tools
 type = Tool
 name = llvm-profdata
 parent = Tools
-required_libraries = Support
+required_libraries = ProfileData Support
index 9d7ad527b169a80a07984a8b378ee5c98e8aaf88..4177780d6cf80d4c58ae63e2ad7e11ef972b5e45 100644 (file)
@@ -9,7 +9,7 @@
 
 LEVEL := ../..
 TOOLNAME := llvm-profdata
 
 LEVEL := ../..
 TOOLNAME := llvm-profdata
-LINK_COMPONENTS := core support
+LINK_COMPONENTS := core profiledata support
 
 # This tool has no plugins, optimize startup time.
 TOOL_NO_EXPORTS := 1
 
 # This tool has no plugins, optimize startup time.
 TOOL_NO_EXPORTS := 1
index 47e213113578bc15fa10d263914eeed6d27fb003..53dcf1602c9f2924e2e1c7af274abb4942269a2f 100644 (file)
@@ -12,8 +12,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/ADT/StringRef.h"
 //===----------------------------------------------------------------------===//
 
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ProfileData/InstrProfReader.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/CommandLine.h"
-#include "llvm/Support/LineIterator.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/PrettyStackTrace.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/PrettyStackTrace.h"
 
 using namespace llvm;
 
 
 using namespace llvm;
 
-static void exitWithError(const std::string &Message,
-                          const std::string &Filename, int64_t Line = -1) {
-  errs() << "error: " << Filename;
-  if (Line >= 0)
-    errs() << ":" << Line;
-  errs() << ": " << Message << "\n";
+static void exitWithError(const Twine &Message, StringRef Whence = "") {
+  errs() << "error: ";
+  if (!Whence.empty())
+    errs() << Whence << ": ";
+  errs() << Message << "\n";
   ::exit(1);
 }
 
   ::exit(1);
 }
 
@@ -45,11 +44,10 @@ int merge_main(int argc, const char *argv[]) {
 
   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
 
 
   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
 
-  std::unique_ptr<MemoryBuffer> File1;
-  std::unique_ptr<MemoryBuffer> File2;
-  if (error_code ec = MemoryBuffer::getFile(Filename1, File1))
+  std::unique_ptr<InstrProfReader> Reader1, Reader2;
+  if (error_code ec = InstrProfReader::create(Filename1, Reader1))
     exitWithError(ec.message(), Filename1);
     exitWithError(ec.message(), Filename1);
-  if (error_code ec = MemoryBuffer::getFile(Filename2, File2))
+  if (error_code ec = InstrProfReader::create(Filename2, Reader2))
     exitWithError(ec.message(), Filename2);
 
   if (OutputFilename.empty())
     exitWithError(ec.message(), Filename2);
 
   if (OutputFilename.empty())
@@ -60,64 +58,32 @@ int merge_main(int argc, const char *argv[]) {
   if (!ErrorInfo.empty())
     exitWithError(ErrorInfo, OutputFilename);
 
   if (!ErrorInfo.empty())
     exitWithError(ErrorInfo, OutputFilename);
 
-  enum {ReadName, ReadHash, ReadCount, ReadCounters} State = ReadName;
-  uint64_t N1, N2, NumCounters;
-  line_iterator I1(*File1, '#'), I2(*File2, '#');
-  for (; !I1.is_at_end() && !I2.is_at_end(); ++I1, ++I2) {
-    if (I1->empty()) {
-      if (!I2->empty())
-        exitWithError("data mismatch", Filename2, I2.line_number());
-      Output << "\n";
-      continue;
-    }
-    switch (State) {
-    case ReadName:
-      if (*I1 != *I2)
-        exitWithError("function name mismatch", Filename2, I2.line_number());
-      Output << *I1 << "\n";
-      State = ReadHash;
-      break;
-    case ReadHash:
-      if (I1->getAsInteger(10, N1))
-        exitWithError("bad function hash", Filename1, I1.line_number());
-      if (I2->getAsInteger(10, N2))
-        exitWithError("bad function hash", Filename2, I2.line_number());
-      if (N1 != N2)
-        exitWithError("function hash mismatch", Filename2, I2.line_number());
-      Output << N1 << "\n";
-      State = ReadCount;
-      break;
-    case ReadCount:
-      if (I1->getAsInteger(10, N1))
-        exitWithError("bad function count", Filename1, I1.line_number());
-      if (I2->getAsInteger(10, N2))
-        exitWithError("bad function count", Filename2, I2.line_number());
-      if (N1 != N2)
-        exitWithError("function count mismatch", Filename2, I2.line_number());
-      Output << N1 << "\n";
-      NumCounters = N1;
-      State = ReadCounters;
-      break;
-    case ReadCounters:
-      if (I1->getAsInteger(10, N1))
-        exitWithError("invalid counter", Filename1, I1.line_number());
-      if (I2->getAsInteger(10, N2))
-        exitWithError("invalid counter", Filename2, I2.line_number());
-      uint64_t Sum = N1 + N2;
-      if (Sum < N1)
-        exitWithError("counter overflow", Filename2, I2.line_number());
-      Output << N1 + N2 << "\n";
-      if (--NumCounters == 0)
-        State = ReadName;
-      break;
+  for (InstrProfIterator I1 = Reader1->begin(), E1 = Reader1->end(),
+                         I2 = Reader2->begin(), E2 = Reader2->end();
+       I1 != E1 && I2 != E2; ++I1, ++I2) {
+    if (I1->Name != I2->Name)
+      exitWithError("Function name mismatch, " + I1->Name + " != " + I2->Name);
+    if (I1->Hash != I2->Hash)
+      exitWithError("Function hash mismatch for " + I1->Name);
+    if (I1->Counts.size() != I2->Counts.size())
+      exitWithError("Function count mismatch for " + I1->Name);
+
+    Output << I1->Name << "\n" << I1->Hash << "\n" << I1->Counts.size() << "\n";
+
+    for (size_t II = 0, EE = I1->Counts.size(); II < EE; ++II) {
+      uint64_t Sum = I1->Counts[II] + I2->Counts[II];
+      if (Sum < I1->Counts[II])
+        exitWithError("Counter overflow for " + I1->Name);
+      Output << Sum << "\n";
     }
     }
+    Output << "\n";
   }
   }
-  if (!I1.is_at_end())
-    exitWithError("truncated file", Filename1, I1.line_number());
-  if (!I2.is_at_end())
-    exitWithError("truncated file", Filename2, I2.line_number());
-  if (State != ReadName)
-    exitWithError("truncated file", Filename1, I1.line_number());
+  if (Reader1->hasError())
+    exitWithError(Reader1->getError().message(), Filename1);
+  if (Reader2->hasError())
+    exitWithError(Reader2->getError().message(), Filename2);
+  if (!Reader1->isEOF() || !Reader2->isEOF())
+    exitWithError("Number of instrumented functions differ.");
 
   return 0;
 }
 
   return 0;
 }
@@ -158,6 +124,6 @@ int main(int argc, const char *argv[]) {
   else
     errs() << ProgName << ": Unknown command!\n";
 
   else
     errs() << ProgName << ": Unknown command!\n";
 
-  errs() << "USAGE: " << ProgName << " <merge|show|generate> [args...]\n";
+  errs() << "USAGE: " << ProgName << " <merge> [args...]\n";
   return 1;
 }
   return 1;
 }