ProfileData: Introduce InstrProfWriter using the naive text format
authorJustin Bogner <mail@justinbogner.com>
Fri, 21 Mar 2014 17:46:22 +0000 (17:46 +0000)
committerJustin Bogner <mail@justinbogner.com>
Fri, 21 Mar 2014 17:46:22 +0000 (17:46 +0000)
This isn't a format we'll want to write out in practice, but moving it
to the writer library simplifies llvm-profdata and isolates it from
further changes to the format.

This also allows us to update the tests to not rely on the text output
format.

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

include/llvm/ProfileData/InstrProf.h
include/llvm/ProfileData/InstrProfWriter.h [new file with mode: 0644]
lib/ProfileData/CMakeLists.txt
lib/ProfileData/InstrProf.cpp
lib/ProfileData/InstrProfWriter.cpp [new file with mode: 0644]
test/tools/llvm-profdata/errors.test
test/tools/llvm-profdata/simple.test
tools/llvm-profdata/llvm-profdata.cpp

index 21cc170..c803069 100644 (file)
@@ -31,7 +31,10 @@ struct instrprof_error {
     too_large,
     truncated,
     malformed,
-    unknown_function
+    unknown_function,
+    hash_mismatch,
+    count_mismatch,
+    counter_overflow
   };
   ErrorType V;
 
diff --git a/include/llvm/ProfileData/InstrProfWriter.h b/include/llvm/ProfileData/InstrProfWriter.h
new file mode 100644 (file)
index 0000000..388921e
--- /dev/null
@@ -0,0 +1,49 @@
+//=-- InstrProfWriter.h - Instrumented profiling writer -----------*- 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 writing profiling data for instrumentation
+// based PGO and coverage.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_PROFILEDATA_INSTRPROF_WRITER_H__
+#define LLVM_PROFILEDATA_INSTRPROF_WRITER_H__
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ProfileData/InstrProf.h"
+#include "llvm/Support/DataTypes.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <vector>
+
+namespace llvm {
+
+/// Writer for instrumentation based profile data.
+class InstrProfWriter {
+public:
+  struct CounterData {
+    uint64_t Hash;
+    std::vector<uint64_t> Counts;
+  };
+private:
+  StringMap<CounterData> FunctionData;
+public:
+  /// Add function counts for the given function. If there are already counts
+  /// for this function and the hash and number of counts match, each counter is
+  /// summed.
+  error_code addFunctionCounts(StringRef FunctionName, uint64_t FunctionHash,
+                               ArrayRef<uint64_t> Counters);
+  /// Ensure that all data is written to disk.
+  void write(raw_ostream &OS);
+};
+
+} // end namespace llvm
+
+#endif // LLVM_PROFILE_INSTRPROF_WRITER_H__
index cef3a53..5204afc 100644 (file)
@@ -1,6 +1,7 @@
 add_llvm_library(LLVMProfileData
   InstrProf.cpp
   InstrProfReader.cpp
+  InstrProfWriter.cpp
 
   LINK_LIBS
   LLVMSupport
index 329abf8..ecabdd7 100644 (file)
@@ -39,6 +39,12 @@ class InstrProfErrorCategoryType : public error_category {
       return "Malformed profile data";
     case instrprof_error::unknown_function:
       return "No profile data available for function";
+    case instrprof_error::hash_mismatch:
+      return "Function hash mismatch";
+    case instrprof_error::count_mismatch:
+      return "Function count mismatch";
+    case instrprof_error::counter_overflow:
+      return "Counter overflow";
     }
     llvm_unreachable("A value of instrprof_error has no message.");
   }
