[llvm] [Bolt] Add a new hidden option to perf2bolt for testing purpose (PR #163785)

via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 21 02:56:37 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-bolt

Author: Ádám Kallai (kaadam)

<details>
<summary>Changes</summary>

Extend perf2bolt functionality by adding a new option to read perf-script output in textual format which created by Linux Perf with using 'script' command.

This option helps to add a large Spe test into the 'bolt-tests' repository to cover Arm Spe aggregation.

Why does the test need to have a textual format Spe profile?
- To collect an Arm Spe profile by Linux Perf, it needs to have an arm developer device which has Spe support.
- To decode Spe data, it also needs to have the proper version of Linux Perf.
  - The minimum required version of Linux Perf is v6.15.

To bypass these technical difficulties, that's easier to provide a pre-generated textual profile format.

How should generate this type of profile?

 1) Gather profile by using Linux Perf:
    `$ perf record -e 'arm_spe_0/branch_filter=1/u' -- BINARY`

 2) Generate a textual format profile by using Linux Perf's script command:
    `$ perf script --show-mmap-events -F pid,brstack --itrace=bl  -i perf.data`

---
Full diff: https://github.com/llvm/llvm-project/pull/163785.diff


2 Files Affected:

- (modified) bolt/include/bolt/Profile/DataAggregator.h (+26) 
- (modified) bolt/lib/Profile/DataAggregator.cpp (+86-3) 


``````````diff
diff --git a/bolt/include/bolt/Profile/DataAggregator.h b/bolt/include/bolt/Profile/DataAggregator.h
index cb1b87f8d0d65..de88a8bb8ad1e 100644
--- a/bolt/include/bolt/Profile/DataAggregator.h
+++ b/bolt/include/bolt/Profile/DataAggregator.h
@@ -440,6 +440,32 @@ class DataAggregator : public DataReader {
   /// B 4b196f 4b19e0 2 0
   void parsePreAggregated();
 
+  /// Detect whether the parsed line is an mmap event or not.
+  bool isMMapEvent(StringRef Line);
+
+  /// Coordinate reading and parsing a hybrid perf-script trace created by
+  /// the following Linux perf script command:
+  /// 'perf script --show-mmap-events -F pid,brstack --itrace=bl -i perf.data'
+  ///
+  /// Note:
+  /// The original perf.data should be profiled with '-b' or 'Arm Spe'.
+  ///
+  /// How the output of this command looks like:
+  /// {<name> .* <sec>.<usec>: }PERF_RECORD_MMAP2 <pid>/<tid>: .* <file_name>
+  /// {<name> .* <sec>.<usec>: }PERF_RECORD_MMAP2 <pid>/<tid>: .* <file_name>
+  ///  PID  {FROM/TO/P/-/-/1/COND/-}+
+  ///  PID  {FROM/TO/P/-/-/1/COND/-}+
+  ///
+  /// The hybrid profile means it contains mmap events along with branch events.
+  /// An mmap event might appear among the branch events, therefore
+  /// Bolt will read this hybrid profile, selects the mmap events, the other
+  /// events treat as branch event.
+  /// Then it prepares the ParsingBuf based on the classification and
+  /// call the proper functions like parseMMapEvents() or parseBranchEvents().
+  ///
+  /// This option is only for testing purposes.
+  void parsePerfScriptEvents();
+
   /// Parse the full output of pre-aggregated LBR samples generated by
   /// an external tool.
   std::error_code parsePreAggregatedLBRSamples();
diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp
index c13fa6dbe582b..8a2119480d49b 100644
--- a/bolt/lib/Profile/DataAggregator.cpp
+++ b/bolt/lib/Profile/DataAggregator.cpp
@@ -115,6 +115,12 @@ cl::opt<std::string>
                             "perf-script output in a textual format"),
                    cl::ReallyHidden, cl::init(""), cl::cat(AggregatorCategory));
 
