[PATCH] Add show and merge tools for sample PGO profiles.
Justin Bogner
mail at justinbogner.com
Fri Oct 31 14:43:51 PDT 2014
Diego Novillo <dnovillo at google.com> writes:
> Hi bogner,
>
> 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.
LGTM with some nitpicks.
> http://reviews.llvm.org/D6065
>
> Files:
> include/llvm/ProfileData/SampleProf.h
> include/llvm/ProfileData/SampleProfReader.h
> include/llvm/ProfileData/SampleProfWriter.h
> lib/ProfileData/SampleProf.cpp
> lib/ProfileData/SampleProfReader.cpp
> lib/ProfileData/SampleProfWriter.cpp
> test/tools/llvm-profdata/Inputs/sample-profile.proftext
> test/tools/llvm-profdata/sample-profile-basic.test
> tools/llvm-profdata/llvm-profdata.cpp
>
> Index: include/llvm/ProfileData/SampleProf.h
> ===================================================================
> --- include/llvm/ProfileData/SampleProf.h
> +++ 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 <system_error>
> @@ -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,51 @@
> /// will be a list of one or more functions.
> class SampleRecord {
> public:
> - typedef SmallVector<std::pair<std::string, unsigned>, 8> CallTargetList;
> + typedef StringMap<unsigned> 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<unsigned>::max() - S)
> + NumSamples += S;
> + else
> + NumSamples = std::numeric_limits<unsigned>::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<unsigned>::max() - S)
> + TargetSamples += S;
> + else
> + TargetSamples = std::numeric_limits<unsigned>::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 (CallTargetMap::const_iterator I = Other.getCallTargets().begin(),
> + E = Other.getCallTargets().end();
> + I != E; ++I)
I guess you can use range-for here. If not, I'd probably just use "auto"
for the iterator type.
> + addCalledTarget(I->first(), I->second);
> + }
>
> private:
> unsigned NumSamples;
> - CallTargetList CallTargets;
> + CallTargetMap CallTargets;
> };
>
> typedef DenseMap<LineLocation, SampleRecord> BodySampleMap;
> @@ -143,7 +170,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 +190,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 +214,19 @@
> /// \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 (BodySampleMap::const_iterator I = Other.getBodySamples().begin(),
> + E = Other.getBodySamples().end();
> + I != E; ++I) {
Same here.
> + 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: include/llvm/ProfileData/SampleProfReader.h
> ===================================================================
> --- include/llvm/ProfileData/SampleProfReader.h
> +++ 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,34 +65,34 @@
>
> virtual ~SampleProfileReader() {}
>
> - /// \brief Print all the profiles to dbgs().
> - void dump();
> -
> /// \brief Read and validate the file header.
> virtual std::error_code readHeader() = 0;
>
> /// \brief Read sample profiles from the associated file.
> 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<FunctionSamples> &getProfiles() { return Profiles; }
> +
> /// \brief Report a parse error message.
> void reportParseError(int64_t LineNumber, Twine Msg) const {
> Ctx.diagnose(DiagnosticInfoSampleProfile(Buffer->getBufferIdentifier(),
> LineNumber, Msg));
> }
>
> /// \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<SampleProfileReader> &Reader,
> LLVMContext &C);
>
> Index: include/llvm/ProfileData/SampleProfWriter.h
> ===================================================================
> --- include/llvm/ProfileData/SampleProfWriter.h
> +++ include/llvm/ProfileData/SampleProfWriter.h
> @@ -24,29 +24,59 @@
>
> namespace sampleprof {
>
> +enum SampleProfileFormat { SPF_None = 0, SPF_Text, SPF_Binary, SPF_GCC };
> +
> /// \brief Sample-based profile writer. Base class.
> class SampleProfileWriter {
> public:
> SampleProfileWriter(StringRef Filename, std::error_code &EC,
> sys::fs::OpenFlags Flags)
> : 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<FunctionSamples> &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<FunctionSamples> &ProfileMap) {
> + for (StringMap<FunctionSamples>::iterator I = ProfileMap.begin(),
> + E = ProfileMap.end();
> + I != E; ++I) {
...and again.
> + 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<SampleProfileWriter> &Result,
> + SampleProfileFormat Format);
This create idiom that passes in a "unique_ptr<> &" is really weird, but
I see you got it from code that I wrote, so I won't make you clean it up
right now. I'll likely follow up soon with something that makes these
return an ErrorOr<unique_ptr> or something like that instead.
> +
> protected:
> /// \brief Output stream where to emit the profile to.
> raw_fd_ostream OS;
> @@ -58,7 +88,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<FunctionSamples> &P) {
> return SampleProfileWriter::write(M, P);
> }
> @@ -69,7 +99,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<FunctionSamples> &P) {
> return SampleProfileWriter::write(M, P);
> }
> Index: lib/ProfileData/SampleProf.cpp
> ===================================================================
> --- lib/ProfileData/SampleProf.cpp
> +++ 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: lib/ProfileData/SampleProfReader.cpp
> ===================================================================
> --- lib/ProfileData/SampleProfReader.cpp
> +++ 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"
> @@ -116,46 +115,39 @@
> SE = BodySamples.end();
> SI != SE; ++SI) {
> LineLocation Loc = SI->first;
> - SampleRecord Sample = SI->second;
> + 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
> + for (SampleRecord::CallTargetMap::const_iterator
> I = Sample.getCallTargets().begin(),
> E = Sample.getCallTargets().end();
> I != E; ++I)
Another case for auto. This one wouldn't have needed to be changed if it
was already auto :) There are a couple more of these below.
> - OS << " " << (*I).first << ":" << (*I).second;
> + 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() {
> +/// \brief Dump all the function profiles found on stream \p OS.
> +void SampleProfileReader::dump(raw_ostream &OS) {
> for (StringMap<FunctionSamples>::const_iterator I = Profiles.begin(),
> E = Profiles.end();
> I != E; ++I)
> - dumpFunctionProfile(I->getKey());
> + dumpFunctionProfile(I->getKey(), OS);
> }
>
> /// \brief Load samples from a text file.
> @@ -396,7 +388,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<SampleProfileReader> &Reader,
> LLVMContext &C) {
> std::unique_ptr<MemoryBuffer> Buffer;
> Index: lib/ProfileData/SampleProfWriter.cpp
> ===================================================================
> --- lib/ProfileData/SampleProfWriter.cpp
> +++ lib/ProfileData/SampleProfWriter.cpp
> @@ -30,31 +30,31 @@
> using namespace llvm;
>
> /// \brief Write samples to a text file.
> -bool SampleProfileWriterText::write(const Function &F,
> +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;
> + const SampleRecord &Sample = I->second;
> if (Loc.Discriminator == 0)
> OS << Loc.LineOffset << ": ";
> else
> OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
>
> OS << Sample.getSamples();
>
> - for (SampleRecord::CallTargetList::const_iterator
> + for (SampleRecord::CallTargetMap::const_iterator
> I = Sample.getCallTargets().begin(),
> E = Sample.getCallTargets().end();
> I != E; ++I)
> - OS << " " << (*I).first << ":" << (*I).second;
> + OS << " " << I->first() << ":" << I->second;
> OS << "\n";
> }
>
> @@ -75,36 +75,61 @@
> /// \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;
> + 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
> + for (SampleRecord::CallTargetMap::const_iterator
> I = Sample.getCallTargets().begin(),
> E = Sample.getCallTargets().end();
> I != E; ++I) {
> - std::string Callee = (*I).first;
> - unsigned CalleeSamples = (*I).second;
> + std::string Callee = I->first();
> + unsigned CalleeSamples = I->second;
> OS << Callee;
> encodeULEB128(0, OS);
> encodeULEB128(CalleeSamples, OS);
> }
> }
>
> 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<SampleProfileWriter> &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: test/tools/llvm-profdata/Inputs/sample-profile.proftext
> ===================================================================
> --- /dev/null
> +++ 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: test/tools/llvm-profdata/sample-profile-basic.test
> ===================================================================
> --- /dev/null
> +++ 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: tools/llvm-profdata/llvm-profdata.cpp
> ===================================================================
> --- tools/llvm-profdata/llvm-profdata.cpp
> +++ 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<std::string> Inputs(cl::Positional, cl::Required, cl::OneOrMore,
> - cl::desc("<filenames...>"));
> -
> - cl::opt<std::string> 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<std::string> 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<std::string> Filename(cl::Positional, cl::Required,
> - cl::desc("<profdata-file>"));
> +void mergeSampleProfile(cl::list<std::string> Inputs, StringRef OutputFilename,
> + sampleprof::SampleProfileFormat OutputFormat) {
> + using namespace sampleprof;
> + std::unique_ptr<SampleProfileWriter> Writer;
> + if (std::error_code EC = SampleProfileWriter::create(OutputFilename.data(),
> + Writer, OutputFormat))
> + exitWithError(EC.message(), OutputFilename);
>
> - cl::opt<bool> ShowCounts("counts", cl::init(false),
> - cl::desc("Show counter values for shown functions"));
> - cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
> - cl::desc("Details for every function"));
> - cl::opt<std::string> ShowFunction("function",
> - cl::desc("Details for matching functions"));
> + StringMap<FunctionSamples> ProfileMap;
> + for (const auto &Filename : Inputs) {
> + std::unique_ptr<SampleProfileReader> 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<FunctionSamples> &Profiles = Reader->getProfiles();
> + for (StringMap<FunctionSamples>::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<std::string> Inputs(cl::Positional, cl::Required, cl::OneOrMore,
> + cl::desc("<filenames...>"));
>
> cl::opt<std::string> 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<ProfileKinds> ProfileKind(
> + cl::desc("Profile kind:"), cl::init(instr),
> + cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
> + clEnumVal(sample, "Sample profile"), clEnumValEnd));
> +
> + cl::opt<sampleprof::SampleProfileFormat> 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<InstrProfReader> 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<SampleProfileReader> Reader;
> + if (std::error_code EC =
> + SampleProfileReader::create(Filename, Reader, getGlobalContext()))
> + exitWithError(EC.message(), Filename);
> +
> + Reader->read();
> + if (!ShowFunction.empty())
> + Reader->dumpFunctionProfile(ShowFunction, OS);
> + else
> + Reader->dump(OS);
I guess this should be:
if (ShowAllFunctions || ShowFunction.empty())
Reader->dump(OS);
else
Reader->dumpFunctionProfile(ShowFunction, OS);
Otherwise the "-function argument ignored" below is lying.
> +
> + return 0;
> +}
> +
> +int show_main(int argc, const char *argv[]) {
> + cl::opt<std::string> Filename(cl::Positional, cl::Required,
> + cl::desc("<profdata-file>"));
> +
> + cl::opt<bool> ShowCounts("counts", cl::init(false),
> + cl::desc("Show counter values for shown functions"));
> + cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
> + cl::desc("Details for every function"));
> + cl::opt<std::string> ShowFunction("function",
> + cl::desc("Details for matching functions"));
> +
> + cl::opt<std::string> 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<ProfileKinds> 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();
More information about the llvm-commits
mailing list