diff --git a/lib/ProfileData/InstrProfWriter.cpp b/lib/ProfileData/InstrProfWriter.cpp
new file mode 100644 (file)
index 0000000..320860a
--- /dev/null
@@ -0,0 +1,58 @@
+//=-- InstrProfWriter.cpp - Instrumented profiling writer -------------------=//
+//
+//                     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 writing profiling data for clang's
+// instrumentation based PGO and coverage.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ProfileData/InstrProfWriter.h"
+#include "llvm/Support/Endian.h"
+
+using namespace llvm;
+
+error_code InstrProfWriter::addFunctionCounts(StringRef FunctionName,
+                                              uint64_t FunctionHash,
+                                              ArrayRef<uint64_t> Counters) {
+  auto Where = FunctionData.find(FunctionName);
+  if (Where == FunctionData.end()) {
+    // If this is the first time we've seen this function, just add it.
+    FunctionData[FunctionName] = {FunctionHash, Counters};
+    return instrprof_error::success;;
+  }
+
+  auto &Data = Where->getValue();
+  // We can only add to existing functions if they match, so we check the hash
+  // and number of counters.
+  if (Data.Hash != FunctionHash)
+    return instrprof_error::hash_mismatch;
+  if (Data.Counts.size() != Counters.size())
+    return instrprof_error::count_mismatch;
+  // These match, add up the counters.
+  for (size_t I = 0, E = Counters.size(); I < E; ++I) {
+    if (Data.Counts[I] + Counters[I] < Data.Counts[I])
+      return instrprof_error::counter_overflow;
+    Data.Counts[I] += Counters[I];
+  }
+  return instrprof_error::success;
+}
+
+void InstrProfWriter::write(raw_ostream &OS) {
+  // Write out the counts for each function.
+  for (const auto &I : FunctionData) {
+    StringRef Name = I.getKey();
+    uint64_t Hash = I.getValue().Hash;
+    const std::vector<uint64_t> &Counts = I.getValue().Counts;
+
+    OS << Name << "\n" << Hash << "\n" << Counts.size() << "\n";
+    for (uint64_t Count : Counts)
+      OS << Count << "\n";
+    OS << "\n";
+  }
+}
index afac7a4..487da74 100644 (file)
@@ -1,19 +1,11 @@
-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: Number of instrumented functions differ
+RUN: llvm-profdata merge %p/Inputs/foo3-1.profdata %p/Inputs/foo4-1.profdata -o /dev/null 2>&1 | FileCheck %s --check-prefix=HASH
+HASH: foo4-1.profdata: foo: Function hash mismatch
 
-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, foo != bar
-
-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 for foo
-
-RUN: not llvm-profdata merge %p/Inputs/overflow.profdata %p/Inputs/overflow.profdata 2>&1 | FileCheck %s --check-prefix=OVERFLOW
-OVERFLOW: error: Counter overflow for overflow
+RUN: llvm-profdata merge %p/Inputs/overflow.profdata %p/Inputs/overflow.profdata -o /dev/null 2>&1 | FileCheck %s --check-prefix=OVERFLOW
+OVERFLOW: overflow.profdata: overflow: Counter 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
-INVALID-COUNT-LATER: error: {{.*}}: Malformed profile data
+INVALID-COUNT-LATER: error: {{.*}}invalid-count-later.profdata: 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
-BAD-HASH: error: {{.*}}: Malformed profile data
+BAD-HASH: error: {{.*}}bad-hash.profdata: Malformed profile data
index 74ff5e6..5ef38d4 100644 (file)
@@ -1,33 +1,68 @@
-RUN: llvm-profdata merge %p/Inputs/foo3-1.profdata %p/Inputs/foo3-2.profdata 2>&1 | FileCheck %s --check-prefix=FOO3
-RUN: llvm-profdata merge %p/Inputs/foo3-2.profdata %p/Inputs/foo3-1.profdata 2>&1 | FileCheck %s --check-prefix=FOO3
-FOO3:      {{^foo$}}
-FOO3-NEXT: {{^3$}}
-FOO3-NEXT: {{^3$}}
-FOO3-NEXT: {{^8$}}
-FOO3-NEXT: {{^7$}}
-FOO3-NEXT: {{^6$}}
+RUN: llvm-profdata merge %p/Inputs/foo3-1.profdata %p/Inputs/foo3-2.profdata 2>&1 | llvm-profdata show - -all-functions -counts | FileCheck %s --check-prefix=FOO3
+RUN: llvm-profdata merge %p/Inputs/foo3-2.profdata %p/Inputs/foo3-1.profdata 2>&1 | llvm-profdata show - -all-functions -counts | FileCheck %s --check-prefix=FOO3
+FOO3: foo:
+FOO3: Counters: 3
+FOO3: Function count: 8
+FOO3: Block counts: [7, 6]
+FOO3: Total functions: 1
+FOO3: Maximum function count: 8
+FOO3: Maximum internal block count: 7
 
