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

Ádám Kallai via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 21 02:51:36 PDT 2025


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

>From e6eafd9e89f594d3a6ea507e9634011f1fb259bd Mon Sep 17 00:00:00 2001
From: Adam Kallai <kadam at inf.u-szeged.hu>
Date: Tue, 14 Oct 2025 13:13:05 +0200
Subject: [PATCH 1/2] [Bolt] Add a new hidden option to perf2bolt for testing
 purpose

Extend perf2bolt functionality by adding a new option to
read perf-script output in textual format which created by
Linux perf 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.

Bypassing these technical difficulties, that easier to prove
a pre-generated textual profile format.

How should generate this type of profile?

 1) Gather profile by using Linux Perf with '-b' or '--spe' option.

 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
---
 bolt/include/bolt/Profile/DataAggregator.h | 26 +++++++
 bolt/lib/Profile/DataAggregator.cpp        | 89 +++++++++++++++++++++-
 2 files changed, 112 insertions(+), 3 deletions(-)

diff --git a/bolt/include/bolt/Profile/DataAggregator.h b/bolt/include/bolt/Profile/DataAggregator.h
index cb1b87f8d0d65..193f7df1efcde 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 '--spe'.
+  ///
+  /// How the input file 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, detects the type of the events
+  /// and classify them into either mmap or branch events.
+  /// Then it prepares the ParsingBuf based on the classification and
+  /// call the proper functions 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);
   }

>From 6e78b4a29787754a3b5c04f038a5413692207f75 Mon Sep 17 00:00:00 2001
From: Adam Kallai <kadam at inf.u-szeged.hu>
Date: Tue, 21 Oct 2025 11:51:12 +0200
Subject: [PATCH 2/2] Upddate the description

---
 bolt/include/bolt/Profile/DataAggregator.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/bolt/include/bolt/Profile/DataAggregator.h b/bolt/include/bolt/Profile/DataAggregator.h
index 193f7df1efcde..de88a8bb8ad1e 100644
--- a/bolt/include/bolt/Profile/DataAggregator.h
+++ b/bolt/include/bolt/Profile/DataAggregator.h
@@ -448,9 +448,9 @@ class DataAggregator : public DataReader {
   /// 'perf script --show-mmap-events -F pid,brstack --itrace=bl -i perf.data'
   ///
   /// Note:
-  /// The original perf.data should be profiled with '-b' or '--spe'.
+  /// The original perf.data should be profiled with '-b' or 'Arm Spe'.
   ///
-  /// How the input file looks like:
+  /// 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/-}+
@@ -458,10 +458,10 @@ class DataAggregator : public DataReader {
   ///
   /// 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, detects the type of the events
-  /// and classify them into either mmap or branch events.
+  /// 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 parseMMapEvents() or parseBranchEvents().
+  /// call the proper functions like parseMMapEvents() or parseBranchEvents().
   ///
   /// This option is only for testing purposes.
   void parsePerfScriptEvents();



More information about the llvm-commits mailing list