[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