-RUN: llvm-profdata merge %p/Inputs/foo4-1.profdata %p/Inputs/foo4-2.profdata 2>&1 | FileCheck %s --check-prefix=FOO4
-RUN: llvm-profdata merge %p/Inputs/foo4-2.profdata %p/Inputs/foo4-1.profdata 2>&1 | FileCheck %s --check-prefix=FOO4
-FOO4:      {{^foo$}}
-FOO4-NEXT: {{^4$}}
-FOO4-NEXT: {{^4$}}
-FOO4-NEXT: {{^18$}}
-FOO4-NEXT: {{^28$}}
-FOO4-NEXT: {{^38$}}
-FOO4-NEXT: {{^48$}}
+RUN: llvm-profdata merge %p/Inputs/foo4-1.profdata %p/Inputs/foo4-2.profdata 2>&1 | llvm-profdata show - -all-functions -counts | FileCheck %s --check-prefix=FOO4
+RUN: llvm-profdata merge %p/Inputs/foo4-2.profdata %p/Inputs/foo4-1.profdata 2>&1 | llvm-profdata show - -all-functions -counts | FileCheck %s --check-prefix=FOO4
+FOO4: foo:
+FOO4: Counters: 4
+FOO4: Function count: 18
+FOO4: Block counts: [28, 38, 48]
+FOO4: Total functions: 1
+FOO4: Maximum function count: 18
+FOO4: Maximum internal block count: 48
 
