summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/llvm/ProfileData/InstrProf.h5
-rw-r--r--include/llvm/ProfileData/InstrProfWriter.h49
-rw-r--r--lib/ProfileData/CMakeLists.txt1
-rw-r--r--lib/ProfileData/InstrProf.cpp6
-rw-r--r--lib/ProfileData/InstrProfWriter.cpp58
-rw-r--r--test/tools/llvm-profdata/errors.test20
-rw-r--r--test/tools/llvm-profdata/simple.test97
-rw-r--r--tools/llvm-profdata/llvm-profdata.cpp50
8 files changed, 205 insertions, 81 deletions
diff --git a/include/llvm/ProfileData/InstrProf.h b/include/llvm/ProfileData/InstrProf.h
index 21cc170065..c803069c46 100644
--- a/include/llvm/ProfileData/InstrProf.h
+++ b/include/llvm/ProfileData/InstrProf.h
@@ -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
index 0000000000..388921e266
--- /dev/null
+++ b/include/llvm/ProfileData/InstrProfWriter.h
@@ -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__
diff --git a/lib/ProfileData/CMakeLists.txt b/lib/ProfileData/CMakeLists.txt
index cef3a5336b..5204afcdc4 100644
--- a/lib/ProfileData/CMakeLists.txt
+++ b/lib/ProfileData/CMakeLists.txt
@@ -1,6 +1,7 @@
add_llvm_library(LLVMProfileData
InstrProf.cpp
InstrProfReader.cpp
+ InstrProfWriter.cpp
LINK_LIBS
LLVMSupport
diff --git a/lib/ProfileData/InstrProf.cpp b/lib/ProfileData/InstrProf.cpp
index 329abf84e6..ecabdd75bd 100644
--- a/lib/ProfileData/InstrProf.cpp
+++ b/lib/ProfileData/InstrProf.cpp
@@ -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
index 0000000000..320860a4cb
--- /dev/null
+++ b/lib/ProfileData/InstrProfWriter.cpp
@@ -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";
+ }
+}
diff --git a/test/tools/llvm-profdata/errors.test b/test/tools/llvm-profdata/errors.test
index afac7a4dec..487da74b86 100644
--- a/test/tools/llvm-profdata/errors.test
+++ b/test/tools/llvm-profdata/errors.test
@@ -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
diff --git a/test/tools/llvm-profdata/simple.test b/test/tools/llvm-profdata/simple.test
index 74ff5e6027..5ef38d447e 100644
--- a/test/tools/llvm-profdata/simple.test
+++ b/test/tools/llvm-profdata/simple.test
@@ -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
diff --git a/tools/llvm-profdata/llvm-profdata.cpp b/tools/llvm-profdata/llvm-profdata.cpp
index d5fa4ac288..ce9ae9ae4f 100644
--- a/tools/llvm-profdata/llvm-profdata.cpp
+++ b/tools/llvm-profdata/llvm-profdata.cpp
@@ -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;
}