+cl::opt<bool>
+    ReadPerfScript("perfscript",
+                   cl::desc("skip perf and read perf-script trace created by "
+                            "Linux perf tool with script command"),
+                   cl::ReallyHidden, cl::cat(AggregatorCategory));
+
 static cl::opt<bool>
 TimeAggregator("time-aggr",
   cl::desc("time BOLT aggregator"),
@@ -184,7 +190,8 @@ void DataAggregator::start() {
 
   // Don't launch perf for pre-aggregated files or when perf input is specified
   // by the user.
-  if (opts::ReadPreAggregated || !opts::ReadPerfEvents.empty())
+  if (opts::ReadPreAggregated || opts::ReadPerfScript ||
+      !opts::ReadPerfEvents.empty())
     return;
 
   findPerfExecutable();
@@ -226,7 +233,7 @@ void DataAggregator::start() {
 }
 
 void DataAggregator::abort() {
-  if (opts::ReadPreAggregated)
+  if (opts::ReadPreAggregated || opts::ReadPerfScript)
     return;
 
   std::string Error;
@@ -326,7 +333,7 @@ void DataAggregator::processFileBuildID(StringRef FileBuildID) {
 }
 
 bool DataAggregator::checkPerfDataMagic(StringRef FileName) {
-  if (opts::ReadPreAggregated)
+  if (opts::ReadPreAggregated || opts::ReadPerfScript)
     return true;
 
   Expected<sys::fs::file_t> FD = sys::fs::openNativeFileForRead(FileName);
@@ -372,6 +379,80 @@ void DataAggregator::parsePreAggregated() {
   }
 }
 
+bool DataAggregator::isMMapEvent(StringRef Line) {
+  // Short cut to avoid string find is possible.
+  if (Line.empty() || Line.size() < 50)
+    return false;
+
+  // Check that PERF_RECORD_MMAP2 or PERF_RECORD_MMAP appear in the line.
+  return Line.contains("PERF_RECORD_MMAP");
+}
+
+void DataAggregator::parsePerfScriptEvents() {
+  outs() << "PERF2BOLT: parsing a hybrid perf-script events...\n";
+  NamedRegionTimer T("parsePerfScriptEvents", "Parsing perf-script events",
+                     TimerGroupName, TimerGroupDesc, opts::TimeAggregator);
+
+  ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
+      MemoryBuffer::getFileOrSTDIN(Filename);
+  if (std::error_code EC = MB.getError()) {
+    errs() << "PERF2BOLT-ERROR: cannot open " << Filename << ": "
+           << EC.message() << "\n";
+    exit(1);
+  }
+
+  FileBuf = std::move(*MB);
+  ParsingBuf = FileBuf->getBuffer();
+  Col = 0;
+  Line = 1;
+  std::string MMapEvents = "";
+  std::string BranchEvents = "";
+
+  if (!hasData())
+    return;
+
+  while (hasData()) {
+
+    size_t LineEnd = ParsingBuf.find_first_of("\n");
+    if (LineEnd == StringRef::npos) {
+      reportError("expected rest of line");
+      errs() << "Found: " << ParsingBuf << "\n";
+    }
+    StringRef Event = ParsingBuf.substr(0, LineEnd);
+
+    if (isMMapEvent(Event)) {
+      MMapEvents += Event.str();
+      MMapEvents += "\n";
+    } else {
+      BranchEvents += Event.str();
+      BranchEvents += '\n';
+    }
+
+    ParsingBuf = ParsingBuf.drop_front(LineEnd + 1);
+    Col = 0;
+    Line += 1;
+  }
+
+  // Set ParsingBuf for MMapEvents
+  ParsingBuf = StringRef(MMapEvents);
+  Col = 0;
+  Line = 1;
+  if (!ParsingBuf.empty() && parseMMapEvents()) {
+    errs() << "PERF2BOLT: failed to parse mmap events from the perf-script "
+              "file.\n";
+    exit(1);
+  }
+
+  // Set ParsingBuf for BranchEvents
+  ParsingBuf = StringRef(BranchEvents);
+  Col = 0;
+  Line = 1;
+  if (!ParsingBuf.empty() && parseBranchEvents()) {
+    errs() << "PERF2BOLT: failed to parse samples from perf-script file.\n";
+    exit(1);
+  }
+}
+
 void DataAggregator::filterBinaryMMapInfo() {
   if (opts::FilterPID) {
     auto MMapInfoIter = BinaryMMapInfo.find(opts::FilterPID);
@@ -606,6 +687,8 @@ Error DataAggregator::preprocessProfile(BinaryContext &BC) {
 
   if (opts::ReadPreAggregated) {
     parsePreAggregated();
+  } else if (opts::ReadPerfScript) {
+    parsePerfScriptEvents();
   } else {
     parsePerfData(BC);
   }

``````````

</details>


https://github.com/llvm/llvm-project/pull/163785


More information about the llvm-commits mailing list