-RUN: llvm-profdata merge %p/Inputs/foo3bar3-1.profdata %p/Inputs/foo3bar3-2.profdata 2>&1 | FileCheck %s --check-prefix=FOO3BAR3
-RUN: llvm-profdata merge %p/Inputs/foo3bar3-2.profdata %p/Inputs/foo3bar3-1.profdata 2>&1 | FileCheck %s --check-prefix=FOO3BAR3
-FOO3BAR3:      {{^foo$}}
-FOO3BAR3-NEXT: {{^3$}}
-FOO3BAR3-NEXT: {{^3$}}
-FOO3BAR3-NEXT: {{^19$}}
-FOO3BAR3-NEXT: {{^22$}}
-FOO3BAR3-NEXT: {{^28$}}
-FOO3BAR3:      {{^bar$}}
-FOO3BAR3-NEXT: {{^3$}}
-FOO3BAR3-NEXT: {{^3$}}
-FOO3BAR3-NEXT: {{^36$}}
-FOO3BAR3-NEXT: {{^42$}}
-FOO3BAR3-NEXT: {{^50$}}
+RUN: llvm-profdata merge %p/Inputs/foo3bar3-1.profdata %p/Inputs/foo3bar3-2.profdata 2>&1 | llvm-profdata show - -all-functions -counts | FileCheck %s --check-prefix=FOO3BAR3
+RUN: llvm-profdata merge %p/Inputs/foo3bar3-2.profdata %p/Inputs/foo3bar3-1.profdata 2>&1 | llvm-profdata show - -all-functions -counts | FileCheck %s --check-prefix=FOO3BAR3
+FOO3BAR3: foo:
+FOO3BAR3: Counters: 3
+FOO3BAR3: Function count: 19
+FOO3BAR3: Block counts: [22, 28]
+FOO3BAR3: bar:
+FOO3BAR3: Counters: 3
+FOO3BAR3: Function count: 36
+FOO3BAR3: Block counts: [42, 50]
+FOO3BAR3: Total functions: 2
+FOO3BAR3: Maximum function count: 36
+FOO3BAR3: Maximum internal block count: 50
+
+RUN: llvm-profdata merge %p/Inputs/empty.profdata %p/Inputs/foo3-1.profdata 2>&1 | llvm-profdata show - -all-functions -counts | FileCheck %s --check-prefix=FOO3EMPTY
+FOO3EMPTY: foo:
+FOO3EMPTY: Counters: 3
+FOO3EMPTY: Function count: 1
+FOO3EMPTY: Block counts: [2, 3]
+FOO3EMPTY: Total functions: 1
+FOO3EMPTY: Maximum function count: 1
+FOO3EMPTY: Maximum internal block count: 3
+
+RUN: llvm-profdata merge %p/Inputs/foo3-1.profdata %p/Inputs/foo3bar3-1.profdata 2>&1 | llvm-profdata show - -all-functions -counts | FileCheck %s --check-prefix=FOO3FOO3BAR3
+FOO3FOO3BAR3: foo:
+FOO3FOO3BAR3: Counters: 3
+FOO3FOO3BAR3: Function count: 3
+FOO3FOO3BAR3: Block counts: [5, 8]
+FOO3FOO3BAR3: bar:
+FOO3FOO3BAR3: Counters: 3
+FOO3FOO3BAR3: Function count: 7
+FOO3FOO3BAR3: Block counts: [11, 13]
+FOO3FOO3BAR3: Total functions: 2
+FOO3FOO3BAR3: Maximum function count: 7
+FOO3FOO3BAR3: Maximum internal block count: 13
+
+RUN: llvm-profdata merge %p/Inputs/foo3-1.profdata %p/Inputs/bar3-1.profdata 2>&1 | llvm-profdata show - -all-functions -counts | FileCheck %s --check-prefix=DISJOINT
+DISJOINT: foo:
+DISJOINT: Counters: 3
+DISJOINT: Function count: 1
+DISJOINT: Block counts: [2, 3]
+DISJOINT: bar:
+DISJOINT: Counters: 3
+DISJOINT: Function count: 1
+DISJOINT: Block counts: [2, 3]
+DISJOINT: Total functions: 2
+DISJOINT: Maximum function count: 1
+DISJOINT: Maximum internal block count: 3
index d5fa4ac..ce9ae9a 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ProfileData/InstrProfReader.h"
+#include "llvm/ProfileData/InstrProfWriter.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -31,10 +32,8 @@ static void exitWithError(const Twine &Message, StringRef Whence = "") {
 }
 
 int merge_main(int argc, const char *argv[]) {
-  cl::opt<std::string> Filename1(cl::Positional, cl::Required,
-                                 cl::desc("file1"));
-  cl::opt<std::string> Filename2(cl::Positional, cl::Required,
-                                 cl::desc("file2"));
+  cl::list<std::string> Inputs(cl::Positional, cl::Required, cl::OneOrMore,
+                               cl::desc("<filenames...>"));
 
   cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
                                       cl::init("-"),
@@ -44,12 +43,6 @@ int merge_main(int argc, const char *argv[]) {
 
   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
 
-  std::unique_ptr<InstrProfReader> Reader1, Reader2;
-  if (error_code ec = InstrProfReader::create(Filename1, Reader1))
-    exitWithError(ec.message(), Filename1);
-  if (error_code ec = InstrProfReader::create(Filename2, Reader2))
-    exitWithError(ec.message(), Filename2);
-
   if (OutputFilename.empty())
     OutputFilename = "-";
 
@@ -58,32 +51,19 @@ int merge_main(int argc, const char *argv[]) {
   if (!ErrorInfo.empty())
     exitWithError(ErrorInfo, OutputFilename);
 
-  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";
+  InstrProfWriter Writer;
+  for (const auto &Filename : Inputs) {
+    std::unique_ptr<InstrProfReader> Reader;
+    if (error_code ec = InstrProfReader::create(Filename, Reader))
+      exitWithError(ec.message(), Filename);
+
+    for (const auto &I : *Reader)
+      if (error_code EC = Writer.addFunctionCounts(I.Name, I.Hash, I.Counts))
+        errs() << Filename << ": " << I.Name << ": " << EC.message() << "\n";
+    if (Reader->hasError())
+      exitWithError(Reader->getError().message(), Filename);
   }
-  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.");
+  Writer.write(Output);
 
   return 0;
 }