Index: llvm/trunk/include/llvm/ProfileData/SampleProf.h =================================================================== --- llvm/trunk/include/llvm/ProfileData/SampleProf.h +++ llvm/trunk/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 @@ unsupported_version, too_large, truncated, - malformed + malformed, + unrecognized_format }; inline std::error_code make_error_code(sampleprof_error E) { @@ -110,27 +113,49 @@ /// 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 @@ 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 @@ 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 @@ /// \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. /// Index: llvm/trunk/include/llvm/ProfileData/SampleProfReader.h =================================================================== --- llvm/trunk/include/llvm/ProfileData/SampleProfReader.h +++ llvm/trunk/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 @@ 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 @@ 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 @@ } /// \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); Index: llvm/trunk/include/llvm/ProfileData/SampleProfWriter.h =================================================================== --- llvm/trunk/include/llvm/ProfileData/SampleProfWriter.h +++ llvm/trunk/include/llvm/ProfileData/SampleProfWriter.h @@ -24,6 +24,8 @@ 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 @@ : 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 @@ 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 @@ 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); } Index: llvm/trunk/lib/ProfileData/SampleProf.cpp =================================================================== --- llvm/trunk/lib/ProfileData/SampleProf.cpp +++ llvm/trunk/lib/ProfileData/SampleProf.cpp @@ -36,6 +36,8 @@ 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."); } Index: llvm/trunk/lib/ProfileData/SampleProfReader.cpp =================================================================== --- llvm/trunk/lib/ProfileData/SampleProfReader.cpp +++ llvm/trunk/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 @@ 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 @@ 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 @@ /// /// \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; Index: llvm/trunk/lib/ProfileData/SampleProfWriter.cpp =================================================================== --- llvm/trunk/lib/ProfileData/SampleProfWriter.cpp +++ llvm/trunk/lib/ProfileData/SampleProfWriter.cpp @@ -30,19 +30,16 @@ 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 @@ 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 @@ /// \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 @@ 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; +} Index: llvm/trunk/test/tools/llvm-profdata/Inputs/sample-profile.proftext =================================================================== --- llvm/trunk/test/tools/llvm-profdata/Inputs/sample-profile.proftext +++ llvm/trunk/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 Index: llvm/trunk/test/tools/llvm-profdata/sample-profile-basic.test =================================================================== --- llvm/trunk/test/tools/llvm-profdata/sample-profile-basic.test +++ llvm/trunk/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 Index: llvm/trunk/tools/llvm-profdata/llvm-profdata.cpp =================================================================== --- llvm/trunk/tools/llvm-profdata/llvm-profdata.cpp +++ llvm/trunk/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 @@ ::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 @@ exitWithError(Reader->getError().message(), Filename); } Writer.write(Output); - - return 0; } -int show_main(int argc, const char *argv[]) { - cl::opt Filename(cl::Positional, cl::Required, - cl::desc("")); +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); - 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")); + 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::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 summary\n"); + cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); + + if (ProfileKind == instr) + mergeInstrProfile(Inputs, OutputFilename); + else + mergeSampleProfile(Inputs, OutputFilename, OutputFormat); + + return 0; +} +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 @@ 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();