[llvm] 6da9241 - [llvm-profgen] Refactor PerfReader to allow different types of perf scripts

via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 2 17:20:07 PDT 2021


Author: wlei
Date: 2021-08-02T17:18:47-07:00
New Revision: 6da9241aabe12c07fb905e5b3d2e4e16ef7d4296

URL: https://github.com/llvm/llvm-project/commit/6da9241aabe12c07fb905e5b3d2e4e16ef7d4296
DIFF: https://github.com/llvm/llvm-project/commit/6da9241aabe12c07fb905e5b3d2e4e16ef7d4296.diff

LOG: [llvm-profgen] Refactor PerfReader to allow different types of perf scripts

In order to support different types of perf scripts, this change tried to refactor `PerfReader` by adding the base class `PerfReaderBase` and current HybridPerfReader is derived from it for CS profile generation. Common functions like, passMM2PEvents, extract_lbrs, extract_callstack, etc. can be reused.

Next step is to add LBR only reader(for non-CS profile) and aggregated perf scripts reader(do a pre-aggregation of scripts).

Reviewed By: hoy, wenlei

Differential Revision: https://reviews.llvm.org/D107014

Added: 
    

Modified: 
    llvm/tools/llvm-profgen/PerfReader.cpp
    llvm/tools/llvm-profgen/PerfReader.h
    llvm/tools/llvm-profgen/ProfiledBinary.h
    llvm/tools/llvm-profgen/llvm-profgen.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/tools/llvm-profgen/PerfReader.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp
index f6b394e7966fd..d95ce49d0ab98 100644
--- a/llvm/tools/llvm-profgen/PerfReader.cpp
+++ b/llvm/tools/llvm-profgen/PerfReader.cpp
@@ -241,7 +241,7 @@ bool VirtualUnwinder::unwind(const HybridSample *Sample, uint64_t Repeat) {
   return true;
 }
 
