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 + +namespace llvm { + +/// Writer for instrumentation based profile data. +class InstrProfWriter { +public: + struct CounterData { + uint64_t Hash; + std::vector Counts; + }; +private: + StringMap 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 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 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 &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 Filename1(cl::Positional, cl::Required, - cl::desc("file1")); - cl::opt Filename2(cl::Positional, cl::Required, - cl::desc("file2")); + cl::list Inputs(cl::Positional, cl::Required, cl::OneOrMore, + cl::desc("")); cl::opt 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 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 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; }