[llvm] [BOLT] Add pre-parsed perf script support (PR #163785)
Ádám Kallai via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 2 03:18:33 PDT 2026
https://github.com/kaadam updated https://github.com/llvm/llvm-project/pull/163785
>From 454ad7d7b277eabaeb2b926dfbf846445f604783 Mon Sep 17 00:00:00 2001
From: Adam Kallai <kadam at inf.u-szeged.hu>
Date: Mon, 8 Dec 2025 16:47:24 +0100
Subject: [PATCH 01/10] Add support to read and parse pre-aggregated profile
This PR implements the functionality to read and parse a pre-paresed
perf-script profile which was made by Perf2bolt's
'--generate-perf-text-data' option.
It helps to add support for large ARM Spe end-to-end tests.
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) You can use Perf2bolt itself to generate a pre-parsed perf-script profile
in textual format.
$ perf2bolt BINARY -p perf.data -o test.text --spe --generate-perf-script
2) Perf2bolt is able to work with this type of profile:
$ perf2bolt BINARY -o test.fdata -p test.text --spe -perf-script
---
bolt/include/bolt/Profile/DataAggregator.h | 14 +++
bolt/lib/Profile/DataAggregator.cpp | 140 +++++++++++++++++++--
bolt/test/perf2bolt/Inputs/perf_test | Bin 0 -> 142568 bytes
3 files changed, 142 insertions(+), 12 deletions(-)
create mode 100755 bolt/test/perf2bolt/Inputs/perf_test
diff --git a/bolt/include/bolt/Profile/DataAggregator.h b/bolt/include/bolt/Profile/DataAggregator.h
index ac4abd7ab9745..3a50d1d2b255d 100644
--- a/bolt/include/bolt/Profile/DataAggregator.h
+++ b/bolt/include/bolt/Profile/DataAggregator.h
@@ -184,6 +184,8 @@ class DataAggregator : public DataReader {
sys::ProcessInfo PI{};
SmallVector<char, 256> StdoutPath{};
SmallVector<char, 256> StderrPath{};
+ uint64_t Length{0};
+ uint64_t Offset{0};
};
/// Process info for spawned processes
@@ -474,6 +476,18 @@ class DataAggregator : public DataReader {
/// an external tool.
std::error_code parsePreAggregatedLBRSamples();
+ /// Coordinate reading and parsing pre-parsed perf-script trace created by
+ /// Perf2bolt's '--generate-perf-script' option.
+ ///
+ /// Perf2bolt first processes the pre-parsed profile's header to determine
+ /// offset/length pairs for each event. Using this metadata, it opens only
+ /// the specific file slice associated with the required events during
+ /// the parsing phase.
+ void parsePerfTextData(BinaryContext &BC);
+
+ /// Parse the header of the perf text file.
+ std::error_code parsePerfTextFileHeader();
+
/// Dump pre-parsed perf profile data into a single file.
/// The generator relies on the aggregator work to spawn the required
/// perf-script jobs based on the the aggregation type, and merges
diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp
index 8d19c9a9aa77a..604e2bd6938d6 100644
--- a/bolt/lib/Profile/DataAggregator.cpp
+++ b/bolt/lib/Profile/DataAggregator.cpp
@@ -133,11 +133,11 @@ cl::opt<bool> ReadPreAggregated(
"pa", cl::desc("skip perf and read data from a pre-aggregated file format"),
cl::cat(AggregatorCategory));
-cl::opt<std::string>
- ReadPerfEvents("perf-script-events",
- cl::desc("skip perf event collection by supplying a "
- "perf-script output in a textual format"),
- cl::ReallyHidden, cl::init(""), cl::cat(AggregatorCategory));
+cl::opt<bool> ReadPerfTextData(
+ "parse-perf-script",
+ cl::desc("skip perf event collection by reading a "
+ "pre-parsed perf-script output in a textual format"),
+ cl::Hidden, cl::cat(AggregatorCategory));
cl::opt<bool> GeneratePerfTextProfile(
"generate-perf-script",
@@ -262,7 +262,7 @@ 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::ReadPerfTextData)
return;
findPerfExecutable();
@@ -404,7 +404,7 @@ void DataAggregator::processFileBuildID(StringRef FileBuildID) {
}
bool DataAggregator::checkPerfDataMagic(StringRef FileName) {
- if (opts::ReadPreAggregated)
+ if (opts::ReadPreAggregated || opts::ReadPerfTextData)
return true;
Expected<sys::fs::file_t> FD = sys::fs::openNativeFileForRead(FileName);
@@ -466,6 +466,105 @@ void DataAggregator::parsePreAggregated() {
}
}
+std::error_code DataAggregator::parsePerfTextFileHeader() {
+ size_t LineEnd = ParsingBuf.find_first_of("\n");
+ if (LineEnd == StringRef::npos) {
+ reportError("expected rest of line");
+ Diag << "Found: " << ParsingBuf << "\n";
+ return make_error_code(llvm::errc::io_error);
+ }
+ StringRef HeaderLine = ParsingBuf.substr(0, LineEnd);
+ size_t HeaderLineSize = HeaderLine.size() + 1;
+
+ if (!HeaderLine.consume_front(PerfTextMagicStr)) {
+ reportError("expected 'PERFTEXT' magic string");
+ Diag << "Found: " << HeaderLine << "\n";
+ return make_error_code(llvm::errc::io_error);
+ }
+ Col += PerfTextMagicStr.size();
+
+ SmallVector<StringRef, 5> Events;
+ HeaderLine.trim().split(Events, ";", -1, false);
+
+ if (Events.empty()) {
+ reportError("missing events=sizes content");
+ Diag << "Found: " << HeaderLine << "\n";
+ return make_error_code(llvm::errc::io_error);
+ }
+
+ uint64_t Offset = HeaderLineSize;
+ uint64_t Length = 0;
+ for (StringRef EV : Events) {
+ StringRef EventStr, LengthStr;
+ std::tie(EventStr, LengthStr) = EV.split("=");
+
+ PerfProcessInfo *PPI =
+ StringSwitch<PerfProcessInfo *>(EventStr)
+ .Case(PerfProcessInfo::EventNamesStr[PerfProcessType::BUILDIDS],
+ &BuildIDProcessInfo)
+ .Case(PerfProcessInfo::EventNamesStr[PerfProcessType::MAIN_EVENTS],
+ &MainEventsPPI)
+ .Case(PerfProcessInfo::EventNamesStr[PerfProcessType::MEM_EVENTS],
+ &MemEventsPPI)
+ .Case(PerfProcessInfo::EventNamesStr[PerfProcessType::MMAP_EVENTS],
+ &MMapEventsPPI)
+ .Case(PerfProcessInfo::EventNamesStr[PerfProcessType::TASK_EVENTS],
+ &TaskEventsPPI)
+ .Default(nullptr);
+
+ if (!PPI) {
+ reportError("malformed text profile");
+ Diag << "Found: " << EventStr << " in " << HeaderLine << "\n";
+ return make_error_code(llvm::errc::io_error);
+ }
+
+ if (LengthStr.getAsInteger(16, Length)) {
+ reportError("expected hexadecimal number");
+ Diag << "Found: " << LengthStr << " in " << HeaderLine << "\n";
+ return make_error_code(llvm::errc::io_error);
+ }
+ PPI->Offset = Offset;
+ PPI->Length = Length;
+ Offset = Offset + Length;
+ Col += EV.size();
+ }
+
+ ErrorOr<uint64_t> FsRes = getFileSize(Filename);
+ if (std::error_code EC = FsRes.getError())
+ return EC;
+ if (*FsRes != Offset) {
+ reportError("corrupted perf text profile");
+ Diag << "Found: " << *FsRes << " != " << Offset << "\n";
+ return make_error_code(llvm::errc::io_error);
+ }
+ return std::error_code();
+}
+
+void DataAggregator::parsePerfTextData(BinaryContext &BC) {
+ outs() << "PERF2BOLT: parsing a hybrid perf-script events...\n";
+ NamedRegionTimer T("parsePerfTextData", "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);
+ }
+
+ ParsingBuf = (*MB)->getBuffer();
+ Col = 0;
+ Line = 1;
+ if (std::error_code EC = parsePerfTextFileHeader()) {
+ errs() << "PERF2BOLT-ERROR: failed to parse text header" << EC.message()
+ << "\n";
+ exit(1);
+ }
+
+ parsePerfData(BC);
+}
+
Error DataAggregator::generatePerfTextData() {
std::error_code EC;
raw_fd_ostream OutFile(opts::OutputFilename, EC, sys::fs::OpenFlags::OF_None);
@@ -554,10 +653,24 @@ void DataAggregator::filterBinaryMMapInfo() {
int DataAggregator::prepareToParse(StringRef Name, PerfProcessInfo &Process,
PerfProcessErrorCallbackTy Callback) {
- if (!opts::ReadPerfEvents.empty()) {
- outs() << "PERF2BOLT: using pre-processed perf events for '" << Name
- << "' (perf-script-events)\n";
- ParsingBuf = opts::ReadPerfEvents;
+ if (opts::ReadPerfTextData) {
+ if (Process.Length == 0) {
+ errs() << "PERF2BOLT-WARNING: your input profile was generated with "
+ << "parsing " << Process.Type << " event enabled. "
+ << "This data is missing from your pre-parsed profile.\n";
+ }
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
+ MemoryBuffer::getFileSlice(Filename, Process.Length, Process.Offset);
+ if (std::error_code EC = MB.getError()) {
+ errs() << "Cannot open " << Process.Type << ": " << EC.message() << "\n";
+ exit(1);
+ }
+
+ FileBuf = std::move(*MB);
+ ParsingBuf = FileBuf->getBuffer();
+ Col = 0;
+ Line = 1;
return 0;
}
@@ -739,8 +852,11 @@ void DataAggregator::parseInput() {
if (opts::ReadPreAggregated)
parsePreAggregated();
- else
+ } else if (opts::ReadPerfTextData) {
+ parsePerfTextData(BC);
+ else {
parsePerfData();
+ }
}
Error DataAggregator::preprocessProfile(BinaryContext &BC) {
diff --git a/bolt/test/perf2bolt/Inputs/perf_test b/bolt/test/perf2bolt/Inputs/perf_test
new file mode 100755
index 0000000000000000000000000000000000000000..50d930355a5c0d88306f5bb71b091018e8e5910b
GIT binary patch
literal 142568
zcmeI$TWnm#8Nl(`bpinbHX#8MOtOMf8PP23E9Me#E}LwG9n#>ar4l_^@2>4_*Sqds
zsDtV at B@|&?q_nC~E4302JU~^cJWz>;sx=KFAo>uhs-o>nRDl!(t=u*(Zb{>QbIyGB
z$+0cyP3pt{MC&>8&3rR+emT2mcE6Gy*b|S%j2=zqZeweQTbuk)ziSw~DelgU={J4m
zGWom3w3&+}x<+38t~FYYxil@;xqfzZoA<11kCtt&pO(5pTiU$Uj?C4;fLfZCpr_4S
zE3d<?LAzd`mi=xWT88uNH744E4vDs4b}ip at 3)3>1*SaOhYu#^WH%7~pUo)n|m6~UJ
z+xb#%zLc9!%ctFZTB_&4x9fSkvX8n>H;g&p^6uIctaDlE)_0dH)&7%`XV>|!9yZ^;
zUJj;PRwmqlTIzhamWso at -M5zV+e*c9V{+SMU+=cw?qsc!?DWU8|IPNA_U=FAk6`aF
zH-%s44+01vfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*sr
zAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_0
z00IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b@
z2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000Iag
zfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}
z0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*sr
zAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_0
z00IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b@
z2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000Iag
zfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}
z0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*sr
zAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_0
z00IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2p|v>f(<J$TqUu at 3h^6OiQm0S
z{L>P*Im?f at iTVGU<Zs-JN{1HLsj<0ui7`#)3oiD;H=er4NS~?DAa>mnlw4yrhvNRJ
z=aq`XIj>gFR_oq)wpccvH#%M^yNGAJeM5s at zECZU7HjoFb!c!`sZuTsWrs_Jpl#(A
zFE^R>MvCQZsrYcg*g2B5O0w7N9oV;Xm)Du>O!h3EcXj(qNwlr;%XPZOYn%MD_DH<n
z#S{D2UeI6br>+mi(QDS>-VDX#8kF*MC?3ohG at c2?gAs%HTqqu_Ac*(7^;|5~aNPc%
z#jb0*vrpo7O&@EDnIS1Ru8Em3DZ8b7T*@ISpOvyM<*QOYBjp(>H_FQ%v4;<u*knuW
zy7d>YJ1+m3_8w at H*Q at cE`MB+{`{EW`Ps!^UnZv(VcU;l(SbW#z7Y$01#BKY54{4up
z?e at Mln!kT3|F~;!54Y!*+Mk#B3o?Hx+#VkdI at lNo0xJmoxp~XqJAS`!`i;+Krhk_?
z_SW2ip>)S=$7`7<w`b*cU)Xf-+L$@}^Tk8r(>-yMd2;O|wzz%1z9}=awpTi3zBXIG
zWZ`u4)}wZs6Rwp05BU9i4%*`8^O@<n%u9c`C-c&LQzrIG=8g09jWWPL<ZWjMShsL`
zq<QP^rF^@-quaMju4&wID06K4Z=|QmOut=UpPAWyzB_Kt6eVybzp$|I%GyoRFZQ6!
zy*R(kf9H$R=&s<9q)tzrn{Ld{q|Y^_=N~*VVvl6=)<=Dv3)*k_koM*uYKKVv{B+~&
zv8l7BammT_IXmvVm(HZ;zw`do+3EB-nb<CIX}-*{_gY-Plj->u>3%YO)_!oDOwY-+
znXgXG_3vypQ at 71b&CfK>p6Qdzai at bzr^8F9_IhX1bH`?5)9JZ)XFFyy$6juE at PxIH
zxz>sIlzdcJ at A$$gX<K)~EVlVInbaK%J>Tz&n}wTxAb*>;_RE>AkJ;9DPRlgfkMjRR
zz}_$Ru=^Eze6X-k3C at hYqs%5((ZW7A?QwWx at O1V&NqyD^<&cezf3&*tZ*Dw&W?$pt
ziA_PiKI65&_G@>~X??Ehy!P2_k24#B%*%qR(R_WT|MIG!{ZiNdxGVp1KDfRYUHi2z
zuKSnkTwSkA_hP#5vCn3EX#4u0Y6dn3E5F>;$6S5RUH_z;@4Tx&>gw{A at V)i_!KMGt
zi<H~1MJIa7t=kL~%bzb)???;`>`rucCR52&qJ6Yj at 0ZtDV>p?sjNefzJv4r+{j+VN
zTKQZdS5Hb~qPNi3)6+51aoccbZ%3-*_EgvHy<Od1xm at 2!N7ryqXJ0<o)78<NzuDwU
z+45-Op+dD*td!j(c7*@-JbQQTx+BqkXt+_XHxeCP$*yE-TW`a!c7D0DJDKXf*@dGT
zOLhDIWA}(JyBjv)n(Y0z#{6;F=i1eN|ErDecU^aLqnyOe<>q*+-swS$-FMpfY$SfK
ziyOC}4tg7tm6Mpc)@0oKMDL$*KYpdr`&IjYOHS<gx-ZxG({f^Wp?d#o{JSz>EPl<h
z_5V~ltaQDvuM+>2KmK|%>E7=;{%8F0m-~ERJH%U!zW=rVFYE=zuQJj5cE7~q at hcYJ
zf7*Y at k2f2AA8Pz9IoTi%hn=&(Czsf}E`HiM`}<;v&B-evpMtH<S*reVrHp^2xzzNz
z>ssUD35l=t{j*czEh~(3kHix$H(ZRY5`Scs_;V7!Av}OPe^KK0dJnkk(zX5A#U~>1
zpSk#P7x%wk$XKtt`13B_u5%@eW%<TAVUp!ay^!3obKkalcGM*EN6NJ$<0d&;ZY141
z{DO*C4XR_=+E~!LE|F at Xl(nhuY@$?`vr0Z&&n9!Vdbddy#=MbgcD&$?<)ypUOtM~>
zl&-~cQU1yo+M-GNx%Sk{R;$?~L8G3_6xs1&PL@!S1UtSAG4AE;w`Q;Ie{J at oc0tKf
zC6_JvU4ub~Yc*p(|HcbtNtUaW1S!XA^=fbyl=2-tJ6x2}Moq3-uhko}Ys^VdzMOjl
z`|cm|JhSwT-P74?M!%|uLY|kec%!AtaJJ;->y>KF%QhyBEMcNls2B3dlnlE<5Bt^L
zUA`x4tsXIfUA{3sene72HP6xoI<D(Pp*rH#3$?l|`hxZEIk;mm?WOncmJ0}efH3|~
z5?0aGy}bkX?c6co-M44Y{plfZXvfZhwEdCdK55Sv%U+{a$jki8f7A$%5&4C~F3|W_
z<P6;k{>@)L^0kULmM!P)4<r}<43f>~&3LigsG0Fbsa_OflP^A0%uC1J5A5GDxNn!<
z`u|BOZzuWOwV%ns at vJ-1Qui6I+3&08wzT^!yC>3oeJ*L4khbulpHCXI`!M_L(tO=d
zX=#5yu!o()u2c6ry3g7wF}ruseBIw at srw>*%4t6>KOy;cFQj$d4{4c_c6;dj(e>Xc
zZGR2r>wZp4_0#!v{r;fxCoM1Art|B5QOn1*qbsdJ_!yLmU6bbPept&{*Eu at B`hCjf
z?{xiiKdhzpv-$R1*RS!c#O#{%^IV at pT1LIJu4UQf>-z6<&#}c)+QWzDY3xgKV&~U<
zeZFgXN;|qz*RSEn<izIdeqNu)hg*XY-?NROqa}21Proj&aK7%p2Q~-!x{uei==#4I
z$=7|tn9J9F{A&5%j^rP8`{tuAKcR`PjIRHC5^t85-XFSOOSA>+k9uic%jYEbicr4p
z-`YQ7{*NTr+UxwfuS&W6r=7ok at 6vhoI({O#wu|QL at 42V057yr<ZQ(=nHFiQy!uh(t
zYTXj#NB_Uvax~q%7RlFrcEaT^c62Av`G5T(`O*H-`a6+)-LJO0`4etlEu#5<k_H=7
zKi$V?Tz>R=qxJVC*Tz!v3Llzp+rx+EYiW}M>2NP&`nLwrlv~*K(rmwj>-zQcXPxBh
h+QTDE+z{k%UYsasja1EsRq~&>G06XIBq35Y{{#`pM9BaE
literal 0
HcmV?d00001
>From db2afa991d0fe635b6034dd9a748645488ce6731 Mon Sep 17 00:00:00 2001
From: Adam Kallai <kadam at inf.u-szeged.hu>
Date: Tue, 27 Jan 2026 09:56:02 +0100
Subject: [PATCH 02/10] Update PerfSpeEvent unittest
---
bolt/unittests/Profile/PerfSpeEvents.cpp | 30 +++++++++++++-----------
1 file changed, 16 insertions(+), 14 deletions(-)
diff --git a/bolt/unittests/Profile/PerfSpeEvents.cpp b/bolt/unittests/Profile/PerfSpeEvents.cpp
index 4f060cd0aa7c8..b736c38f691c8 100644
--- a/bolt/unittests/Profile/PerfSpeEvents.cpp
+++ b/bolt/unittests/Profile/PerfSpeEvents.cpp
@@ -22,7 +22,7 @@ using namespace llvm::object;
using namespace llvm::ELF;
namespace opts {
-extern cl::opt<std::string> ReadPerfEvents;
+extern cl::opt<bool> ReadPerfTextData;
extern cl::opt<bool> ArmSPE;
} // namespace opts
@@ -92,10 +92,10 @@ struct PerfSpeEventsTestHelper : public testing::Test {
/// Parse and check SPE brstack as LBR.
void parseAndCheckBrstackEvents(
- uint64_t PID,
+ uint64_t PID, StringRef &Buffer,
const std::vector<std::pair<Trace, TakenBranchInfo>> &ExpectedSamples) {
DataAggregator DA("<pseudo input>");
- DA.ParsingBuf = opts::ReadPerfEvents;
+ DA.ParsingBuf = Buffer;
DA.BC = BC.get();
DataAggregator::MMapInfo MMap;
DA.BinaryMMapInfo.insert(std::make_pair(PID, MMap));
@@ -134,14 +134,15 @@ TEST_F(PerfSpeEventsTestHelper, SpeBranchesWithBrstack) {
// ```
opts::ArmSPE = true;
- opts::ReadPerfEvents = " 1234 0xa001/0xa002/PN/-/-/10/COND/-\n"
- " 1234 0xb001/0xb002/P/-/-/4/RET/-\n"
- " 1234 0xc456/0xc789/P/-/-/13/-/-\n"
- " 1234 0xd123/0xd456/M/-/-/7/RET/-\n"
- " 1234 0xe001/0xe002/P/-/-/14/RET/-\n"
- " 1234 0xd123/0xd456/M/-/-/7/RET/-\n"
- " 1234 0xf001/0xf002/MN/-/-/8/COND/-\n"
- " 1234 0xc456/0xc789/M/-/-/13/-/-\n";
+ opts::ReadPerfTextData = true;
+ StringRef Buffer = " 1234 0xa001/0xa002/PN/-/-/10/COND/-\n"
+ " 1234 0xb001/0xb002/P/-/-/4/RET/-\n"
+ " 1234 0xc456/0xc789/P/-/-/13/-/-\n"
+ " 1234 0xd123/0xd456/M/-/-/7/RET/-\n"
+ " 1234 0xe001/0xe002/P/-/-/14/RET/-\n"
+ " 1234 0xd123/0xd456/M/-/-/7/RET/-\n"
+ " 1234 0xf001/0xf002/MN/-/-/8/COND/-\n"
+ " 1234 0xc456/0xc789/M/-/-/13/-/-\n";
// ExpectedSamples contains the aggregated information about
// a branch {{Branch From, To}, {TakenCount, MispredCount}}.
@@ -158,7 +159,7 @@ TEST_F(PerfSpeEventsTestHelper, SpeBranchesWithBrstack) {
{{0xe001, 0xe002, Trace::BR_ONLY}, {1, 0}},
{{0xf001, 0xf002, Trace::BR_ONLY}, {1, 1}}};
- parseAndCheckBrstackEvents(1234, ExpectedSamples);
+ parseAndCheckBrstackEvents(1234, Buffer, ExpectedSamples);
}
TEST_F(PerfSpeEventsTestHelper, SpeBranchesWithBrstackAndPbt) {
@@ -174,7 +175,8 @@ TEST_F(PerfSpeEventsTestHelper, SpeBranchesWithBrstackAndPbt) {
// ```
opts::ArmSPE = true;
- opts::ReadPerfEvents =
+ opts::ReadPerfTextData = true;
+ StringRef Buffer =
// "<PID> <SRC>/<DEST>/PN/-/-/10/COND/- <NULL>/<PBT>/-/-/-/0//-\n"
" 4567 0xa002/0xa003/PN/-/-/10/COND/- 0x0/0xa001/-/-/-/0//-\n"
" 4567 0xb002/0xb003/P/-/-/4/RET/- 0x0/0xb001/-/-/-/0//-\n"
@@ -246,7 +248,7 @@ TEST_F(PerfSpeEventsTestHelper, SpeBranchesWithBrstackAndPbt) {
{{0xf002, 0xf003, Trace::BR_ONLY}, {1, 1}},
{{0x0, 0xf001, 0xf002}, {1, 0}}};
- parseAndCheckBrstackEvents(4567, ExpectedSamples);
+ parseAndCheckBrstackEvents(4567, Buffer, ExpectedSamples);
}
#endif
>From d2a2c19f5200f10eaea52e87775270d99b3f8622 Mon Sep 17 00:00:00 2001
From: Adam Kallai <kadam at inf.u-szeged.hu>
Date: Wed, 25 Mar 2026 14:11:22 +0100
Subject: [PATCH 03/10] Rename function and update cl option
---
bolt/lib/Profile/DataAggregator.cpp | 10 +++++-----
bolt/unittests/Profile/PerfSpeEvents.cpp | 6 +++---
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp
index 604e2bd6938d6..9534b3ac96b08 100644
--- a/bolt/lib/Profile/DataAggregator.cpp
+++ b/bolt/lib/Profile/DataAggregator.cpp
@@ -133,8 +133,8 @@ cl::opt<bool> ReadPreAggregated(
"pa", cl::desc("skip perf and read data from a pre-aggregated file format"),
cl::cat(AggregatorCategory));
-cl::opt<bool> ReadPerfTextData(
- "parse-perf-script",
+cl::opt<bool> ReadPerfTextProfile(
+ "perf-script",
cl::desc("skip perf event collection by reading a "
"pre-parsed perf-script output in a textual format"),
cl::Hidden, cl::cat(AggregatorCategory));
@@ -262,7 +262,7 @@ void DataAggregator::start() {
// Don't launch perf for pre-aggregated files or when perf input is specified
// by the user.
- if (opts::ReadPreAggregated || opts::ReadPerfTextData)
+ if (opts::ReadPreAggregated || opts::ReadPerfTextProfile)
return;
findPerfExecutable();
@@ -404,7 +404,7 @@ void DataAggregator::processFileBuildID(StringRef FileBuildID) {
}
bool DataAggregator::checkPerfDataMagic(StringRef FileName) {
- if (opts::ReadPreAggregated || opts::ReadPerfTextData)
+ if (opts::ReadPreAggregated || opts::ReadPerfTextProfile)
return true;
Expected<sys::fs::file_t> FD = sys::fs::openNativeFileForRead(FileName);
@@ -653,7 +653,7 @@ void DataAggregator::filterBinaryMMapInfo() {
int DataAggregator::prepareToParse(StringRef Name, PerfProcessInfo &Process,
PerfProcessErrorCallbackTy Callback) {
- if (opts::ReadPerfTextData) {
+ if (opts::ReadPerfTextProfile) {
if (Process.Length == 0) {
errs() << "PERF2BOLT-WARNING: your input profile was generated with "
<< "parsing " << Process.Type << " event enabled. "
diff --git a/bolt/unittests/Profile/PerfSpeEvents.cpp b/bolt/unittests/Profile/PerfSpeEvents.cpp
index b736c38f691c8..aafd2ae209b75 100644
--- a/bolt/unittests/Profile/PerfSpeEvents.cpp
+++ b/bolt/unittests/Profile/PerfSpeEvents.cpp
@@ -22,7 +22,7 @@ using namespace llvm::object;
using namespace llvm::ELF;
namespace opts {
-extern cl::opt<bool> ReadPerfTextData;
+extern cl::opt<bool> ReadPerfTextProfile;
extern cl::opt<bool> ArmSPE;
} // namespace opts
@@ -134,7 +134,7 @@ TEST_F(PerfSpeEventsTestHelper, SpeBranchesWithBrstack) {
// ```
opts::ArmSPE = true;
- opts::ReadPerfTextData = true;
+ opts::ReadPerfTextProfile = true;
StringRef Buffer = " 1234 0xa001/0xa002/PN/-/-/10/COND/-\n"
" 1234 0xb001/0xb002/P/-/-/4/RET/-\n"
" 1234 0xc456/0xc789/P/-/-/13/-/-\n"
@@ -175,7 +175,7 @@ TEST_F(PerfSpeEventsTestHelper, SpeBranchesWithBrstackAndPbt) {
// ```
opts::ArmSPE = true;
- opts::ReadPerfTextData = true;
+ opts::ReadPerfTextProfile = true;
StringRef Buffer =
// "<PID> <SRC>/<DEST>/PN/-/-/10/COND/- <NULL>/<PBT>/-/-/-/0//-\n"
" 4567 0xa002/0xa003/PN/-/-/10/COND/- 0x0/0xa001/-/-/-/0//-\n"
>From 2463838a348084938d01319c06860348b1a7104e Mon Sep 17 00:00:00 2001
From: Adam Kallai <kadam at inf.u-szeged.hu>
Date: Fri, 27 Mar 2026 15:19:24 +0100
Subject: [PATCH 04/10] Update MemoryMaps unittests
---
bolt/lib/Profile/DataAggregator.cpp | 35 ++++++++++++++++-------------
bolt/unittests/Core/MemoryMaps.cpp | 34 +++++++++++++++++-----------
2 files changed, 40 insertions(+), 29 deletions(-)
diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp
index 9534b3ac96b08..c5c3f5d3cfaf0 100644
--- a/bolt/lib/Profile/DataAggregator.cpp
+++ b/bolt/lib/Profile/DataAggregator.cpp
@@ -544,24 +544,24 @@ void DataAggregator::parsePerfTextData(BinaryContext &BC) {
outs() << "PERF2BOLT: parsing a hybrid perf-script events...\n";
NamedRegionTimer T("parsePerfTextData", "Parsing perf-script events",
TimerGroupName, TimerGroupDesc, opts::TimeAggregator);
+ if (!Filename.empty()) {
+ 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);
+ }
- 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);
- }
-
- ParsingBuf = (*MB)->getBuffer();
- Col = 0;
- Line = 1;
- if (std::error_code EC = parsePerfTextFileHeader()) {
- errs() << "PERF2BOLT-ERROR: failed to parse text header" << EC.message()
- << "\n";
- exit(1);
+ ParsingBuf = (*MB)->getBuffer();
+ Col = 0;
+ Line = 1;
+ if (std::error_code EC = parsePerfTextFileHeader()) {
+ errs() << "PERF2BOLT-ERROR: failed to parse text header" << EC.message()
+ << "\n";
+ exit(1);
+ }
}
-
parsePerfData(BC);
}
@@ -654,6 +654,9 @@ void DataAggregator::filterBinaryMMapInfo() {
int DataAggregator::prepareToParse(StringRef Name, PerfProcessInfo &Process,
PerfProcessErrorCallbackTy Callback) {
if (opts::ReadPerfTextProfile) {
+ // No profile, ParsingBuf is set directly in unittests.
+ if (Filename.empty())
+ return 0;
if (Process.Length == 0) {
errs() << "PERF2BOLT-WARNING: your input profile was generated with "
<< "parsing " << Process.Type << " event enabled. "
diff --git a/bolt/unittests/Core/MemoryMaps.cpp b/bolt/unittests/Core/MemoryMaps.cpp
index 8eb8f8ae529b1..8391e837a240d 100644
--- a/bolt/unittests/Core/MemoryMaps.cpp
+++ b/bolt/unittests/Core/MemoryMaps.cpp
@@ -21,7 +21,7 @@ using namespace llvm::ELF;
using namespace bolt;
namespace opts {
-extern cl::opt<std::string> ReadPerfEvents;
+extern cl::opt<bool> ReadPerfTextProfile;
} // namespace opts
namespace {
@@ -93,12 +93,15 @@ INSTANTIATE_TEST_SUITE_P(AArch64, MemoryMapsTester,
TEST_P(MemoryMapsTester, ParseMultipleSegments) {
const int Pid = 1234;
StringRef Filename = "BINARY";
- opts::ReadPerfEvents = formatv(
- "name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
- "[0xabc0000000(0x1000000) @ 0x11c0000 103:01 1573523 0]: r-xp {1}\n"
- "name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
- "[0xabc2000000(0x8000000) @ 0x31d0000 103:01 1573523 0]: r-xp {1}\n",
- Pid, Filename);
+ opts::ReadPerfTextProfile = true;
+ std::string MemEvents =
+ formatv(
+ "name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
+ "[0xabc0000000(0x1000000) @ 0x11c0000 103:01 1573523 0]: r-xp {1}\n"
+ "name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
+ "[0xabc2000000(0x8000000) @ 0x31d0000 103:01 1573523 0]: r-xp {1}\n",
+ Pid, Filename)
+ .str();
BC->SegmentMapInfo[0x11da000] = SegmentInfo{
0x11da000, 0x10da000, 0x11ca000, 0x10da000, 0x10000, true, false};
@@ -106,6 +109,7 @@ TEST_P(MemoryMapsTester, ParseMultipleSegments) {
0x31d0000, 0x51ac82c, 0x31d0000, 0x3000000, 0x200000, true, false};
DataAggregator DA("");
+ DA.setParsingBuffer(MemEvents);
BC->setFilename(Filename);
Error Err = DA.preprocessProfile(*BC);
@@ -124,12 +128,15 @@ TEST_P(MemoryMapsTester, ParseMultipleSegments) {
TEST_P(MemoryMapsTester, MultipleSegmentsMismatchedBaseAddress) {
const int Pid = 1234;
StringRef Filename = "BINARY";
- opts::ReadPerfEvents = formatv(
- "name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
- "[0xabc0000000(0x1000000) @ 0x11c0000 103:01 1573523 0]: r-xp {1}\n"
- "name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
- "[0xabc2000000(0x8000000) @ 0x31d0000 103:01 1573523 0]: r-xp {1}\n",
- Pid, Filename);
+ opts::ReadPerfTextProfile = true;
+ std::string MemEvents =
+ formatv(
+ "name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
+ "[0xabc0000000(0x1000000) @ 0x11c0000 103:01 1573523 0]: r-xp {1}\n"
+ "name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
+ "[0xabc2000000(0x8000000) @ 0x31d0000 103:01 1573523 0]: r-xp {1}\n",
+ Pid, Filename)
+ .str();
BC->SegmentMapInfo[0x11da000] = SegmentInfo{
0x11da000, 0x10da000, 0x11ca000, 0x10da000, 0x10000, true, false};
@@ -139,6 +146,7 @@ TEST_P(MemoryMapsTester, MultipleSegmentsMismatchedBaseAddress) {
0x31d0000, 0x51ac82c, 0x31d0fff, 0x3000000, 0x200000, true, false};
DataAggregator DA("");
+ DA.setParsingBuffer(MemEvents);
BC->setFilename(Filename);
ASSERT_DEBUG_DEATH(
{ Error Err = DA.preprocessProfile(*BC); },
>From 51c3d957af9498a5ee51ee50e9b42d881cca0591 Mon Sep 17 00:00:00 2001
From: Adam Kallai <kadam at inf.u-szeged.hu>
Date: Wed, 13 May 2026 14:19:51 +0200
Subject: [PATCH 05/10] Address reviewers
---
bolt/lib/Profile/DataAggregator.cpp | 2 +-
bolt/test/perf2bolt/Inputs/perf_test | Bin 142568 -> 0 bytes
2 files changed, 1 insertion(+), 1 deletion(-)
delete mode 100755 bolt/test/perf2bolt/Inputs/perf_test
diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp
index c5c3f5d3cfaf0..9f59933cfec92 100644
--- a/bolt/lib/Profile/DataAggregator.cpp
+++ b/bolt/lib/Profile/DataAggregator.cpp
@@ -541,7 +541,7 @@ std::error_code DataAggregator::parsePerfTextFileHeader() {
}
void DataAggregator::parsePerfTextData(BinaryContext &BC) {
- outs() << "PERF2BOLT: parsing a hybrid perf-script events...\n";
+ outs() << "PERF2BOLT: parsing a textual perf-script events...\n";
NamedRegionTimer T("parsePerfTextData", "Parsing perf-script events",
TimerGroupName, TimerGroupDesc, opts::TimeAggregator);
if (!Filename.empty()) {
diff --git a/bolt/test/perf2bolt/Inputs/perf_test b/bolt/test/perf2bolt/Inputs/perf_test
deleted file mode 100755
index 50d930355a5c0d88306f5bb71b091018e8e5910b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 142568
zcmeI$TWnm#8Nl(`bpinbHX#8MOtOMf8PP23E9Me#E}LwG9n#>ar4l_^@2>4_*Sqds
zsDtV at B@|&?q_nC~E4302JU~^cJWz>;sx=KFAo>uhs-o>nRDl!(t=u*(Zb{>QbIyGB
z$+0cyP3pt{MC&>8&3rR+emT2mcE6Gy*b|S%j2=zqZeweQTbuk)ziSw~DelgU={J4m
zGWom3w3&+}x<+38t~FYYxil@;xqfzZoA<11kCtt&pO(5pTiU$Uj?C4;fLfZCpr_4S
zE3d<?LAzd`mi=xWT88uNH744E4vDs4b}ip at 3)3>1*SaOhYu#^WH%7~pUo)n|m6~UJ
z+xb#%zLc9!%ctFZTB_&4x9fSkvX8n>H;g&p^6uIctaDlE)_0dH)&7%`XV>|!9yZ^;
zUJj;PRwmqlTIzhamWso at -M5zV+e*c9V{+SMU+=cw?qsc!?DWU8|IPNA_U=FAk6`aF
zH-%s44+01vfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*sr
zAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_0
z00IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b@
z2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000Iag
zfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}
z0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*sr
zAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_0
z00IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b@
z2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000Iag
zfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}
z0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*sr
zAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2q1s}0tg_0
z00IagfB*srAb<b at 2q1s}0tg_000IagfB*srAb<b at 2p|v>f(<J$TqUu at 3h^6OiQm0S
z{L>P*Im?f at iTVGU<Zs-JN{1HLsj<0ui7`#)3oiD;H=er4NS~?DAa>mnlw4yrhvNRJ
z=aq`XIj>gFR_oq)wpccvH#%M^yNGAJeM5s at zECZU7HjoFb!c!`sZuTsWrs_Jpl#(A
zFE^R>MvCQZsrYcg*g2B5O0w7N9oV;Xm)Du>O!h3EcXj(qNwlr;%XPZOYn%MD_DH<n
z#S{D2UeI6br>+mi(QDS>-VDX#8kF*MC?3ohG at c2?gAs%HTqqu_Ac*(7^;|5~aNPc%
z#jb0*vrpo7O&@EDnIS1Ru8Em3DZ8b7T*@ISpOvyM<*QOYBjp(>H_FQ%v4;<u*knuW
zy7d>YJ1+m3_8w at H*Q at cE`MB+{`{EW`Ps!^UnZv(VcU;l(SbW#z7Y$01#BKY54{4up
z?e at Mln!kT3|F~;!54Y!*+Mk#B3o?Hx+#VkdI at lNo0xJmoxp~XqJAS`!`i;+Krhk_?
z_SW2ip>)S=$7`7<w`b*cU)Xf-+L$@}^Tk8r(>-yMd2;O|wzz%1z9}=awpTi3zBXIG
zWZ`u4)}wZs6Rwp05BU9i4%*`8^O@<n%u9c`C-c&LQzrIG=8g09jWWPL<ZWjMShsL`
zq<QP^rF^@-quaMju4&wID06K4Z=|QmOut=UpPAWyzB_Kt6eVybzp$|I%GyoRFZQ6!
zy*R(kf9H$R=&s<9q)tzrn{Ld{q|Y^_=N~*VVvl6=)<=Dv3)*k_koM*uYKKVv{B+~&
zv8l7BammT_IXmvVm(HZ;zw`do+3EB-nb<CIX}-*{_gY-Plj->u>3%YO)_!oDOwY-+
znXgXG_3vypQ at 71b&CfK>p6Qdzai at bzr^8F9_IhX1bH`?5)9JZ)XFFyy$6juE at PxIH
zxz>sIlzdcJ at A$$gX<K)~EVlVInbaK%J>Tz&n}wTxAb*>;_RE>AkJ;9DPRlgfkMjRR
zz}_$Ru=^Eze6X-k3C at hYqs%5((ZW7A?QwWx at O1V&NqyD^<&cezf3&*tZ*Dw&W?$pt
ziA_PiKI65&_G@>~X??Ehy!P2_k24#B%*%qR(R_WT|MIG!{ZiNdxGVp1KDfRYUHi2z
zuKSnkTwSkA_hP#5vCn3EX#4u0Y6dn3E5F>;$6S5RUH_z;@4Tx&>gw{A at V)i_!KMGt
zi<H~1MJIa7t=kL~%bzb)???;`>`rucCR52&qJ6Yj at 0ZtDV>p?sjNefzJv4r+{j+VN
zTKQZdS5Hb~qPNi3)6+51aoccbZ%3-*_EgvHy<Od1xm at 2!N7ryqXJ0<o)78<NzuDwU
z+45-Op+dD*td!j(c7*@-JbQQTx+BqkXt+_XHxeCP$*yE-TW`a!c7D0DJDKXf*@dGT
zOLhDIWA}(JyBjv)n(Y0z#{6;F=i1eN|ErDecU^aLqnyOe<>q*+-swS$-FMpfY$SfK
ziyOC}4tg7tm6Mpc)@0oKMDL$*KYpdr`&IjYOHS<gx-ZxG({f^Wp?d#o{JSz>EPl<h
z_5V~ltaQDvuM+>2KmK|%>E7=;{%8F0m-~ERJH%U!zW=rVFYE=zuQJj5cE7~q at hcYJ
zf7*Y at k2f2AA8Pz9IoTi%hn=&(Czsf}E`HiM`}<;v&B-evpMtH<S*reVrHp^2xzzNz
z>ssUD35l=t{j*czEh~(3kHix$H(ZRY5`Scs_;V7!Av}OPe^KK0dJnkk(zX5A#U~>1
zpSk#P7x%wk$XKtt`13B_u5%@eW%<TAVUp!ay^!3obKkalcGM*EN6NJ$<0d&;ZY141
z{DO*C4XR_=+E~!LE|F at Xl(nhuY@$?`vr0Z&&n9!Vdbddy#=MbgcD&$?<)ypUOtM~>
zl&-~cQU1yo+M-GNx%Sk{R;$?~L8G3_6xs1&PL@!S1UtSAG4AE;w`Q;Ie{J at oc0tKf
zC6_JvU4ub~Yc*p(|HcbtNtUaW1S!XA^=fbyl=2-tJ6x2}Moq3-uhko}Ys^VdzMOjl
z`|cm|JhSwT-P74?M!%|uLY|kec%!AtaJJ;->y>KF%QhyBEMcNls2B3dlnlE<5Bt^L
zUA`x4tsXIfUA{3sene72HP6xoI<D(Pp*rH#3$?l|`hxZEIk;mm?WOncmJ0}efH3|~
z5?0aGy}bkX?c6co-M44Y{plfZXvfZhwEdCdK55Sv%U+{a$jki8f7A$%5&4C~F3|W_
z<P6;k{>@)L^0kULmM!P)4<r}<43f>~&3LigsG0Fbsa_OflP^A0%uC1J5A5GDxNn!<
z`u|BOZzuWOwV%ns at vJ-1Qui6I+3&08wzT^!yC>3oeJ*L4khbulpHCXI`!M_L(tO=d
zX=#5yu!o()u2c6ry3g7wF}ruseBIw at srw>*%4t6>KOy;cFQj$d4{4c_c6;dj(e>Xc
zZGR2r>wZp4_0#!v{r;fxCoM1Art|B5QOn1*qbsdJ_!yLmU6bbPept&{*Eu at B`hCjf
z?{xiiKdhzpv-$R1*RS!c#O#{%^IV at pT1LIJu4UQf>-z6<&#}c)+QWzDY3xgKV&~U<
zeZFgXN;|qz*RSEn<izIdeqNu)hg*XY-?NROqa}21Proj&aK7%p2Q~-!x{uei==#4I
z$=7|tn9J9F{A&5%j^rP8`{tuAKcR`PjIRHC5^t85-XFSOOSA>+k9uic%jYEbicr4p
z-`YQ7{*NTr+UxwfuS&W6r=7ok at 6vhoI({O#wu|QL at 42V057yr<ZQ(=nHFiQy!uh(t
zYTXj#NB_Uvax~q%7RlFrcEaT^c62Av`G5T(`O*H-`a6+)-LJO0`4etlEu#5<k_H=7
zKi$V?Tz>R=qxJVC*Tz!v3Llzp+rx+EYiW}M>2NP&`nLwrlv~*K(rmwj>-zQcXPxBh
h+QTDE+z{k%UYsasja1EsRq~&>G06XIBq35Y{{#`pM9BaE
>From 1aeec7a2c2b1f5d2e7f9cd178e4d8fbbac6e0576 Mon Sep 17 00:00:00 2001
From: Adam Kallai <kadam at inf.u-szeged.hu>
Date: Tue, 26 May 2026 16:15:56 +0200
Subject: [PATCH 06/10] Address reviewers 2
---
bolt/lib/Profile/DataAggregator.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp
index 9f59933cfec92..06617b46c1bed 100644
--- a/bolt/lib/Profile/DataAggregator.cpp
+++ b/bolt/lib/Profile/DataAggregator.cpp
@@ -545,8 +545,9 @@ void DataAggregator::parsePerfTextData(BinaryContext &BC) {
NamedRegionTimer T("parsePerfTextData", "Parsing perf-script events",
TimerGroupName, TimerGroupDesc, opts::TimeAggregator);
if (!Filename.empty()) {
+ // Load only the file header
ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
- MemoryBuffer::getFileOrSTDIN(Filename);
+ MemoryBuffer::getFileSlice(Filename, 133, 0);
if (std::error_code EC = MB.getError()) {
errs() << "PERF2BOLT-ERROR: cannot open " << Filename << ": "
<< EC.message() << "\n";
>From f6db69f16743a2e9ed161120ba5d4604ffd4d112 Mon Sep 17 00:00:00 2001
From: Adam Kallai <kadam at inf.u-szeged.hu>
Date: Thu, 28 May 2026 15:55:08 +0200
Subject: [PATCH 07/10] Address reviewers 3
---
bolt/include/bolt/Profile/DataAggregator.h | 35 +++---
bolt/include/bolt/Utils/CommandLineOpts.h | 2 +-
bolt/lib/Profile/DataAggregator.cpp | 129 ++++++++++-----------
bolt/lib/Utils/CommandLineOpts.cpp | 4 +-
bolt/unittests/Core/MemoryMaps.cpp | 34 +++++-
bolt/unittests/Profile/DataAggregator.cpp | 6 +-
bolt/unittests/Profile/PerfSpeEvents.cpp | 6 +-
7 files changed, 122 insertions(+), 94 deletions(-)
diff --git a/bolt/include/bolt/Profile/DataAggregator.h b/bolt/include/bolt/Profile/DataAggregator.h
index 3a50d1d2b255d..bc6f6238254e2 100644
--- a/bolt/include/bolt/Profile/DataAggregator.h
+++ b/bolt/include/bolt/Profile/DataAggregator.h
@@ -80,6 +80,9 @@ class DataAggregator : public DataReader {
/// Check whether \p FileName is a perf.data file
static bool checkPerfDataMagic(StringRef FileName);
+ /// Checks if a file starts with a specific magic string.
+ static bool checkInputFileMagic(StringRef FileName, StringLiteral MagicStr);
+
private:
struct LBREntry {
uint64_t From;
@@ -176,7 +179,7 @@ class DataAggregator : public DataReader {
/// Perf process spawning bookkeeping
struct PerfProcessInfo {
- static constexpr StringLiteral EventNamesStr[] = {"BUILDIDS", "MAIN", "MEM",
+ static constexpr StringLiteral PerfProcessTypeNames[] = {"BUILDIDS", "MAIN", "MEM",
"MMAP", "TASK"};
enum PerfProcessType Type;
@@ -184,6 +187,10 @@ class DataAggregator : public DataReader {
sys::ProcessInfo PI{};
SmallVector<char, 256> StdoutPath{};
SmallVector<char, 256> StderrPath{};
+
+ /// Helper variables for parsing perfscript profile.
+ /// - Length: Total size of the content from the Offset.
+ /// - Offset: Position where content begins in the file.
uint64_t Length{0};
uint64_t Offset{0};
};
@@ -476,25 +483,21 @@ class DataAggregator : public DataReader {
/// an external tool.
std::error_code parsePreAggregatedLBRSamples();
- /// Coordinate reading and parsing pre-parsed perf-script trace created by
- /// Perf2bolt's '--generate-perf-script' option.
- ///
- /// Perf2bolt first processes the pre-parsed profile's header to determine
- /// offset/length pairs for each event. Using this metadata, it opens only
- /// the specific file slice associated with the required events during
- /// the parsing phase.
- void parsePerfTextData(BinaryContext &BC);
+ /// Coordinate reading pre-parsed perf-script:
+ /// - open file header to determine offset and length for each part,
+ /// - read perf script slices.
+ void parsePerfScriptData();
/// Parse the header of the perf text file.
- std::error_code parsePerfTextFileHeader();
+ std::error_code parsePerfScriptFileHeader();
/// Dump pre-parsed perf profile data into a single file.
/// The generator relies on the aggregator work to spawn the required
- /// perf-script jobs based on the the aggregation type, and merges
+ /// perf-script jobs based on the aggregation type, and merges
/// their results into a single file.
- /// This hybrid profile contains all required events such as BuildID,
+ /// This hybrid profile contains all required items such as BuildID,
/// MMAP, TASK, MAIN (brstack or basic samples), or MEM for the aggregation.
- /// The generator also creates a file header, where these events
+ /// The generator also creates a file header, where these data types
/// are listed along with the length information of their contents.
/// The given length numbers in the header are in bytes, they are used
/// as an offset in the pre-parsed profile.
@@ -515,7 +518,7 @@ class DataAggregator : public DataReader {
/// based on how it was collected by Linux Perf.
///
/// Example how you can generate pre-parsed profile for 'basic' aggregation:
- /// perf2bolt -p perf.data BINARY -o perf.text --ba --generate-perf-script
+ /// perf2bolt -p perf.data BINARY -o perf.text --ba --profile-format=perfscript
///
/// This is how a pre-parsed profile data looks like for Basic Aggregation:
/// PERFTEXT;BUILDIDS=32;MMAP=2DC6C0;MAIN=1388;TASK=55730;MEM=128;
@@ -531,7 +534,7 @@ class DataAggregator : public DataReader {
/// ...
/// 1234 mem-loads: efgh1234 efgh1234
/// 1234 mem-loads: efgh4567 efgh8910
- Error generatePerfTextData();
+ Error generatePerfScriptData();
/// If \p Address falls into the binary address space based on memory
/// mapping info \p MMI, then adjust it for further processing by subtracting
@@ -675,7 +678,7 @@ inline raw_ostream &operator<<(raw_ostream &OS,
inline raw_ostream &operator<<(raw_ostream &OS,
const DataAggregator::PerfProcessType &T) {
- OS << DataAggregator::PerfProcessInfo::EventNamesStr[T];
+ OS << DataAggregator::PerfProcessInfo::PerfProcessTypeNames[T];
return OS;
}
} // namespace bolt
diff --git a/bolt/include/bolt/Utils/CommandLineOpts.h b/bolt/include/bolt/Utils/CommandLineOpts.h
index 5a6440034350f..994e352e16218 100644
--- a/bolt/include/bolt/Utils/CommandLineOpts.h
+++ b/bolt/include/bolt/Utils/CommandLineOpts.h
@@ -101,7 +101,7 @@ extern llvm::cl::opt<bool> UpdateBranchProtection;
extern llvm::cl::opt<SplitFunctionsStrategy> SplitStrategy;
// The format to use with -o in aggregation mode (perf2bolt)
-enum ProfileFormatKind { PF_Fdata, PF_YAML, PF_PreAgg };
+enum ProfileFormatKind { PF_Fdata, PF_YAML, PF_PreAgg, PF_PerfScript };
extern llvm::cl::opt<ProfileFormatKind> ProfileFormat;
extern llvm::cl::opt<bool> ShowDensity;
diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp
index 06617b46c1bed..a28857dc98854 100644
--- a/bolt/lib/Profile/DataAggregator.cpp
+++ b/bolt/lib/Profile/DataAggregator.cpp
@@ -129,20 +129,13 @@ extern cl::opt<opts::ProfileFormatKind> ProfileFormat;
extern cl::opt<bool> ProfileWritePseudoProbes;
extern cl::opt<std::string> SaveProfile;
-cl::opt<bool> ReadPreAggregated(
+cl::opt<bool> ReadPreAggOrPerfScript(
"pa", cl::desc("skip perf and read data from a pre-aggregated file format"),
cl::cat(AggregatorCategory));
-cl::opt<bool> ReadPerfTextProfile(
- "perf-script",
- cl::desc("skip perf event collection by reading a "
- "pre-parsed perf-script output in a textual format"),
- cl::Hidden, cl::cat(AggregatorCategory));
-
-cl::opt<bool> GeneratePerfTextProfile(
- "generate-perf-script",
- cl::desc("Dump perf-script jobs' output into a file"), cl::Hidden,
- cl::cat(AggregatorCategory));
+static cl::alias ReadPerfScript("ps",
+ cl::desc("read pre-parsed perf script output"),
+ cl::NotHidden, cl::aliasopt(ReadPreAggOrPerfScript));
static cl::opt<bool>
TimeAggregator("time-aggr",
@@ -159,6 +152,7 @@ const char TimerGroupName[] = "aggregator";
const char TimerGroupDesc[] = "Aggregator";
constexpr const StringLiteral PerfTextMagicStr = "PERFTEXT";
+constexpr const StringLiteral PerfDataMagicStr = "PERFILE";
std::vector<SectionNameAndRange> getTextSections(const BinaryContext *BC) {
std::vector<SectionNameAndRange> sections;
@@ -262,7 +256,7 @@ void DataAggregator::start() {
// Don't launch perf for pre-aggregated files or when perf input is specified
// by the user.
- if (opts::ReadPreAggregated || opts::ReadPerfTextProfile)
+ if (opts::ReadPreAggOrPerfScript)
return;
findPerfExecutable();
@@ -306,7 +300,7 @@ void DataAggregator::start() {
}
void DataAggregator::abort() {
- if (opts::ReadPreAggregated)
+ if (opts::ReadPreAggOrPerfScript)
return;
std::string Error;
@@ -404,29 +398,34 @@ void DataAggregator::processFileBuildID(StringRef FileBuildID) {
}
bool DataAggregator::checkPerfDataMagic(StringRef FileName) {
- if (opts::ReadPreAggregated || opts::ReadPerfTextProfile)
+ if (opts::ReadPreAggOrPerfScript)
return true;
+ return DataAggregator::checkInputFileMagic(FileName, PerfDataMagicStr);
+}
+
+bool DataAggregator::checkInputFileMagic(StringRef FileName, StringLiteral MagicStr) {
Expected<sys::fs::file_t> FD = sys::fs::openNativeFileForRead(FileName);
if (!FD) {
consumeError(FD.takeError());
return false;
}
-
- char Buf[7] = {0, 0, 0, 0, 0, 0, 0};
+ const size_t MagicStrSize = MagicStr.size();
+ char Buf[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ assert(MagicStr.size() <= 8 && "Size must be maximum 8");
llvm::scope_exit Close([&] { sys::fs::closeFile(*FD); });
Expected<size_t> BytesRead = sys::fs::readNativeFileSlice(
- *FD, MutableArrayRef(Buf, sizeof(Buf)), 0);
+ *FD, MutableArrayRef(Buf, MagicStrSize), 0);
if (!BytesRead) {
consumeError(BytesRead.takeError());
return false;
}
- if (*BytesRead != 7)
+ if (*BytesRead != MagicStrSize)
return false;
- if (strncmp(Buf, "PERFILE", 7) == 0)
+ if (strncmp(Buf, MagicStr.data(), MagicStrSize) == 0)
return true;
return false;
}
@@ -466,83 +465,84 @@ void DataAggregator::parsePreAggregated() {
}
}
-std::error_code DataAggregator::parsePerfTextFileHeader() {
- size_t LineEnd = ParsingBuf.find_first_of("\n");
- if (LineEnd == StringRef::npos) {
+std::error_code DataAggregator::parsePerfScriptFileHeader() {
+ size_t HeaderLineEndPos = ParsingBuf.find_first_of("\n");
+ if (HeaderLineEndPos == StringRef::npos) {
reportError("expected rest of line");
Diag << "Found: " << ParsingBuf << "\n";
return make_error_code(llvm::errc::io_error);
}
- StringRef HeaderLine = ParsingBuf.substr(0, LineEnd);
- size_t HeaderLineSize = HeaderLine.size() + 1;
- if (!HeaderLine.consume_front(PerfTextMagicStr)) {
+ ErrorOr<StringRef> PSMagicStrRes = parseString(';');
+ if (std::error_code EC = PSMagicStrRes.getError())
+ return EC;
+ StringRef PSMagicStr = PSMagicStrRes.get();
+ if (PSMagicStr != PerfTextMagicStr) {
reportError("expected 'PERFTEXT' magic string");
- Diag << "Found: " << HeaderLine << "\n";
- return make_error_code(llvm::errc::io_error);
- }
- Col += PerfTextMagicStr.size();
-
- SmallVector<StringRef, 5> Events;
- HeaderLine.trim().split(Events, ";", -1, false);
-
- if (Events.empty()) {
- reportError("missing events=sizes content");
- Diag << "Found: " << HeaderLine << "\n";
+ Diag << "Found: " << PSMagicStr << "\n";
return make_error_code(llvm::errc::io_error);
}
- uint64_t Offset = HeaderLineSize;
+ uint64_t Offset = HeaderLineEndPos + 1;
uint64_t Length = 0;
- for (StringRef EV : Events) {
- StringRef EventStr, LengthStr;
- std::tie(EventStr, LengthStr) = EV.split("=");
+ while (ParsingBuf.size() > 0 && ParsingBuf[0] != '\n') {
+ ErrorOr<StringRef> TypeLengthPairStrRes = parseString(';');
+ if (std::error_code EC = TypeLengthPairStrRes.getError())
+ return EC;
+ StringRef TypeLengthPairStr = TypeLengthPairStrRes.get();
+
+ // Parse 'PPIType=Length' pairs
+ const auto KV = TypeLengthPairStr.split("=");
+ if (KV.second.empty()) {
+ reportError("expected type=length content");
+ Diag << "Found: " << TypeLengthPairStr << "\n";
+ return make_error_code(llvm::errc::io_error);
+ }
PerfProcessInfo *PPI =
- StringSwitch<PerfProcessInfo *>(EventStr)
- .Case(PerfProcessInfo::EventNamesStr[PerfProcessType::BUILDIDS],
+ StringSwitch<PerfProcessInfo *>(KV.first)
+ .Case(PerfProcessInfo::PerfProcessTypeNames[PerfProcessType::BUILDIDS],
&BuildIDProcessInfo)
- .Case(PerfProcessInfo::EventNamesStr[PerfProcessType::MAIN_EVENTS],
+ .Case(PerfProcessInfo::PerfProcessTypeNames[PerfProcessType::MAIN_EVENTS],
&MainEventsPPI)
- .Case(PerfProcessInfo::EventNamesStr[PerfProcessType::MEM_EVENTS],
+ .Case(PerfProcessInfo::PerfProcessTypeNames[PerfProcessType::MEM_EVENTS],
&MemEventsPPI)
- .Case(PerfProcessInfo::EventNamesStr[PerfProcessType::MMAP_EVENTS],
+ .Case(PerfProcessInfo::PerfProcessTypeNames[PerfProcessType::MMAP_EVENTS],
&MMapEventsPPI)
- .Case(PerfProcessInfo::EventNamesStr[PerfProcessType::TASK_EVENTS],
+ .Case(PerfProcessInfo::PerfProcessTypeNames[PerfProcessType::TASK_EVENTS],
&TaskEventsPPI)
.Default(nullptr);
if (!PPI) {
- reportError("malformed text profile");
- Diag << "Found: " << EventStr << " in " << HeaderLine << "\n";
+ reportError("supported types: BUILDID, MAIN, MMAP, TASK, MEM");
+ Diag << "Found: " << KV.first << " in " << TypeLengthPairStr << "\n";
return make_error_code(llvm::errc::io_error);
}
- if (LengthStr.getAsInteger(16, Length)) {
+ if (KV.second.getAsInteger(16, Length)) {
reportError("expected hexadecimal number");
- Diag << "Found: " << LengthStr << " in " << HeaderLine << "\n";
+ Diag << "Found: " << KV.second << " in " << TypeLengthPairStr << "\n";
return make_error_code(llvm::errc::io_error);
}
PPI->Offset = Offset;
PPI->Length = Length;
Offset = Offset + Length;
- Col += EV.size();
}
ErrorOr<uint64_t> FsRes = getFileSize(Filename);
if (std::error_code EC = FsRes.getError())
return EC;
if (*FsRes != Offset) {
- reportError("corrupted perf text profile");
+ reportError("corrupted perfscript profile");
Diag << "Found: " << *FsRes << " != " << Offset << "\n";
return make_error_code(llvm::errc::io_error);
}
return std::error_code();
}
-void DataAggregator::parsePerfTextData(BinaryContext &BC) {
+void DataAggregator::parsePerfScriptData() {
outs() << "PERF2BOLT: parsing a textual perf-script events...\n";
- NamedRegionTimer T("parsePerfTextData", "Parsing perf-script events",
+ NamedRegionTimer T("parsePerfScript", "Parsing perf-script events",
TimerGroupName, TimerGroupDesc, opts::TimeAggregator);
if (!Filename.empty()) {
// Load only the file header
@@ -557,16 +557,16 @@ void DataAggregator::parsePerfTextData(BinaryContext &BC) {
ParsingBuf = (*MB)->getBuffer();
Col = 0;
Line = 1;
- if (std::error_code EC = parsePerfTextFileHeader()) {
+ if (std::error_code EC = parsePerfScriptFileHeader()) {
errs() << "PERF2BOLT-ERROR: failed to parse text header" << EC.message()
<< "\n";
exit(1);
}
}
- parsePerfData(BC);
+ parsePerfData();
}
-Error DataAggregator::generatePerfTextData() {
+Error DataAggregator::generatePerfScriptData() {
std::error_code EC;
raw_fd_ostream OutFile(opts::OutputFilename, EC, sys::fs::OpenFlags::OF_None);
if (EC) {
@@ -654,7 +654,7 @@ void DataAggregator::filterBinaryMMapInfo() {
int DataAggregator::prepareToParse(StringRef Name, PerfProcessInfo &Process,
PerfProcessErrorCallbackTy Callback) {
- if (opts::ReadPerfTextProfile) {
+ if (opts::ReadPreAggOrPerfScript) {
// No profile, ParsingBuf is set directly in unittests.
if (Filename.empty())
return 0;
@@ -853,12 +853,11 @@ void DataAggregator::imputeFallThroughs() {
void DataAggregator::parseInput() {
start();
-
- if (opts::ReadPreAggregated)
+ if (opts::ReadPreAggOrPerfScript && checkInputFileMagic(Filename, PerfTextMagicStr)) {
+ parsePerfScriptData();
+ } else if (opts::ReadPreAggOrPerfScript) {
parsePreAggregated();
- } else if (opts::ReadPerfTextData) {
- parsePerfTextData(BC);
- else {
+ } else {
parsePerfData();
}
}
@@ -870,9 +869,9 @@ Error DataAggregator::preprocessProfile(BinaryContext &BC) {
this->BC = &BC;
- if (opts::GeneratePerfTextProfile) {
+ if (opts::ProfileFormat == opts::ProfileFormatKind::PF_PerfScript) {
start();
- if (Error E = generatePerfTextData()) {
+ if (Error E = generatePerfScriptData()) {
deleteTempFiles();
exit(1);
}
diff --git a/bolt/lib/Utils/CommandLineOpts.cpp b/bolt/lib/Utils/CommandLineOpts.cpp
index 36a55d7a9d283..5eca5906fd0a5 100644
--- a/bolt/lib/Utils/CommandLineOpts.cpp
+++ b/bolt/lib/Utils/CommandLineOpts.cpp
@@ -279,7 +279,9 @@ cl::opt<ProfileFormatKind> ProfileFormat(
cl::values(clEnumValN(PF_Fdata, "fdata", "offset-based plaintext format"),
clEnumValN(PF_YAML, "yaml", "dense YAML representation"),
clEnumValN(PF_PreAgg, "preagg",
- "pre-aggregated profile format")),
+ "pre-aggregated profile format"),
+ clEnumValN(PF_PerfScript, "perfscript",
+ "perfscript profile format")),
cl::ZeroOrMore, cl::Hidden, cl::cat(BoltCategory));
cl::opt<std::string> SaveProfile("w",
diff --git a/bolt/unittests/Core/MemoryMaps.cpp b/bolt/unittests/Core/MemoryMaps.cpp
index 8391e837a240d..e2a80299e4a71 100644
--- a/bolt/unittests/Core/MemoryMaps.cpp
+++ b/bolt/unittests/Core/MemoryMaps.cpp
@@ -21,7 +21,7 @@ using namespace llvm::ELF;
using namespace bolt;
namespace opts {
-extern cl::opt<bool> ReadPerfTextProfile;
+extern cl::opt<bool> ReadPreAggOrPerfScript;
} // namespace opts
namespace {
@@ -68,6 +68,17 @@ struct MemoryMapsTester : public testing::TestWithParam<Triple::ArchType> {
ASSERT_FALSE(!BC);
}
+ void createTempFileWithContent(std::string &Buffer,
+ SmallVector<char, 256> &Path) {
+ int FD;
+ sys::fs::createTemporaryFile("perf-script-mmap", "text", FD, Path);
+ ASSERT_GE(FD, 0);
+
+ llvm::raw_fd_ostream FileStream(FD, true);
+ FileStream << Buffer;
+ FileStream.flush();
+ }
+
char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};
std::unique_ptr<ObjectFile> ObjFile;
std::unique_ptr<BinaryContext> BC;
@@ -93,7 +104,7 @@ INSTANTIATE_TEST_SUITE_P(AArch64, MemoryMapsTester,
TEST_P(MemoryMapsTester, ParseMultipleSegments) {
const int Pid = 1234;
StringRef Filename = "BINARY";
- opts::ReadPerfTextProfile = true;
+ opts::ReadPreAggOrPerfScript = true;
std::string MemEvents =
formatv(
"name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
@@ -102,13 +113,18 @@ TEST_P(MemoryMapsTester, ParseMultipleSegments) {
"[0xabc2000000(0x8000000) @ 0x31d0000 103:01 1573523 0]: r-xp {1}\n",
Pid, Filename)
.str();
+ std::string Buffer =
+ formatv("PERFTEXT;MMAP={0:x-};\n{1}", MemEvents.size(), MemEvents).str();
+
+ SmallVector<char, 256> Path{};
+ createTempFileWithContent(Buffer, Path);
BC->SegmentMapInfo[0x11da000] = SegmentInfo{
0x11da000, 0x10da000, 0x11ca000, 0x10da000, 0x10000, true, false};
BC->SegmentMapInfo[0x31d0000] = SegmentInfo{
0x31d0000, 0x51ac82c, 0x31d0000, 0x3000000, 0x200000, true, false};
- DataAggregator DA("");
+ DataAggregator DA(Path.data());
DA.setParsingBuffer(MemEvents);
BC->setFilename(Filename);
Error Err = DA.preprocessProfile(*BC);
@@ -121,6 +137,7 @@ TEST_P(MemoryMapsTester, ParseMultipleSegments) {
// Check that memory mapping is present and has the expected size.
ASSERT_NE(El, BinaryMMapInfo.end());
ASSERT_EQ(El->second.Size, static_cast<uint64_t>(0xb1d0000));
+ sys::fs::remove(Path);
}
/// Check that DataAggregator aborts when pre-processing an input binary
@@ -128,7 +145,7 @@ TEST_P(MemoryMapsTester, ParseMultipleSegments) {
TEST_P(MemoryMapsTester, MultipleSegmentsMismatchedBaseAddress) {
const int Pid = 1234;
StringRef Filename = "BINARY";
- opts::ReadPerfTextProfile = true;
+ opts::ReadPreAggOrPerfScript = true;
std::string MemEvents =
formatv(
"name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
@@ -138,6 +155,12 @@ TEST_P(MemoryMapsTester, MultipleSegmentsMismatchedBaseAddress) {
Pid, Filename)
.str();
+ std::string Buffer =
+ formatv("PERFTEXT;MMAP={0:x-};\n{1}", MemEvents.size(), MemEvents).str();
+
+ SmallVector<char, 256> Path{};
+ createTempFileWithContent(Buffer, Path);
+
BC->SegmentMapInfo[0x11da000] = SegmentInfo{
0x11da000, 0x10da000, 0x11ca000, 0x10da000, 0x10000, true, false};
// Using '0x31d0fff' FileOffset which triggers a different base address
@@ -145,10 +168,11 @@ TEST_P(MemoryMapsTester, MultipleSegmentsMismatchedBaseAddress) {
BC->SegmentMapInfo[0x31d0000] = SegmentInfo{
0x31d0000, 0x51ac82c, 0x31d0fff, 0x3000000, 0x200000, true, false};
- DataAggregator DA("");
+ DataAggregator DA(Path.data());
DA.setParsingBuffer(MemEvents);
BC->setFilename(Filename);
ASSERT_DEBUG_DEATH(
{ Error Err = DA.preprocessProfile(*BC); },
"Base address on multiple segment mappings should match");
+ sys::fs::remove(Path);
}
diff --git a/bolt/unittests/Profile/DataAggregator.cpp b/bolt/unittests/Profile/DataAggregator.cpp
index 0d5f4933189df..eaafa21204109 100644
--- a/bolt/unittests/Profile/DataAggregator.cpp
+++ b/bolt/unittests/Profile/DataAggregator.cpp
@@ -18,7 +18,7 @@ using namespace llvm;
using namespace llvm::bolt;
namespace opts {
-extern cl::opt<bool> ReadPreAggregated;
+extern cl::opt<bool> ReadPreAggOrPerfScript;
} // namespace opts
namespace llvm {
@@ -28,7 +28,7 @@ namespace bolt {
/// Used for both parseHexField tests (no BC needed) and pre-aggregated
/// parsing tests (BC needed, X86-only).
struct PreAggregatedTestHelper : public testing::Test {
- void SetUp() override { opts::ReadPreAggregated = true; }
+ void SetUp() override { opts::ReadPreAggOrPerfScript = true; }
protected:
using Trace = DataAggregator::Trace;
@@ -87,7 +87,7 @@ struct PreAggregatedTestHelper : public testing::Test {
} // namespace llvm
TEST(DataAggregatorTest, buildID) {
- opts::ReadPreAggregated = true;
+ opts::ReadPreAggOrPerfScript = true;
DataAggregator DA("<pseudo input>");
std::optional<StringRef> FileName;
diff --git a/bolt/unittests/Profile/PerfSpeEvents.cpp b/bolt/unittests/Profile/PerfSpeEvents.cpp
index aafd2ae209b75..325f4aa89b241 100644
--- a/bolt/unittests/Profile/PerfSpeEvents.cpp
+++ b/bolt/unittests/Profile/PerfSpeEvents.cpp
@@ -22,7 +22,7 @@ using namespace llvm::object;
using namespace llvm::ELF;
namespace opts {
-extern cl::opt<bool> ReadPerfTextProfile;
+extern cl::opt<bool> ReadPreAggOrPerfScript;
extern cl::opt<bool> ArmSPE;
} // namespace opts
@@ -134,7 +134,7 @@ TEST_F(PerfSpeEventsTestHelper, SpeBranchesWithBrstack) {
// ```
opts::ArmSPE = true;
- opts::ReadPerfTextProfile = true;
+ opts::ReadPreAggOrPerfScript = true;
StringRef Buffer = " 1234 0xa001/0xa002/PN/-/-/10/COND/-\n"
" 1234 0xb001/0xb002/P/-/-/4/RET/-\n"
" 1234 0xc456/0xc789/P/-/-/13/-/-\n"
@@ -175,7 +175,7 @@ TEST_F(PerfSpeEventsTestHelper, SpeBranchesWithBrstackAndPbt) {
// ```
opts::ArmSPE = true;
- opts::ReadPerfTextProfile = true;
+ opts::ReadPreAggOrPerfScript = true;
StringRef Buffer =
// "<PID> <SRC>/<DEST>/PN/-/-/10/COND/- <NULL>/<PBT>/-/-/-/0//-\n"
" 4567 0xa002/0xa003/PN/-/-/10/COND/- 0x0/0xa001/-/-/-/0//-\n"
>From 53be11f4cd3bf0aa3273e0cfa64db897ed021f1d Mon Sep 17 00:00:00 2001
From: Adam Kallai <kadam at inf.u-szeged.hu>
Date: Thu, 21 May 2026 11:26:05 +0200
Subject: [PATCH 08/10] Add unittest for parsing perfscript profile
---
bolt/include/bolt/Profile/DataAggregator.h | 1 +
bolt/unittests/Profile/CMakeLists.txt | 1 +
bolt/unittests/Profile/PerfScriptsData.cpp | 222 +++++++++++++++++++++
3 files changed, 224 insertions(+)
create mode 100644 bolt/unittests/Profile/PerfScriptsData.cpp
diff --git a/bolt/include/bolt/Profile/DataAggregator.h b/bolt/include/bolt/Profile/DataAggregator.h
index bc6f6238254e2..d13b1c99e66f5 100644
--- a/bolt/include/bolt/Profile/DataAggregator.h
+++ b/bolt/include/bolt/Profile/DataAggregator.h
@@ -93,6 +93,7 @@ class DataAggregator : public DataReader {
friend struct PerfSpeEventsTestHelper;
friend struct PreAggregatedTestHelper;
+ friend struct PerfScriptDataTestHelper;
struct PerfBranchSample {
SmallVector<LBREntry, 32> LBR;
diff --git a/bolt/unittests/Profile/CMakeLists.txt b/bolt/unittests/Profile/CMakeLists.txt
index 7b3cbd2cad724..8b6f75fb71456 100644
--- a/bolt/unittests/Profile/CMakeLists.txt
+++ b/bolt/unittests/Profile/CMakeLists.txt
@@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS
add_bolt_unittest(ProfileTests
DataAggregator.cpp
PerfSpeEvents.cpp
+ PerfScriptsData.cpp
DISABLE_LLVM_LINK_LLVM_DYLIB
)
diff --git a/bolt/unittests/Profile/PerfScriptsData.cpp b/bolt/unittests/Profile/PerfScriptsData.cpp
new file mode 100644
index 0000000000000..86cdc9bf8ab0a
--- /dev/null
+++ b/bolt/unittests/Profile/PerfScriptsData.cpp
@@ -0,0 +1,222 @@
+//===- bolt/unittests/Profile/PerfScriptData.cpp--------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "bolt/Core/BinaryContext.h"
+#include "bolt/Profile/DataAggregator.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/TargetSelect.h"
+#include "gtest/gtest.h"
+#include <gmock/gmock.h>
+
+using namespace llvm;
+using namespace llvm::bolt;
+using namespace llvm::object;
+using namespace llvm::ELF;
+
+namespace opts {
+extern cl::opt<bool> ReadPreAggOrPerfScript;
+extern cl::opt<bool> ArmSPE;
+} // namespace opts
+
+namespace llvm {
+namespace bolt {
+
+/// Tests textual profile parsing using dummy input and
+/// performs negative checks on PERFTEXT headers.
+struct PerfScriptDataTestHelper : public testing::Test {
+ void SetUp() override {
+ initalizeLLVM();
+ prepareElf();
+ initializeBOLT();
+ }
+
+protected:
+ void initalizeLLVM() {
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllAsmParsers();
+ llvm::InitializeAllDisassemblers();
+ llvm::InitializeAllTargets();
+ llvm::InitializeAllAsmPrinters();
+ }
+
+ void prepareElf() {
+ memcpy(ElfBuf, "\177ELF", 4);
+ ELF64LE::Ehdr *EHdr = reinterpret_cast<typename ELF64LE::Ehdr *>(ElfBuf);
+ EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64;
+ EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB;
+ EHdr->e_machine = llvm::ELF::EM_AARCH64;
+ MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF");
+ ObjFile = cantFail(ObjectFile::createObjectFile(Source));
+ }
+
+ void initializeBOLT() {
+ Relocation::Arch = ObjFile->makeTriple().getArch();
+ BC = cantFail(BinaryContext::createBinaryContext(
+ ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
+ ObjFile->getFileName(), nullptr, /*IsPIC*/ false,
+ DWARFContext::create(*ObjFile), {llvm::outs(), llvm::errs()}));
+ ASSERT_FALSE(!BC);
+ }
+
+ char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};
+ std::unique_ptr<ObjectFile> ObjFile;
+ std::unique_ptr<BinaryContext> BC;
+
+ void createTempFileWithContent(const std::string &Buffer,
+ SmallVector<char, 256> &Path) {
+ int FD;
+ sys::fs::createTemporaryFile("perf-script", "text", FD, Path);
+ ASSERT_GE(FD, 0);
+
+ llvm::raw_fd_ostream FileStream(FD, true);
+ FileStream << Buffer;
+ FileStream.flush();
+ }
+
+ // Checks several type of parsing errors on pre-parsed file header.
+ void checkPreParsedFileHeaderErrors(const std::string &Buffer,
+ const std::string &ErrorMessage) {
+ testing::internal::CaptureStderr();
+ SmallVector<char, 256> Path{};
+ createTempFileWithContent(Buffer, Path);
+
+ DataAggregator DA(Path.data());
+
+ DA.ParsingBuf = Buffer;
+ DA.BC = BC.get();
+
+ std::error_code EC = DA.parsePerfScriptFileHeader();
+ ASSERT_TRUE(EC == llvm::errc::io_error);
+
+ errs().flush();
+ std::string CapturedStderr = testing::internal::GetCapturedStderr();
+ EXPECT_THAT(CapturedStderr, testing::HasSubstr(ErrorMessage));
+
+ sys::fs::remove(Path);
+ }
+
+ // Sanity check whether MAIN events are processed
+ void parseAndCheckPerfScriptProfile(const std::string &Buffer, const int Pid,
+ const size_t Expected) {
+ SmallVector<char, 256> Path{};
+ createTempFileWithContent(Buffer, Path);
+
+ DataAggregator DA(Path.data());
+ DA.BC = BC.get();
+ DataAggregator::MMapInfo MMap;
+ DA.BinaryMMapInfo.insert(std::make_pair(Pid, MMap));
+
+ DA.parsePerfScriptData();
+ EXPECT_EQ(DA.Traces.size(), Expected);
+
+ sys::fs::remove(Path);
+ }
+};
+
+} // namespace bolt
+} // namespace llvm
+
+TEST_F(PerfScriptDataTestHelper, CheckMissingEndOfLineChar) {
+ opts::ReadPreAggOrPerfScript = true;
+ std::string ErrorMessage = "expected rest of line";
+ std::string Buffer =
+ "PERFTEXT;BUILDIDS=32;MMAP=2DC6C0;MAIN=1388;TASK=55730;MEM=128;";
+
+ checkPreParsedFileHeaderErrors(Buffer, ErrorMessage);
+}
+
+TEST_F(PerfScriptDataTestHelper, CheckMissingPerfMagicString) {
+ // Checks missing/wrong "PERFTEXT" string.
+ opts::ReadPreAggOrPerfScript = true;
+ std::string ErrorMessage = "expected 'PERFTEXT' magic string";
+ std::string Buffer =
+ "PERF;BUILDIDS=32;MMAP=2DC6C0;MAIN=1388;TASK=55730;MEM=128;\n";
+
+ checkPreParsedFileHeaderErrors(Buffer, ErrorMessage);
+}
+
+TEST_F(PerfScriptDataTestHelper, CheckMissingEventAndSizeContent) {
+ opts::ReadPreAggOrPerfScript = true;
+ std::string ErrorMessage = "expected type=length content";
+ std::string Buffer = "PERFTEXT;BUILDID?1;\n";
+
+ checkPreParsedFileHeaderErrors(Buffer, ErrorMessage);
+}
+
+TEST_F(PerfScriptDataTestHelper, CheckMalformedTypes) {
+ // Checks malformed type: actual: BUID, expected: BUILDID.
+ opts::ReadPreAggOrPerfScript = true;
+ std::string ErrorMessage = "supported types: BUILDID, MAIN, MMAP, TASK, MEM";
+ std::string Buffer =
+ "PERFTEXT;BUID=32;MMAP=2DC6C0;MAIN=1388;TASK=55730;MEM=128;\n";
+
+ checkPreParsedFileHeaderErrors(Buffer, ErrorMessage);
+}
+
+TEST_F(PerfScriptDataTestHelper, CheckExpectedHexNumber) {
+ // Checks expected hexadecimal number error message: BUILDIDS=32y.
+ opts::ReadPreAggOrPerfScript = true;
+ std::string ErrorMessage = "expected hexadecimal number";
+ std::string Buffer =
+ "PERFTEXT;BUILDIDS=32y;MMAP=2DC6C0;MAIN=1388;TASK=55730;MEM=128;\n";
+
+ checkPreParsedFileHeaderErrors(Buffer, ErrorMessage);
+}
+
+TEST_F(PerfScriptDataTestHelper, CheckCorruptedTextProfile) {
+ // Checks the sum of events length is not equal to file size.
+ opts::ReadPreAggOrPerfScript = true;
+ std::string ErrorMessage = "corrupted perfscript profile";
+ std::string Buffer =
+ "PERFTEXT;BUILDIDS=32;MMAP=2DC6C0;MAIN=1388;TASK=55730;MEM=128;\n";
+
+ checkPreParsedFileHeaderErrors(Buffer, ErrorMessage);
+}
+
+TEST_F(PerfScriptDataTestHelper, ParseAndCheckFileHeader) {
+ opts::ReadPreAggOrPerfScript = true;
+ opts::ArmSPE = true;
+ const int Pid = 1234;
+ StringRef Filename = "ELF";
+ std::string BuildID = formatv("{0} /example/{1}\n", Pid, Filename).str();
+ std::string MainEvents = formatv(" {0} 0xa002/0xa003/PN/-/-/10/COND/-\n"
+ " {0} 0xb002/0xb003/P/-/-/4/RET/-\n"
+ " {0} 0xc456/0xc789/P/-/-/13/-/-\n",
+ Pid)
+ .str();
+ std::string MemEvents =
+ formatv(
+ "name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
+ "[0xabc0000000(0x1000000) @ 0x11c0000 103:01 1573523 0]: r-xp {1}\n"
+ "name 0 [000] 0.000000: PERF_RECORD_MMAP2 {0}/{0}: "
+ "[0xabc2000000(0x8000000) @ 0x31d0000 103:01 1573523 0]: r-xp {1}\n",
+ Pid, Filename)
+ .str();
+ std::string TaskEvents =
+ formatv("{1} {0} PERF_RECORD_COMM exec: {1}:{0}/{0}\n"
+ "{1} {0} PERF_RECORD_EXIT({0}:{0}}):(20469:20469)\n",
+ Pid, Filename)
+ .str();
+
+ std::string Header =
+ formatv("PERFTEXT;BUILDIDS={0:x-};MMAP={1:x-};MAIN={2:x-};TASK={3:x-};\n",
+ BuildID.size(), MemEvents.size(), MainEvents.size(),
+ TaskEvents.size())
+ .str();
+
+ std::string Buffer = formatv("{0}{1}{2}{3}{4}", Header, BuildID, MemEvents,
+ MainEvents, TaskEvents)
+ .str();
+
+ // Defined 3 entries on MainEvents. The size of Traces intermediate storage
+ // should be 'size == 3' after the parsing this dummy MainEvents.
+ parseAndCheckPerfScriptProfile(Buffer, Pid, 3);
+}
>From 8240d1ca5c8c4aac51d298e36b90e365b8b8b5bd Mon Sep 17 00:00:00 2001
From: Adam Kallai <kadam at inf.u-szeged.hu>
Date: Tue, 2 Jun 2026 11:39:01 +0200
Subject: [PATCH 09/10] Fix clang-format
---
bolt/include/bolt/Profile/DataAggregator.h | 7 ++--
bolt/lib/Profile/DataAggregator.cpp | 43 +++++++++++++---------
bolt/lib/Utils/CommandLineOpts.cpp | 3 +-
bolt/unittests/Core/MemoryMaps.cpp | 2 +-
4 files changed, 31 insertions(+), 24 deletions(-)
diff --git a/bolt/include/bolt/Profile/DataAggregator.h b/bolt/include/bolt/Profile/DataAggregator.h
index d13b1c99e66f5..70343e8f152e9 100644
--- a/bolt/include/bolt/Profile/DataAggregator.h
+++ b/bolt/include/bolt/Profile/DataAggregator.h
@@ -180,8 +180,8 @@ class DataAggregator : public DataReader {
/// Perf process spawning bookkeeping
struct PerfProcessInfo {
- static constexpr StringLiteral PerfProcessTypeNames[] = {"BUILDIDS", "MAIN", "MEM",
- "MMAP", "TASK"};
+ static constexpr StringLiteral PerfProcessTypeNames[] = {
+ "BUILDIDS", "MAIN", "MEM", "MMAP", "TASK"};
enum PerfProcessType Type;
bool IsFinished{false};
@@ -519,7 +519,8 @@ class DataAggregator : public DataReader {
/// based on how it was collected by Linux Perf.
///
/// Example how you can generate pre-parsed profile for 'basic' aggregation:
- /// perf2bolt -p perf.data BINARY -o perf.text --ba --profile-format=perfscript
+ /// perf2bolt -p perf.data BINARY -o perf.text --ba
+ /// --profile-format=perfscript
///
/// This is how a pre-parsed profile data looks like for Basic Aggregation:
/// PERFTEXT;BUILDIDS=32;MMAP=2DC6C0;MAIN=1388;TASK=55730;MEM=128;
diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp
index a28857dc98854..b9d97affe62e9 100644
--- a/bolt/lib/Profile/DataAggregator.cpp
+++ b/bolt/lib/Profile/DataAggregator.cpp
@@ -135,7 +135,8 @@ cl::opt<bool> ReadPreAggOrPerfScript(
static cl::alias ReadPerfScript("ps",
cl::desc("read pre-parsed perf script output"),
- cl::NotHidden, cl::aliasopt(ReadPreAggOrPerfScript));
+ cl::NotHidden,
+ cl::aliasopt(ReadPreAggOrPerfScript));
static cl::opt<bool>
TimeAggregator("time-aggr",
@@ -404,7 +405,8 @@ bool DataAggregator::checkPerfDataMagic(StringRef FileName) {
return DataAggregator::checkInputFileMagic(FileName, PerfDataMagicStr);
}
-bool DataAggregator::checkInputFileMagic(StringRef FileName, StringLiteral MagicStr) {
+bool DataAggregator::checkInputFileMagic(StringRef FileName,
+ StringLiteral MagicStr) {
Expected<sys::fs::file_t> FD = sys::fs::openNativeFileForRead(FileName);
if (!FD) {
consumeError(FD.takeError());
@@ -415,8 +417,8 @@ bool DataAggregator::checkInputFileMagic(StringRef FileName, StringLiteral Magic
assert(MagicStr.size() <= 8 && "Size must be maximum 8");
llvm::scope_exit Close([&] { sys::fs::closeFile(*FD); });
- Expected<size_t> BytesRead = sys::fs::readNativeFileSlice(
- *FD, MutableArrayRef(Buf, MagicStrSize), 0);
+ Expected<size_t> BytesRead =
+ sys::fs::readNativeFileSlice(*FD, MutableArrayRef(Buf, MagicStrSize), 0);
if (!BytesRead) {
consumeError(BytesRead.takeError());
return false;
@@ -499,19 +501,23 @@ std::error_code DataAggregator::parsePerfScriptFileHeader() {
return make_error_code(llvm::errc::io_error);
}
- PerfProcessInfo *PPI =
- StringSwitch<PerfProcessInfo *>(KV.first)
- .Case(PerfProcessInfo::PerfProcessTypeNames[PerfProcessType::BUILDIDS],
- &BuildIDProcessInfo)
- .Case(PerfProcessInfo::PerfProcessTypeNames[PerfProcessType::MAIN_EVENTS],
- &MainEventsPPI)
- .Case(PerfProcessInfo::PerfProcessTypeNames[PerfProcessType::MEM_EVENTS],
- &MemEventsPPI)
- .Case(PerfProcessInfo::PerfProcessTypeNames[PerfProcessType::MMAP_EVENTS],
- &MMapEventsPPI)
- .Case(PerfProcessInfo::PerfProcessTypeNames[PerfProcessType::TASK_EVENTS],
- &TaskEventsPPI)
- .Default(nullptr);
+ PerfProcessInfo *PPI = StringSwitch<PerfProcessInfo *>(KV.first)
+ .Case(PerfProcessInfo::PerfProcessTypeNames
+ [PerfProcessType::BUILDIDS],
+ &BuildIDProcessInfo)
+ .Case(PerfProcessInfo::PerfProcessTypeNames
+ [PerfProcessType::MAIN_EVENTS],
+ &MainEventsPPI)
+ .Case(PerfProcessInfo::PerfProcessTypeNames
+ [PerfProcessType::MEM_EVENTS],
+ &MemEventsPPI)
+ .Case(PerfProcessInfo::PerfProcessTypeNames
+ [PerfProcessType::MMAP_EVENTS],
+ &MMapEventsPPI)
+ .Case(PerfProcessInfo::PerfProcessTypeNames
+ [PerfProcessType::TASK_EVENTS],
+ &TaskEventsPPI)
+ .Default(nullptr);
if (!PPI) {
reportError("supported types: BUILDID, MAIN, MMAP, TASK, MEM");
@@ -853,7 +859,8 @@ void DataAggregator::imputeFallThroughs() {
void DataAggregator::parseInput() {
start();
- if (opts::ReadPreAggOrPerfScript && checkInputFileMagic(Filename, PerfTextMagicStr)) {
+ if (opts::ReadPreAggOrPerfScript &&
+ checkInputFileMagic(Filename, PerfTextMagicStr)) {
parsePerfScriptData();
} else if (opts::ReadPreAggOrPerfScript) {
parsePreAggregated();
diff --git a/bolt/lib/Utils/CommandLineOpts.cpp b/bolt/lib/Utils/CommandLineOpts.cpp
index 5eca5906fd0a5..ee34b7075ee31 100644
--- a/bolt/lib/Utils/CommandLineOpts.cpp
+++ b/bolt/lib/Utils/CommandLineOpts.cpp
@@ -278,8 +278,7 @@ cl::opt<ProfileFormatKind> ProfileFormat(
cl::init(PF_Fdata),
cl::values(clEnumValN(PF_Fdata, "fdata", "offset-based plaintext format"),
clEnumValN(PF_YAML, "yaml", "dense YAML representation"),
- clEnumValN(PF_PreAgg, "preagg",
- "pre-aggregated profile format"),
+ clEnumValN(PF_PreAgg, "preagg", "pre-aggregated profile format"),
clEnumValN(PF_PerfScript, "perfscript",
"perfscript profile format")),
cl::ZeroOrMore, cl::Hidden, cl::cat(BoltCategory));
diff --git a/bolt/unittests/Core/MemoryMaps.cpp b/bolt/unittests/Core/MemoryMaps.cpp
index e2a80299e4a71..207e169348a26 100644
--- a/bolt/unittests/Core/MemoryMaps.cpp
+++ b/bolt/unittests/Core/MemoryMaps.cpp
@@ -69,7 +69,7 @@ struct MemoryMapsTester : public testing::TestWithParam<Triple::ArchType> {
}
void createTempFileWithContent(std::string &Buffer,
- SmallVector<char, 256> &Path) {
+ SmallVector<char, 256> &Path) {
int FD;
sys::fs::createTemporaryFile("perf-script-mmap", "text", FD, Path);
ASSERT_GE(FD, 0);
>From 086cbf07ee5cf47537bca8ad801f5ac281379949 Mon Sep 17 00:00:00 2001
From: Adam Kallai <kadam at inf.u-szeged.hu>
Date: Tue, 2 Jun 2026 12:11:30 +0200
Subject: [PATCH 10/10] Update
---
bolt/include/bolt/Profile/DataAggregator.h | 2 +-
bolt/lib/Profile/DataAggregator.cpp | 10 ++++------
bolt/unittests/Profile/CMakeLists.txt | 2 +-
.../Profile/{PerfScriptsData.cpp => PerfScripts.cpp} | 2 +-
4 files changed, 7 insertions(+), 9 deletions(-)
rename bolt/unittests/Profile/{PerfScriptsData.cpp => PerfScripts.cpp} (99%)
diff --git a/bolt/include/bolt/Profile/DataAggregator.h b/bolt/include/bolt/Profile/DataAggregator.h
index 70343e8f152e9..ea9a95e97f78c 100644
--- a/bolt/include/bolt/Profile/DataAggregator.h
+++ b/bolt/include/bolt/Profile/DataAggregator.h
@@ -487,7 +487,7 @@ class DataAggregator : public DataReader {
/// Coordinate reading pre-parsed perf-script:
/// - open file header to determine offset and length for each part,
/// - read perf script slices.
- void parsePerfScriptData();
+ void parsePerfScript();
/// Parse the header of the perf text file.
std::error_code parsePerfScriptFileHeader();
diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp
index b9d97affe62e9..5d987ec2c0a3b 100644
--- a/bolt/lib/Profile/DataAggregator.cpp
+++ b/bolt/lib/Profile/DataAggregator.cpp
@@ -133,10 +133,8 @@ cl::opt<bool> ReadPreAggOrPerfScript(
"pa", cl::desc("skip perf and read data from a pre-aggregated file format"),
cl::cat(AggregatorCategory));
-static cl::alias ReadPerfScript("ps",
- cl::desc("read pre-parsed perf script output"),
- cl::NotHidden,
- cl::aliasopt(ReadPreAggOrPerfScript));
+cl::alias ReadPerfScript("ps", cl::desc("read pre-parsed perf script output"),
+ cl::NotHidden, cl::aliasopt(ReadPreAggOrPerfScript));
static cl::opt<bool>
TimeAggregator("time-aggr",
@@ -546,7 +544,7 @@ std::error_code DataAggregator::parsePerfScriptFileHeader() {
return std::error_code();
}
-void DataAggregator::parsePerfScriptData() {
+void DataAggregator::parsePerfScript() {
outs() << "PERF2BOLT: parsing a textual perf-script events...\n";
NamedRegionTimer T("parsePerfScript", "Parsing perf-script events",
TimerGroupName, TimerGroupDesc, opts::TimeAggregator);
@@ -861,7 +859,7 @@ void DataAggregator::parseInput() {
start();
if (opts::ReadPreAggOrPerfScript &&
checkInputFileMagic(Filename, PerfTextMagicStr)) {
- parsePerfScriptData();
+ parsePerfScript();
} else if (opts::ReadPreAggOrPerfScript) {
parsePreAggregated();
} else {
diff --git a/bolt/unittests/Profile/CMakeLists.txt b/bolt/unittests/Profile/CMakeLists.txt
index 8b6f75fb71456..4befd08cb37df 100644
--- a/bolt/unittests/Profile/CMakeLists.txt
+++ b/bolt/unittests/Profile/CMakeLists.txt
@@ -7,7 +7,7 @@ set(LLVM_LINK_COMPONENTS
add_bolt_unittest(ProfileTests
DataAggregator.cpp
PerfSpeEvents.cpp
- PerfScriptsData.cpp
+ PerfScripts.cpp
DISABLE_LLVM_LINK_LLVM_DYLIB
)
diff --git a/bolt/unittests/Profile/PerfScriptsData.cpp b/bolt/unittests/Profile/PerfScripts.cpp
similarity index 99%
rename from bolt/unittests/Profile/PerfScriptsData.cpp
rename to bolt/unittests/Profile/PerfScripts.cpp
index 86cdc9bf8ab0a..843b8e3d893dd 100644
--- a/bolt/unittests/Profile/PerfScriptsData.cpp
+++ b/bolt/unittests/Profile/PerfScripts.cpp
@@ -114,7 +114,7 @@ struct PerfScriptDataTestHelper : public testing::Test {
DataAggregator::MMapInfo MMap;
DA.BinaryMMapInfo.insert(std::make_pair(Pid, MMap));
- DA.parsePerfScriptData();
+ DA.parsePerfScript();
EXPECT_EQ(DA.Traces.size(), Expected);
sys::fs::remove(Path);
More information about the llvm-commits
mailing list