-void PerfReader::validateCommandLine(
+void PerfReaderBase::validateCommandLine(
     cl::list<std::string> &BinaryFilenames,
     cl::list<std::string> &PerfTraceFilenames) {
   // Allow the invalid perfscript if we only use to show binary disassembly
@@ -276,16 +276,33 @@ void PerfReader::validateCommandLine(
   }
 }
 
-PerfReader::PerfReader(cl::list<std::string> &BinaryFilenames,
+std::unique_ptr<PerfReaderBase>
+PerfReaderBase::create(cl::list<std::string> &BinaryFilenames,
                        cl::list<std::string> &PerfTraceFilenames) {
   validateCommandLine(BinaryFilenames, PerfTraceFilenames);
+
+  PerfScriptType PerfType = extractPerfType(PerfTraceFilenames);
+  std::unique_ptr<PerfReaderBase> PerfReader;
+  if (PerfType == PERF_LBR_STACK) {
+    PerfReader.reset(new HybridPerfReader(BinaryFilenames));
+  } else if (PerfType == PERF_LBR) {
+    // TODO:
+    exitWithError("Unsupported perfscript!");
+  } else {
+    exitWithError("Unsupported perfscript!");
+  }
+
+  return PerfReader;
+}
+
+PerfReaderBase::PerfReaderBase(cl::list<std::string> &BinaryFilenames) {
   // Load the binaries.
   for (auto Filename : BinaryFilenames)
     loadBinary(Filename, /*AllowNameConflict*/ false);
 }
 
-ProfiledBinary &PerfReader::loadBinary(const StringRef BinaryPath,
-                                       bool AllowNameConflict) {
+ProfiledBinary &PerfReaderBase::loadBinary(const StringRef BinaryPath,
+                                           bool AllowNameConflict) {
   // The binary table is currently indexed by the binary name not the full
   // binary path. This is because the user-given path may not match the one
   // that was actually executed.
@@ -303,7 +320,7 @@ ProfiledBinary &PerfReader::loadBinary(const StringRef BinaryPath,
   return Ret.first->second;
 }
 
-void PerfReader::updateBinaryAddress(const MMapEvent &Event) {
+void PerfReaderBase::updateBinaryAddress(const MMapEvent &Event) {
   // Load the binary.
   StringRef BinaryPath = Event.BinaryPath;
   StringRef BinaryName = llvm::sys::path::filename(BinaryPath);
@@ -353,7 +370,7 @@ void PerfReader::updateBinaryAddress(const MMapEvent &Event) {
   }
 }
 
-ProfiledBinary *PerfReader::getBinary(uint64_t Address) {
+ProfiledBinary *PerfReaderBase::getBinary(uint64_t Address) {
   auto Iter = AddrToBinaryMap.lower_bound(Address);
   if (Iter == AddrToBinaryMap.end() || Iter->first != Address) {
     if (Iter == AddrToBinaryMap.begin())
@@ -415,7 +432,7 @@ static void printBranchCounter(ContextSampleCounterMap &Counter,
   printSampleCounter(OrderedCounter);
 }
 
-void PerfReader::printUnwinderOutput() {
+void HybridPerfReader::printUnwinderOutput() {
   for (auto I : BinarySampleCounters) {
     const ProfiledBinary *Binary = I.first;
     outs() << "Binary(" << Binary->getName().str() << ")'s Range Counter:\n";
@@ -425,7 +442,7 @@ void PerfReader::printUnwinderOutput() {
   }
 }
 
-void PerfReader::unwindSamples() {
+void HybridPerfReader::unwindSamples() {
   for (const auto &Item : AggregatedSamples) {
     const HybridSample *Sample = dyn_cast<HybridSample>(Item.first.getPtr());
     VirtualUnwinder Unwinder(&BinarySampleCounters[Sample->Binary],
@@ -437,9 +454,9 @@ void PerfReader::unwindSamples() {
     printUnwinderOutput();
 }
 
-bool PerfReader::extractLBRStack(TraceStream &TraceIt,
-                                 SmallVectorImpl<LBREntry> &LBRStack,
-                                 ProfiledBinary *Binary) {
+bool PerfReaderBase::extractLBRStack(TraceStream &TraceIt,
+                                     SmallVectorImpl<LBREntry> &LBRStack,
+                                     ProfiledBinary *Binary) {
   // The raw format of LBR stack is like:
   // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
   //                           ... 0x4005c8/0x4005dc/P/-/-/0
@@ -531,8 +548,8 @@ bool PerfReader::extractLBRStack(TraceStream &TraceIt,
   return !LBRStack.empty();
 }
 
-bool PerfReader::extractCallstack(TraceStream &TraceIt,
-                                  SmallVectorImpl<uint64_t> &CallStack) {
+bool PerfReaderBase::extractCallstack(TraceStream &TraceIt,
+                                      SmallVectorImpl<uint64_t> &CallStack) {
   // The raw format of call stack is like:
   //            4005dc      # leaf frame
   //	          400634
@@ -593,7 +610,7 @@ bool PerfReader::extractCallstack(TraceStream &TraceIt,
          !Binary->addressInPrologEpilog(CallStack.front());
 }
 
-void PerfReader::parseHybridSample(TraceStream &TraceIt) {
+void HybridPerfReader::parseSample(TraceStream &TraceIt) {
   // The raw hybird sample started with call stack in FILO order and followed
   // intermediately by LBR sample
   // e.g.
@@ -631,7 +648,7 @@ void PerfReader::parseHybridSample(TraceStream &TraceIt) {
   }
 }
 
-void PerfReader::parseMMap2Event(TraceStream &TraceIt) {
+void PerfReaderBase::parseMMap2Event(TraceStream &TraceIt) {
   // Parse a line like:
   //  PERF_RECORD_MMAP2 2113428/2113428: [0x7fd4efb57000(0x204000) @ 0
   //  08:04 19532229 3585508847]: r-xp /usr/lib64/libdl-2.17.so
@@ -677,26 +694,23 @@ void PerfReader::parseMMap2Event(TraceStream &TraceIt) {
   TraceIt.advance();
 }
 
-void PerfReader::parseEventOrSample(TraceStream &TraceIt) {
+void PerfReaderBase::parseEventOrSample(TraceStream &TraceIt) {
   if (TraceIt.getCurrentLine().startswith("PERF_RECORD_MMAP2"))
     parseMMap2Event(TraceIt);
-  else if (getPerfScriptType() == PERF_LBR_STACK)
-    parseHybridSample(TraceIt);
-  else {
-    // TODO: parse other type sample
-    TraceIt.advance();
-  }
+  else
+    parseSample(TraceIt);
 }
 
-void PerfReader::parseAndAggregateTrace(StringRef Filename) {
+void PerfReaderBase::parseAndAggregateTrace(StringRef Filename) {
   // Trace line iterator
   TraceStream TraceIt(Filename);
   while (!TraceIt.isAtEoF())
     parseEventOrSample(TraceIt);
 }
 
-void PerfReader::checkAndSetPerfType(
-    cl::list<std::string> &PerfTraceFilenames) {
+PerfScriptType
+PerfReaderBase::extractPerfType(cl::list<std::string> &PerfTraceFilenames) {
+  PerfScriptType PerfType = PERF_UNKNOWN;
   for (auto FileName : PerfTraceFilenames) {
     PerfScriptType Type = checkPerfScriptType(FileName);
     if (Type == PERF_INVALID)
@@ -705,20 +719,13 @@ void PerfReader::checkAndSetPerfType(
       exitWithError("Inconsistent sample among 
diff erent perf scripts");
     PerfType = Type;
   }
+  return PerfType;
 }
 
-void PerfReader::generateRawProfile() {
-  if (getPerfScriptType() == PERF_LBR_STACK) {
-    // Unwind samples if it's hybird sample
-    unwindSamples();
-  } else if (getPerfScriptType() == PERF_LBR) {
-    // TODO: range overlap computation for regular AutoFDO
-  }
-}
+void HybridPerfReader::generateRawProfile() { unwindSamples(); }
 
-void PerfReader::parsePerfTraces(cl::list<std::string> &PerfTraceFilenames) {
-  // Check and set current perfscript type
-  checkAndSetPerfType(PerfTraceFilenames);
+void PerfReaderBase::parsePerfTraces(
+    cl::list<std::string> &PerfTraceFilenames) {
   // Parse perf traces and do aggregation.
   for (auto Filename : PerfTraceFilenames)
     parseAndAggregateTrace(Filename);

diff  --git a/llvm/tools/llvm-profgen/PerfReader.h b/llvm/tools/llvm-profgen/PerfReader.h
index 05bf2d698c528..630d14a0c44c2 100644
--- a/llvm/tools/llvm-profgen/PerfReader.h
+++ b/llvm/tools/llvm-profgen/PerfReader.h
@@ -541,11 +541,13 @@ using BinarySampleCounterMap =
     std::unordered_map<ProfiledBinary *, ContextSampleCounterMap>;
 
 // Load binaries and read perf trace to parse the events and samples
-class PerfReader {
-
+class PerfReaderBase {
 public:
-  PerfReader(cl::list<std::string> &BinaryFilenames,
-             cl::list<std::string> &PerfTraceFilenames);
+  PerfReaderBase(cl::list<std::string> &BinaryFilenames);
+  virtual ~PerfReaderBase() = default;
+  static std::unique_ptr<PerfReaderBase>
+  create(cl::list<std::string> &BinaryFilenames,
+         cl::list<std::string> &PerfTraceFilenames);
 
   // A LBR sample is like:
   // 0x5c6313f/0x5c63170/P/-/-/0  0x5c630e7/0x5c63130/P/-/-/0 ...
@@ -614,10 +616,12 @@ class PerfReader {
     return BinarySampleCounters;
   }
 
-private:
+protected:
   /// Validate the command line input
-  void validateCommandLine(cl::list<std::string> &BinaryFilenames,
-                           cl::list<std::string> &PerfTraceFilenames);
+  static void validateCommandLine(cl::list<std::string> &BinaryFilenames,
+                                  cl::list<std::string> &PerfTraceFilenames);
+  static PerfScriptType
+  extractPerfType(cl::list<std::string> &PerfTraceFilenames);
   /// Parse a single line of a PERF_RECORD_MMAP2 event looking for a
   /// mapping between the binary name and its memory layout.
   ///
@@ -626,8 +630,6 @@ class PerfReader {
   void parseAndAggregateTrace(StringRef Filename);
   // Parse either an MMAP event or a perf sample
   void parseEventOrSample(TraceStream &TraceIt);
-  // Parse the hybrid sample including the call and LBR line
-  void parseHybridSample(TraceStream &TraceIt);
   // Extract call stack from the perf trace lines
   bool extractCallstack(TraceStream &TraceIt,
                         SmallVectorImpl<uint64_t> &CallStack);
@@ -635,13 +637,12 @@ class PerfReader {
   bool extractLBRStack(TraceStream &TraceIt,
                        SmallVectorImpl<LBREntry> &LBRStack,
                        ProfiledBinary *Binary);
-  void checkAndSetPerfType(cl::list<std::string> &PerfTraceFilenames);
+  // Parse one sample from multiple perf lines, override this for 
diff erent
+  // sample type
+  virtual void parseSample(TraceStream &TraceIt) = 0;
   // Post process the profile after trace aggregation, we will do simple range
   // overlap computation for AutoFDO, or unwind for CSSPGO(hybrid sample).
-  void generateRawProfile();
-  // Unwind the hybrid samples after aggregration
-  void unwindSamples();
-  void printUnwinderOutput();
+  virtual void generateRawProfile() = 0;
   // Helper function for looking up binary in AddressBinaryMap
   ProfiledBinary *getBinary(uint64_t Address);
 
@@ -654,6 +655,31 @@ class PerfReader {
   PerfScriptType PerfType = PERF_UNKNOWN;
 };
 
+/*
+  Hybrid perf script includes a group of hybrid samples(LBRs + call stack),
+  which is used to generate CS profile. An example of hybrid sample:
+    4005dc    # call stack leaf
+    400634
+    400684    # call stack root
+    0x4005c8/0x4005dc/P/-/-/0   0x40062f/0x4005b0/P/-/-/0 ...
+          ... 0x4005c8/0x4005dc/P/-/-/0    # LBR Entries
+*/
+class HybridPerfReader : public PerfReaderBase {
+public:
+  HybridPerfReader(cl::list<std::string> &BinaryFilenames)
+      : PerfReaderBase(BinaryFilenames) {
+    PerfType = PERF_LBR_STACK;
+  };
+  // Parse the hybrid sample including the call and LBR line
+  void parseSample(TraceStream &TraceIt) override;
+  void generateRawProfile() override;
+
+private:
+  // Unwind the hybrid samples after aggregration
+  void unwindSamples();
+  void printUnwinderOutput();
+};
+
 } // end namespace sampleprof
 } // end namespace llvm
 

diff  --git a/llvm/tools/llvm-profgen/ProfiledBinary.h b/llvm/tools/llvm-profgen/ProfiledBinary.h
index be1169e41f7e3..5c628470bdabe 100644
--- a/llvm/tools/llvm-profgen/ProfiledBinary.h
+++ b/llvm/tools/llvm-profgen/ProfiledBinary.h
@@ -27,6 +27,7 @@
 #include "llvm/MC/MCTargetOptions.h"
 #include "llvm/Object/ELFObjectFile.h"
 #include "llvm/ProfileData/SampleProf.h"
+#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Path.h"
 #include <list>
 #include <set>
@@ -190,7 +191,7 @@ class ProfiledBinary {
   StringRef getName() const { return llvm::sys::path::filename(Path); }
   uint64_t getBaseAddress() const { return BaseAddress; }
   void setBaseAddress(uint64_t Address) { BaseAddress = Address; }
-  
+
   // Return the preferred load address for the first executable segment.
   uint64_t getPreferredBaseAddress() const { return PreferredTextSegmentAddresses[0]; }
   // Return the file offset for the first executable segment.

diff  --git a/llvm/tools/llvm-profgen/llvm-profgen.cpp b/llvm/tools/llvm-profgen/llvm-profgen.cpp
index 8ea0156aee09e..4045a26209daa 100644
--- a/llvm/tools/llvm-profgen/llvm-profgen.cpp
+++ b/llvm/tools/llvm-profgen/llvm-profgen.cpp
@@ -49,14 +49,20 @@ int main(int argc, const char *argv[]) {
   cl::HideUnrelatedOptions({&ProfGenCategory, &getColorCategory()});
   cl::ParseCommandLineOptions(argc, argv, "llvm SPGO profile generator\n");
 
-  // Load binaries and parse perf events and samples
-  PerfReader Reader(BinaryFilenames, PerfTraceFilenames);
-  if (ShowDisassemblyOnly)
+  if (ShowDisassemblyOnly) {
+    for (auto BinaryPath : BinaryFilenames) {
+      (void)ProfiledBinary(BinaryPath);
+    }
     return EXIT_SUCCESS;
-  Reader.parsePerfTraces(PerfTraceFilenames);
+  }
+
+  // Load binaries and parse perf events and samples
+  std::unique_ptr<PerfReaderBase> Reader =
+      PerfReaderBase::create(BinaryFilenames, PerfTraceFilenames);
+  Reader->parsePerfTraces(PerfTraceFilenames);
 
   std::unique_ptr<ProfileGenerator> Generator = ProfileGenerator::create(
-      Reader.getBinarySampleCounters(), Reader.getPerfScriptType());
+      Reader->getBinarySampleCounters(), Reader->getPerfScriptType());
   Generator->generateProfile();
   Generator->write();
 


        


More information about the llvm-commits mailing list