From 9657de5f228505bc17c68ce15e0c9215456a71fa Mon Sep 17 00:00:00 2001 From: Diego Novillo Date: Sat, 1 Nov 2014 00:56:55 +0000 Subject: [PATCH] Add show and merge tools for sample PGO profiles. Summary: This patch extends the 'show' and 'merge' commands in llvm-profdata to handle sample PGO formats. Using the 'merge' command it is now possible to convert one sample PGO format to another. The only format that is currently not working is 'gcc'. I still need to implement support for it in lib/ProfileData. The changes in the sample profile support classes are needed for the merge operation. Reviewers: bogner Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D6065 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@221032 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/ProfileData/SampleProf.h | 60 +++++- include/llvm/ProfileData/SampleProfReader.h | 15 +- include/llvm/ProfileData/SampleProfWriter.h | 40 +++- lib/ProfileData/SampleProf.cpp | 2 + lib/ProfileData/SampleProfReader.cpp | 46 ++--- lib/ProfileData/SampleProfWriter.cpp | 66 ++++--- .../Inputs/sample-profile.proftext | 12 ++ .../llvm-profdata/sample-profile-basic.test | 30 +++ tools/llvm-profdata/llvm-profdata.cpp | 175 +++++++++++++----- 9 files changed, 323 insertions(+), 123 deletions(-) create mode 100644 test/tools/llvm-profdata/Inputs/sample-profile.proftext create mode 100644 test/tools/llvm-profdata/sample-profile-basic.test diff --git a/include/llvm/ProfileData/SampleProf.h b/include/llvm/ProfileData/SampleProf.h index ef782c11043..5c70b316fbc 100644 --- a/include/llvm/ProfileData/SampleProf.h +++ b/include/llvm/ProfileData/SampleProf.h @@ -16,6 +16,8 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include @@ -30,7 +32,8 @@ enum class sampleprof_error { unsupported_version, too_large, truncated, - malformed + malformed, + unrecognized_format }; inline std::error_code make_error_code(sampleprof_error E) { @@ -110,27 +113,49 @@ namespace sampleprof { /// will be a list of one or more functions. class SampleRecord { public: - typedef SmallVector, 8> CallTargetList; + typedef StringMap CallTargetMap; SampleRecord() : NumSamples(0), CallTargets() {} /// \brief Increment the number of samples for this record by \p S. - void addSamples(unsigned S) { NumSamples += S; } + /// + /// Sample counts accumulate using saturating arithmetic, to avoid wrapping + /// around unsigned integers. + void addSamples(unsigned S) { + if (NumSamples <= std::numeric_limits::max() - S) + NumSamples += S; + else + NumSamples = std::numeric_limits::max(); + } /// \brief Add called function \p F with samples \p S. - void addCalledTarget(std::string F, unsigned S) { - CallTargets.push_back(std::make_pair(F, S)); + /// + /// Sample counts accumulate using saturating arithmetic, to avoid wrapping + /// around unsigned integers. + void addCalledTarget(StringRef F, unsigned S) { + unsigned &TargetSamples = CallTargets[F]; + if (TargetSamples <= std::numeric_limits::max() - S) + TargetSamples += S; + else + TargetSamples = std::numeric_limits::max(); } /// \brief Return true if this sample record contains function calls. bool hasCalls() const { return CallTargets.size() > 0; } unsigned getSamples() const { return NumSamples; } - const CallTargetList &getCallTargets() const { return CallTargets; } + const CallTargetMap &getCallTargets() const { return CallTargets; } + + /// \brief Merge the samples in \p Other into this record. + void merge(const SampleRecord &Other) { + addSamples(Other.getSamples()); + for (const auto &I : Other.getCallTargets()) + addCalledTarget(I.first(), I.second); + } private: unsigned NumSamples; - CallTargetList CallTargets; + CallTargetMap CallTargets; }; typedef DenseMap BodySampleMap; @@ -143,7 +168,7 @@ typedef DenseMap BodySampleMap; class FunctionSamples { public: FunctionSamples() : TotalSamples(0), TotalHeadSamples(0) {} - void print(raw_ostream &OS); + void print(raw_ostream &OS = dbgs()); void addTotalSamples(unsigned Num) { TotalSamples += Num; } void addHeadSamples(unsigned Num) { TotalHeadSamples += Num; } void addBodySamples(int LineOffset, unsigned Discriminator, unsigned Num) { @@ -163,10 +188,16 @@ public: Num); } + /// \brief Return the sample record at the given location. + /// Each location is specified by \p LineOffset and \p Discriminator. + SampleRecord &sampleRecordAt(const LineLocation &Loc) { + return BodySamples[Loc]; + } + /// \brief Return the number of samples collected at the given location. /// Each location is specified by \p LineOffset and \p Discriminator. unsigned samplesAt(int LineOffset, unsigned Discriminator) { - return BodySamples[LineLocation(LineOffset, Discriminator)].getSamples(); + return sampleRecordAt(LineLocation(LineOffset, Discriminator)).getSamples(); } bool empty() const { return BodySamples.empty(); } @@ -181,6 +212,17 @@ public: /// \brief Return all the samples collected in the body of the function. const BodySampleMap &getBodySamples() const { return BodySamples; } + /// \brief Merge the samples in \p Other into this one. + void merge(const FunctionSamples &Other) { + addTotalSamples(Other.getTotalSamples()); + addHeadSamples(Other.getHeadSamples()); + for (const auto &I : Other.getBodySamples()) { + const LineLocation &Loc = I.first; + const SampleRecord &Rec = I.second; + sampleRecordAt(Loc).merge(Rec); + } + } + private: /// \brief Total number of samples collected inside this function. /// diff --git a/include/llvm/ProfileData/SampleProfReader.h b/include/llvm/ProfileData/SampleProfReader.h index e52ecf8e212..e9ceae5d835 100644 --- a/include/llvm/ProfileData/SampleProfReader.h +++ b/include/llvm/ProfileData/SampleProfReader.h @@ -21,6 +21,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/ProfileData/SampleProf.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/MemoryBuffer.h" @@ -64,9 +65,6 @@ public: virtual ~SampleProfileReader() {} - /// \brief Print all the profiles to dbgs(). - void dump(); - /// \brief Read and validate the file header. virtual std::error_code readHeader() = 0; @@ -74,16 +72,19 @@ public: virtual std::error_code read() = 0; /// \brief Print the profile for \p FName on stream \p OS. - void printFunctionProfile(raw_ostream &OS, StringRef FName); + void dumpFunctionProfile(StringRef FName, raw_ostream &OS = dbgs()); - /// \brief Print the profile for \p FName on dbgs(). - void dumpFunctionProfile(StringRef FName); + /// \brief Print all the profiles on stream \p OS. + void dump(raw_ostream &OS = dbgs()); /// \brief Return the samples collected for function \p F. FunctionSamples *getSamplesFor(const Function &F) { return &Profiles[F.getName()]; } + /// \brief Return all the profiles. + StringMap &getProfiles() { return Profiles; } + /// \brief Report a parse error message. void reportParseError(int64_t LineNumber, Twine Msg) const { Ctx.diagnose(DiagnosticInfoSampleProfile(Buffer->getBufferIdentifier(), @@ -91,7 +92,7 @@ public: } /// \brief Create a sample profile reader appropriate to the file format. - static std::error_code create(std::string Filename, + static std::error_code create(StringRef Filename, std::unique_ptr &Reader, LLVMContext &C); diff --git a/include/llvm/ProfileData/SampleProfWriter.h b/include/llvm/ProfileData/SampleProfWriter.h index 62de704c3af..465adb6a203 100644 --- a/include/llvm/ProfileData/SampleProfWriter.h +++ b/include/llvm/ProfileData/SampleProfWriter.h @@ -24,6 +24,8 @@ namespace llvm { namespace sampleprof { +enum SampleProfileFormat { SPF_None = 0, SPF_Text, SPF_Binary, SPF_GCC }; + /// \brief Sample-based profile writer. Base class. class SampleProfileWriter { public: @@ -32,21 +34,47 @@ public: : OS(Filename, EC, Flags) {} virtual ~SampleProfileWriter() {} - /// \brief Write sample profiles in \p S for function \p F. + /// \brief Write sample profiles in \p S for function \p FName. /// /// \returns true if the file was updated successfully. False, otherwise. - virtual bool write(const Function &F, const FunctionSamples &S) = 0; + virtual bool write(StringRef FName, const FunctionSamples &S) = 0; + + /// \brief Write sample profiles in \p S for function \p F. + bool write(const Function &F, const FunctionSamples &S) { + return write(F.getName(), S); + } /// \brief Write all the sample profiles for all the functions in \p M. /// /// \returns true if the file was updated successfully. False, otherwise. bool write(const Module &M, StringMap &P) { - for (Module::const_iterator I = M.begin(), E = M.end(); I != E; ++I) - if (!write((*I), P[I->getName()])) + for (const auto &F : M) { + StringRef Name = F.getName(); + if (!write(Name, P[Name])) return false; + } return true; } + /// \brief Write all the sample profiles in the given map of samples. + /// + /// \returns true if the file was updated successfully. False, otherwise. + bool write(StringMap &ProfileMap) { + for (auto &I : ProfileMap) { + StringRef FName = I.first(); + FunctionSamples &Profile = I.second; + if (!write(FName, Profile)) + return false; + } + return true; + } + + /// \brief Profile writer factory. Create a new writer based on the value of + /// \p Format. + static std::error_code create(StringRef Filename, + std::unique_ptr &Result, + SampleProfileFormat Format); + protected: /// \brief Output stream where to emit the profile to. raw_fd_ostream OS; @@ -58,7 +86,7 @@ public: SampleProfileWriterText(StringRef F, std::error_code &EC) : SampleProfileWriter(F, EC, sys::fs::F_Text) {} - bool write(const Function &F, const FunctionSamples &S) override; + bool write(StringRef FName, const FunctionSamples &S) override; bool write(const Module &M, StringMap &P) { return SampleProfileWriter::write(M, P); } @@ -69,7 +97,7 @@ class SampleProfileWriterBinary : public SampleProfileWriter { public: SampleProfileWriterBinary(StringRef F, std::error_code &EC); - bool write(const Function &F, const FunctionSamples &S) override; + bool write(StringRef F, const FunctionSamples &S) override; bool write(const Module &M, StringMap &P) { return SampleProfileWriter::write(M, P); } diff --git a/lib/ProfileData/SampleProf.cpp b/lib/ProfileData/SampleProf.cpp index 8bd2249f57e..920c48a2464 100644 --- a/lib/ProfileData/SampleProf.cpp +++ b/lib/ProfileData/SampleProf.cpp @@ -36,6 +36,8 @@ class SampleProfErrorCategoryType : public std::error_category { return "Truncated profile data"; case sampleprof_error::malformed: return "Malformed profile data"; + case sampleprof_error::unrecognized_format: + return "Unrecognized profile encoding format"; } llvm_unreachable("A value of sampleprof_error has no message."); } diff --git a/lib/ProfileData/SampleProfReader.cpp b/lib/ProfileData/SampleProfReader.cpp index df4be83f5f8..a6e4e0ce502 100644 --- a/lib/ProfileData/SampleProfReader.cpp +++ b/lib/ProfileData/SampleProfReader.cpp @@ -95,7 +95,6 @@ //===----------------------------------------------------------------------===// #include "llvm/ProfileData/SampleProfReader.h" -#include "llvm/ProfileData/SampleProfWriter.h" // REMOVE #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/LEB128.h" @@ -112,50 +111,36 @@ using namespace llvm; void FunctionSamples::print(raw_ostream &OS) { OS << TotalSamples << ", " << TotalHeadSamples << ", " << BodySamples.size() << " sampled lines\n"; - for (BodySampleMap::const_iterator SI = BodySamples.begin(), - SE = BodySamples.end(); - SI != SE; ++SI) { - LineLocation Loc = SI->first; - SampleRecord Sample = SI->second; + for (const auto &SI : BodySamples) { + LineLocation Loc = SI.first; + const SampleRecord &Sample = SI.second; OS << "\tline offset: " << Loc.LineOffset << ", discriminator: " << Loc.Discriminator << ", number of samples: " << Sample.getSamples(); if (Sample.hasCalls()) { OS << ", calls:"; - for (SampleRecord::CallTargetList::const_iterator - I = Sample.getCallTargets().begin(), - E = Sample.getCallTargets().end(); - I != E; ++I) - OS << " " << (*I).first << ":" << (*I).second; + for (const auto &I : Sample.getCallTargets()) + OS << " " << I.first() << ":" << I.second; } OS << "\n"; } OS << "\n"; } -/// \brief Print the function profile for \p FName on stream \p OS. +/// \brief Dump the function profile for \p FName. /// -/// \param OS Stream to emit the output to. /// \param FName Name of the function to print. -void SampleProfileReader::printFunctionProfile(raw_ostream &OS, - StringRef FName) { +/// \param OS Stream to emit the output to. +void SampleProfileReader::dumpFunctionProfile(StringRef FName, + raw_ostream &OS) { OS << "Function: " << FName << ": "; Profiles[FName].print(OS); } -/// \brief Dump the function profile for \p FName. -/// -/// \param FName Name of the function to print. -void SampleProfileReader::dumpFunctionProfile(StringRef FName) { - printFunctionProfile(dbgs(), FName); -} - -/// \brief Dump all the function profiles found. -void SampleProfileReader::dump() { - for (StringMap::const_iterator I = Profiles.begin(), - E = Profiles.end(); - I != E; ++I) - dumpFunctionProfile(I->getKey()); +/// \brief Dump all the function profiles found on stream \p OS. +void SampleProfileReader::dump(raw_ostream &OS) { + for (const auto &I : Profiles) + dumpFunctionProfile(I.getKey(), OS); } /// \brief Load samples from a text file. @@ -245,8 +230,7 @@ std::error_code SampleProfileReaderText::read() { return sampleprof_error::success; } -template -ErrorOr SampleProfileReaderBinary::readNumber() { +template ErrorOr SampleProfileReaderBinary::readNumber() { unsigned NumBytesRead = 0; std::error_code EC; uint64_t Val = decodeULEB128(Data, &NumBytesRead); @@ -396,7 +380,7 @@ setupMemoryBuffer(std::string Filename, std::unique_ptr &Buffer) { /// /// \returns an error code indicating the status of the created reader. std::error_code -SampleProfileReader::create(std::string Filename, +SampleProfileReader::create(StringRef Filename, std::unique_ptr &Reader, LLVMContext &C) { std::unique_ptr Buffer; diff --git a/lib/ProfileData/SampleProfWriter.cpp b/lib/ProfileData/SampleProfWriter.cpp index ab2e7617762..49d6fdbf4f7 100644 --- a/lib/ProfileData/SampleProfWriter.cpp +++ b/lib/ProfileData/SampleProfWriter.cpp @@ -30,19 +30,16 @@ using namespace llvm::sampleprof; using namespace llvm; /// \brief Write samples to a text file. -bool SampleProfileWriterText::write(const Function &F, - const FunctionSamples &S) { +bool SampleProfileWriterText::write(StringRef FName, const FunctionSamples &S) { if (S.empty()) return true; - OS << F.getName() << ":" << S.getTotalSamples() << ":" << S.getHeadSamples() + OS << FName << ":" << S.getTotalSamples() << ":" << S.getHeadSamples() << "\n"; - for (BodySampleMap::const_iterator I = S.getBodySamples().begin(), - E = S.getBodySamples().end(); - I != E; ++I) { - LineLocation Loc = I->first; - SampleRecord Sample = I->second; + for (const auto &I : S.getBodySamples()) { + LineLocation Loc = I.first; + const SampleRecord &Sample = I.second; if (Loc.Discriminator == 0) OS << Loc.LineOffset << ": "; else @@ -50,11 +47,8 @@ bool SampleProfileWriterText::write(const Function &F, OS << Sample.getSamples(); - for (SampleRecord::CallTargetList::const_iterator - I = Sample.getCallTargets().begin(), - E = Sample.getCallTargets().end(); - I != E; ++I) - OS << " " << (*I).first << ":" << (*I).second; + for (const auto &J : Sample.getCallTargets()) + OS << " " << J.first() << ":" << J.second; OS << "\n"; } @@ -75,31 +69,26 @@ SampleProfileWriterBinary::SampleProfileWriterBinary(StringRef F, /// \brief Write samples to a binary file. /// /// \returns true if the samples were written successfully, false otherwise. -bool SampleProfileWriterBinary::write(const Function &F, +bool SampleProfileWriterBinary::write(StringRef FName, const FunctionSamples &S) { if (S.empty()) return true; - OS << F.getName(); + OS << FName; encodeULEB128(0, OS); encodeULEB128(S.getTotalSamples(), OS); encodeULEB128(S.getHeadSamples(), OS); encodeULEB128(S.getBodySamples().size(), OS); - for (BodySampleMap::const_iterator I = S.getBodySamples().begin(), - E = S.getBodySamples().end(); - I != E; ++I) { - LineLocation Loc = I->first; - SampleRecord Sample = I->second; + for (const auto &I : S.getBodySamples()) { + LineLocation Loc = I.first; + const SampleRecord &Sample = I.second; encodeULEB128(Loc.LineOffset, OS); encodeULEB128(Loc.Discriminator, OS); encodeULEB128(Sample.getSamples(), OS); encodeULEB128(Sample.getCallTargets().size(), OS); - for (SampleRecord::CallTargetList::const_iterator - I = Sample.getCallTargets().begin(), - E = Sample.getCallTargets().end(); - I != E; ++I) { - std::string Callee = (*I).first; - unsigned CalleeSamples = (*I).second; + for (const auto &J : Sample.getCallTargets()) { + std::string Callee = J.first(); + unsigned CalleeSamples = J.second; OS << Callee; encodeULEB128(0, OS); encodeULEB128(CalleeSamples, OS); @@ -108,3 +97,28 @@ bool SampleProfileWriterBinary::write(const Function &F, return true; } + +/// \brief Create a sample profile writer based on the specified format. +/// +/// \param Filename The file to create. +/// +/// \param Writer The writer to instantiate according to the specified format. +/// +/// \param Format Encoding format for the profile file. +/// +/// \returns an error code indicating the status of the created writer. +std::error_code +SampleProfileWriter::create(StringRef Filename, + std::unique_ptr &Writer, + SampleProfileFormat Format) { + std::error_code EC; + + if (Format == SPF_Binary) + Writer.reset(new SampleProfileWriterBinary(Filename, EC)); + else if (Format == SPF_Text) + Writer.reset(new SampleProfileWriterText(Filename, EC)); + else + EC = sampleprof_error::unrecognized_format; + + return EC; +} diff --git a/test/tools/llvm-profdata/Inputs/sample-profile.proftext b/test/tools/llvm-profdata/Inputs/sample-profile.proftext new file mode 100644 index 00000000000..9dc6d4310da --- /dev/null +++ b/test/tools/llvm-profdata/Inputs/sample-profile.proftext @@ -0,0 +1,12 @@ +_Z3bari:20301:1437 +1: 1437 +_Z3fooi:7711:610 +1: 610 +main:184019:0 +4: 534 +4.2: 534 +5: 1075 +5.1: 1075 +6: 2080 +7: 534 +9: 2064 _Z3bari:1471 _Z3fooi:631 diff --git a/test/tools/llvm-profdata/sample-profile-basic.test b/test/tools/llvm-profdata/sample-profile-basic.test new file mode 100644 index 00000000000..0651c513e96 --- /dev/null +++ b/test/tools/llvm-profdata/sample-profile-basic.test @@ -0,0 +1,30 @@ +Basic tests for sample profiles. + +1- Show all functions +RUN: llvm-profdata show --sample %p/Inputs/sample-profile.proftext | FileCheck %s --check-prefix=SHOW1 +SHOW1: Function: main: 184019, 0, 7 sampled lines +SHOW1: line offset: 9, discriminator: 0, number of samples: 2064, calls: _Z3fooi:631 _Z3bari:1471 +SHOW1: Function: _Z3fooi: 7711, 610, 1 sampled lines +SHOW1: Function: _Z3bari: 20301, 1437, 1 sampled lines +SHOW1: line offset: 1, discriminator: 0, number of samples: 1437 + +2- Show only bar +RUN: llvm-profdata show --sample --function=_Z3bari %p/Inputs/sample-profile.proftext | FileCheck %s --check-prefix=SHOW2 +SHOW2: Function: _Z3bari: 20301, 1437, 1 sampled lines +SHOW2: line offset: 1, discriminator: 0, number of samples: 1437 +SHOW2-NOT: Function: main: 184019, 0, 7 sampled lines +SHOW2-NOT: Function: _Z3fooi: 7711, 610, 1 sampled lines + +3- Convert the profile to binary encoding and check that they are both + identical. +RUN: llvm-profdata merge --sample %p/Inputs/sample-profile.proftext --binary -o - | llvm-profdata show --sample - -o %t-binary +RUN: llvm-profdata show --sample %p/Inputs/sample-profile.proftext -o %t-text +RUN: diff %t-binary %t-text + +4- Merge the binary and text encodings of the profile and check that the + counters have doubled. +RUN: llvm-profdata merge --sample %p/Inputs/sample-profile.proftext -o %t-binprof +RUN: llvm-profdata merge --sample --text %p/Inputs/sample-profile.proftext %t-binprof -o - | FileCheck %s --check-prefix=MERGE1 +MERGE1: main:368038:0 +MERGE1: 9: 4128 _Z3fooi:1262 _Z3bari:2942 +MERGE1: _Z3fooi:15422:1220 diff --git a/tools/llvm-profdata/llvm-profdata.cpp b/tools/llvm-profdata/llvm-profdata.cpp index 3229d4da5d6..22acedc569a 100644 --- a/tools/llvm-profdata/llvm-profdata.cpp +++ b/tools/llvm-profdata/llvm-profdata.cpp @@ -14,6 +14,9 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ProfileData/InstrProfReader.h" #include "llvm/ProfileData/InstrProfWriter.h" +#include "llvm/ProfileData/SampleProfReader.h" +#include "llvm/ProfileData/SampleProfWriter.h" +#include "llvm/IR/LLVMContext.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" @@ -33,18 +36,9 @@ static void exitWithError(const Twine &Message, StringRef Whence = "") { ::exit(1); } -int merge_main(int argc, const char *argv[]) { - cl::list Inputs(cl::Positional, cl::Required, cl::OneOrMore, - cl::desc("")); - - cl::opt OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::Required, - cl::desc("Output file")); - cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), - cl::aliasopt(OutputFilename)); - - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); +enum ProfileKinds { instr, sample }; +void mergeInstrProfile(cl::list Inputs, StringRef OutputFilename) { if (OutputFilename.compare("-") == 0) exitWithError("Cannot write indexed profdata format to stdout."); @@ -67,50 +61,84 @@ int merge_main(int argc, const char *argv[]) { exitWithError(Reader->getError().message(), Filename); } Writer.write(Output); +} + +void mergeSampleProfile(cl::list Inputs, StringRef OutputFilename, + sampleprof::SampleProfileFormat OutputFormat) { + using namespace sampleprof; + std::unique_ptr Writer; + if (std::error_code EC = SampleProfileWriter::create(OutputFilename.data(), + Writer, OutputFormat)) + exitWithError(EC.message(), OutputFilename); + + StringMap ProfileMap; + for (const auto &Filename : Inputs) { + std::unique_ptr Reader; + if (std::error_code EC = + SampleProfileReader::create(Filename, Reader, getGlobalContext())) + exitWithError(EC.message(), Filename); + + if (std::error_code EC = Reader->read()) + exitWithError(EC.message(), Filename); + + StringMap &Profiles = Reader->getProfiles(); + for (StringMap::iterator I = Profiles.begin(), + E = Profiles.end(); + I != E; ++I) { + StringRef FName = I->first(); + FunctionSamples &Samples = I->second; + ProfileMap[FName].merge(Samples); + } + } + Writer->write(ProfileMap); +} + +int merge_main(int argc, const char *argv[]) { + cl::list Inputs(cl::Positional, cl::Required, cl::OneOrMore, + cl::desc("")); + + cl::opt OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::Required, + cl::desc("Output file")); + cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), + cl::aliasopt(OutputFilename)); + cl::opt ProfileKind( + cl::desc("Profile kind:"), cl::init(instr), + cl::values(clEnumVal(instr, "Instrumentation profile (default)"), + clEnumVal(sample, "Sample profile"), clEnumValEnd)); + + cl::opt OutputFormat( + cl::desc("Format of output profile (only meaningful with --sample)"), + cl::init(sampleprof::SPF_Binary), + cl::values(clEnumValN(sampleprof::SPF_Binary, "binary", + "Binary encoding (default)"), + clEnumValN(sampleprof::SPF_Text, "text", "Text encoding"), + clEnumValN(sampleprof::SPF_GCC, "gcc", "GCC encoding"), + clEnumValEnd)); + + cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); + + if (ProfileKind == instr) + mergeInstrProfile(Inputs, OutputFilename); + else + mergeSampleProfile(Inputs, OutputFilename, OutputFormat); return 0; } -int show_main(int argc, const char *argv[]) { - cl::opt Filename(cl::Positional, cl::Required, - cl::desc("")); - - cl::opt ShowCounts("counts", cl::init(false), - cl::desc("Show counter values for shown functions")); - cl::opt ShowAllFunctions("all-functions", cl::init(false), - cl::desc("Details for every function")); - cl::opt ShowFunction("function", - cl::desc("Details for matching functions")); - - cl::opt OutputFilename("output", cl::value_desc("output"), - cl::init("-"), - cl::desc("Output file")); - cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), - cl::aliasopt(OutputFilename)); - - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); - +int showInstrProfile(std::string Filename, bool ShowCounts, + bool ShowAllFunctions, std::string ShowFunction, + raw_fd_ostream &OS) { std::unique_ptr Reader; if (std::error_code EC = InstrProfReader::create(Filename, Reader)) exitWithError(EC.message(), Filename); - if (OutputFilename.empty()) - OutputFilename = "-"; - - std::error_code EC; - raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text); - if (EC) - exitWithError(EC.message(), OutputFilename); - - if (ShowAllFunctions && !ShowFunction.empty()) - errs() << "warning: -function argument ignored: showing all functions\n"; - uint64_t MaxFunctionCount = 0, MaxBlockCount = 0; size_t ShownFunctions = 0, TotalFunctions = 0; for (const auto &Func : *Reader) { - bool Show = ShowAllFunctions || - (!ShowFunction.empty() && - Func.Name.find(ShowFunction) != Func.Name.npos); + bool Show = + ShowAllFunctions || (!ShowFunction.empty() && + Func.Name.find(ShowFunction) != Func.Name.npos); ++TotalFunctions; assert(Func.Counts.size() > 0 && "function missing entry counter"); @@ -150,6 +178,65 @@ int show_main(int argc, const char *argv[]) { return 0; } +int showSampleProfile(std::string Filename, bool ShowCounts, + bool ShowAllFunctions, std::string ShowFunction, + raw_fd_ostream &OS) { + using namespace sampleprof; + std::unique_ptr Reader; + if (std::error_code EC = + SampleProfileReader::create(Filename, Reader, getGlobalContext())) + exitWithError(EC.message(), Filename); + + Reader->read(); + if (ShowAllFunctions || ShowFunction.empty()) + Reader->dump(OS); + else + Reader->dumpFunctionProfile(ShowFunction, OS); + + return 0; +} + +int show_main(int argc, const char *argv[]) { + cl::opt Filename(cl::Positional, cl::Required, + cl::desc("")); + + cl::opt ShowCounts("counts", cl::init(false), + cl::desc("Show counter values for shown functions")); + cl::opt ShowAllFunctions("all-functions", cl::init(false), + cl::desc("Details for every function")); + cl::opt ShowFunction("function", + cl::desc("Details for matching functions")); + + cl::opt OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::desc("Output file")); + cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), + cl::aliasopt(OutputFilename)); + cl::opt ProfileKind( + cl::desc("Profile kind:"), cl::init(instr), + cl::values(clEnumVal(instr, "Instrumentation profile (default)"), + clEnumVal(sample, "Sample profile"), clEnumValEnd)); + + cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); + + if (OutputFilename.empty()) + OutputFilename = "-"; + + std::error_code EC; + raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text); + if (EC) + exitWithError(EC.message(), OutputFilename); + + if (ShowAllFunctions && !ShowFunction.empty()) + errs() << "warning: -function argument ignored: showing all functions\n"; + + if (ProfileKind == instr) + return showInstrProfile(Filename, ShowCounts, ShowAllFunctions, + ShowFunction, OS); + else + return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, + ShowFunction, OS); +} + int main(int argc, const char *argv[]) { // Print a stack trace if we signal out. sys::PrintStackTraceOnErrorSignal();