[llvm] [BOLT][AArch64] Introduce SPE mode in BasicAggregation (PR #120741)
Paschalis Mpeis via llvm-commits
llvm-commits at lists.llvm.org
Fri Dec 20 07:15:01 PST 2024
https://github.com/paschalis-mpeis updated https://github.com/llvm/llvm-project/pull/120741
>From dbd86f1b90d4cbf66240b85c885e9811aafa6cb1 Mon Sep 17 00:00:00 2001
From: Paschalis Mpeis <paschalis.mpeis at arm.com>
Date: Fri, 20 Dec 2024 14:19:01 +0000
Subject: [PATCH 1/2] [BOLT][AArch64] Introduce SPE mode in BasicAggregation
BOLT gains the ability to process branch target information generated by
Arm SPE data, using the `BasicAggregation` format.
Example usage is:
```bash
perf2bolt -p perf.data -o perf.boltdata --nl --spe BINARY
```
New branch data and compatibility:
---
SPE branch entries in perf data contain a branch pair (`IP` -> `ADDR`)
for the source and destination branches. DataAggregator processes those
by creating two basic samples. Any other event types will have `ADDR`
field set to `0x0`. For those a single sample will be created. Such
events can be either SPE or non-SPE, like `l1d-access` and `cycles`
respectively.
The format of the input perf entries is:
```
PID EVENT-TYPE ADDR IP
```
When on SPE mode and:
- host is not `AArch64`, BOLT will exit with a relevant message
- `ADDR` field is unavailable, BOLT will exit with a relevant message
- no branch pairs were recorded, BOLT will present a warning
Examples of generating profiling data for the SPE mode:
---
Profiles can be captured with perf on AArch64 machines with SPE enabled.
They can be combined with other events, SPE or not.
Capture only SPE branch data events:
```bash
perf record -e 'arm_spe_0/branch_filter=1/u' -- BINARY
```
Capture any SPE events:
```bash
perf record -e 'arm_spe_0//u' -- BINARY
```
Capture any SPE events and cycles
```bash
perf record -e 'arm_spe_0//u' -e cycles:u -- BINARY
```
More filters, jitter, and specify count to control overheads/quality.
```bash
perf record -e 'arm_spe_0/branch_filter=1,load_filter=0,store_filter=0,jitter=1/u' -c 10007 -- BINARY
```
---
bolt/include/bolt/Profile/DataAggregator.h | 14 ++
bolt/lib/Profile/DataAggregator.cpp | 143 +++++++++++++--
.../test/perf2bolt/AArch64/perf2bolt-spe.test | 14 ++
bolt/test/perf2bolt/X86/perf2bolt-spe.test | 9 +
bolt/tools/driver/llvm-bolt.cpp | 9 +
bolt/unittests/Profile/CMakeLists.txt | 14 ++
bolt/unittests/Profile/PerfSpeEvents.cpp | 173 ++++++++++++++++++
7 files changed, 365 insertions(+), 11 deletions(-)
create mode 100644 bolt/test/perf2bolt/AArch64/perf2bolt-spe.test
create mode 100644 bolt/test/perf2bolt/X86/perf2bolt-spe.test
create mode 100644 bolt/unittests/Profile/PerfSpeEvents.cpp
diff --git a/bolt/include/bolt/Profile/DataAggregator.h b/bolt/include/bolt/Profile/DataAggregator.h
index 320623cfa15af1..be6e0fbd6347a0 100644
--- a/bolt/include/bolt/Profile/DataAggregator.h
+++ b/bolt/include/bolt/Profile/DataAggregator.h
@@ -78,6 +78,8 @@ class DataAggregator : public DataReader {
static bool checkPerfDataMagic(StringRef FileName);
private:
+ friend struct PerfSpeEventsTestHelper;
+
struct PerfBranchSample {
SmallVector<LBREntry, 32> LBR;
uint64_t PC;
@@ -294,6 +296,15 @@ class DataAggregator : public DataReader {
/// and a PC
ErrorOr<PerfBasicSample> parseBasicSample();
+ /// Parse an Arm SPE entry into the non-lbr format by generating two basic
+ /// samples. The format of an input SPE entry is:
+ /// ```
+ /// PID EVENT-TYPE ADDR IP
+ /// ```
+ /// SPE branch events will have 'ADDR' set to a branch target address while
+ /// other perf or SPE events will have it set to zero.
+ ErrorOr<std::pair<PerfBasicSample,PerfBasicSample>> parseSpeAsBasicSamples();
+
/// Parse a single perf sample containing a PID associated with an IP and
/// address.
ErrorOr<PerfMemSample> parseMemSample();
@@ -343,6 +354,9 @@ class DataAggregator : public DataReader {
/// Process non-LBR events.
void processBasicEvents();
+ /// Parse Arm SPE events into the non-LBR format.
+ std::error_code parseSpeAsBasicEvents();
+
/// Parse the full output generated by perf script to report memory events.
std::error_code parseMemEvents();
diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp
index 2b02086e3e0c99..7038ca5b1452ab 100644
--- a/bolt/lib/Profile/DataAggregator.cpp
+++ b/bolt/lib/Profile/DataAggregator.cpp
@@ -49,6 +49,13 @@ static cl::opt<bool>
cl::desc("aggregate basic samples (without LBR info)"),
cl::cat(AggregatorCategory));
+cl::opt<bool> ArmSPE(
+ "spe",
+ cl::desc(
+ "Enable Arm SPE mode. Used in conjuction with no-lbr mode, ie `--spe "
+ "--nl`"),
+ cl::cat(AggregatorCategory));
+
static cl::opt<std::string>
ITraceAggregation("itrace",
cl::desc("Generate LBR info with perf itrace argument"),
@@ -180,11 +187,19 @@ void DataAggregator::start() {
findPerfExecutable();
- if (opts::BasicAggregation) {
- launchPerfProcess("events without LBR",
- MainEventsPPI,
+ if (opts::ArmSPE) {
+ if (!opts::BasicAggregation) {
+ errs() << "PERF2BOLT-ERROR: Arm SPE mode is combined only with "
+ "BasicAggregation.\n";
+ exit(1);
+ }
+ launchPerfProcess("branch events with SPE", MainEventsPPI,
+ "script -F pid,event,ip,addr --itrace=i1i",
+ /*Wait = */ false);
+ } else if (opts::BasicAggregation) {
+ launchPerfProcess("events without LBR", MainEventsPPI,
"script -F pid,event,ip",
- /*Wait = */false);
+ /*Wait = */ false);
} else if (!opts::ITraceAggregation.empty()) {
std::string ItracePerfScriptArgs = llvm::formatv(
"script -F pid,ip,brstack --itrace={0}", opts::ITraceAggregation);
@@ -192,10 +207,9 @@ void DataAggregator::start() {
ItracePerfScriptArgs.c_str(),
/*Wait = */ false);
} else {
- launchPerfProcess("branch events",
- MainEventsPPI,
+ launchPerfProcess("branch events", MainEventsPPI,
"script -F pid,ip,brstack",
- /*Wait = */false);
+ /*Wait = */ false);
}
// Note: we launch script for mem events regardless of the option, as the
@@ -531,14 +545,20 @@ Error DataAggregator::preprocessProfile(BinaryContext &BC) {
"not read one from input binary\n";
}
- auto ErrorCallback = [](int ReturnCode, StringRef ErrBuf) {
+ const Regex NoData("Samples for '.*' event do not have ADDR attribute set. "
+ "Cannot print 'addr' field.");
+
+ auto ErrorCallback = [&NoData](int ReturnCode, StringRef ErrBuf) {
+ if (opts::ArmSPE && NoData.match(ErrBuf)) {
+ errs() << "PERF2BOLT-ERROR: perf data are incompatible for Arm SPE mode "
+ "consumption. ADDR attribute is unset.\n";
+ exit(1);
+ }
errs() << "PERF-ERROR: return code " << ReturnCode << "\n" << ErrBuf;
exit(1);
};
auto MemEventsErrorCallback = [&](int ReturnCode, StringRef ErrBuf) {
- Regex NoData("Samples for '.*' event do not have ADDR attribute set. "
- "Cannot print 'addr' field.");
if (!NoData.match(ErrBuf))
ErrorCallback(ReturnCode, ErrBuf);
};
@@ -579,7 +599,8 @@ Error DataAggregator::preprocessProfile(BinaryContext &BC) {
exit(0);
}
- if ((!opts::BasicAggregation && parseBranchEvents()) ||
+ if (((!opts::BasicAggregation && !opts::ArmSPE) && parseBranchEvents()) ||
+ (opts::BasicAggregation && opts::ArmSPE && parseSpeAsBasicEvents()) ||
(opts::BasicAggregation && parseBasicEvents()))
errs() << "PERF2BOLT: failed to parse samples\n";
@@ -1226,6 +1247,66 @@ ErrorOr<DataAggregator::PerfBasicSample> DataAggregator::parseBasicSample() {
return PerfBasicSample{Event.get(), Address};
}
+ErrorOr<
+ std::pair<DataAggregator::PerfBasicSample, DataAggregator::PerfBasicSample>>
+DataAggregator::parseSpeAsBasicSamples() {
+ while (checkAndConsumeFS()) {
+ }
+
+ ErrorOr<int64_t> PIDRes = parseNumberField(FieldSeparator, true);
+ if (std::error_code EC = PIDRes.getError())
+ return EC;
+
+ constexpr PerfBasicSample EmptySample = PerfBasicSample{StringRef(), 0};
+ auto MMapInfoIter = BinaryMMapInfo.find(*PIDRes);
+ if (MMapInfoIter == BinaryMMapInfo.end()) {
+ consumeRestOfLine();
+ return std::make_pair(EmptySample, EmptySample);
+ }
+
+ while (checkAndConsumeFS()) {
+ }
+
+ ErrorOr<StringRef> Event = parseString(FieldSeparator);
+ if (std::error_code EC = Event.getError())
+ return EC;
+
+ while (checkAndConsumeFS()) {
+ }
+
+ ErrorOr<uint64_t> AddrResTo = parseHexField(FieldSeparator);
+ if (std::error_code EC = AddrResTo.getError())
+ return EC;
+ consumeAllRemainingFS();
+
+ ErrorOr<uint64_t> AddrResFrom = parseHexField(FieldSeparator, true);
+ if (std::error_code EC = AddrResFrom.getError())
+ return EC;
+
+ if (!checkAndConsumeNewLine()) {
+ reportError("expected end of line");
+ return make_error_code(llvm::errc::io_error);
+ }
+
+ auto genBasicSample = [&](uint64_t Address) {
+ // When fed with non SPE branch events the target address will be null.
+ // This is expected and ignored.
+ if (Address == 0x0)
+ return EmptySample;
+
+ if (!BC->HasFixedLoadAddress)
+ adjustAddress(Address, MMapInfoIter->second);
+ return PerfBasicSample{Event.get(), Address};
+ };
+
+ // Show more meaningful event names on boltdata.
+ if (Event->str() == "instructions:")
+ Event = *AddrResTo != 0x0 ? "branch-spe:" : "instruction-spe:";
+
+ return std::make_pair(genBasicSample(*AddrResFrom),
+ genBasicSample(*AddrResTo));
+}
+
ErrorOr<DataAggregator::PerfMemSample> DataAggregator::parseMemSample() {
PerfMemSample Res{0, 0};
@@ -1703,6 +1784,46 @@ std::error_code DataAggregator::parseBasicEvents() {
return std::error_code();
}
+std::error_code DataAggregator::parseSpeAsBasicEvents() {
+ outs() << "PERF2BOLT: parsing SPE data as basic events (no LBR)...\n";
+ NamedRegionTimer T("parseSPEBasic", "Parsing SPE as basic events",
+ TimerGroupName, TimerGroupDesc, opts::TimeAggregator);
+ uint64_t NumSpeBranchSamples = 0;
+
+ // Convert entries to one or two basic samples, depending on whether there is
+ // branch target information.
+ while (hasData()) {
+ auto SamplePair = parseSpeAsBasicSamples();
+ if (std::error_code EC = SamplePair.getError())
+ return EC;
+
+ auto registerSample = [this](const PerfBasicSample *Sample) {
+ if (!Sample->PC)
+ return;
+
+ if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Sample->PC))
+ BF->setHasProfileAvailable();
+
+ ++BasicSamples[Sample->PC];
+ EventNames.insert(Sample->EventName);
+ };
+
+ if (SamplePair->first.PC != 0x0 && SamplePair->second.PC != 0x0)
+ ++NumSpeBranchSamples;
+
+ registerSample(&SamplePair->first);
+ registerSample(&SamplePair->second);
+ }
+
+ if (NumSpeBranchSamples == 0)
+ errs() << "PERF2BOLT-WARNING: no SPE branches found\n";
+ else
+ outs() << "PERF2BOLT: found " << NumSpeBranchSamples
+ << " SPE branch sample pairs.\n";
+
+ return std::error_code();
+}
+
void DataAggregator::processBasicEvents() {
outs() << "PERF2BOLT: processing basic events (without LBR)...\n";
NamedRegionTimer T("processBasic", "Processing basic events", TimerGroupName,
diff --git a/bolt/test/perf2bolt/AArch64/perf2bolt-spe.test b/bolt/test/perf2bolt/AArch64/perf2bolt-spe.test
new file mode 100644
index 00000000000000..d7cea7ff769b8e
--- /dev/null
+++ b/bolt/test/perf2bolt/AArch64/perf2bolt-spe.test
@@ -0,0 +1,14 @@
+## Check that Arm SPE mode is available on AArch64 with BasicAggregation.
+
+REQUIRES: system-linux,perf,target=aarch64{{.*}}
+
+RUN: %clang %cflags %p/../../Inputs/asm_foo.s %p/../../Inputs/asm_main.c -o %t.exe
+RUN: touch %t.empty.perf.data
+RUN: perf2bolt -p %t.empty.perf.data -o %t.perf.boltdata --nl --spe --pa %t.exe 2>&1 | FileCheck %s --check-prefix=CHECK-SPE-NO-LBR
+
+CHECK-SPE-NO-LBR: PERF2BOLT: Starting data aggregation job
+
+RUN: perf record -e cycles -q -o %t.perf.data -- %t.exe
+RUN: not perf2bolt -p %t.perf.data -o %t.perf.boltdata --spe %t.exe 2>&1 | FileCheck %s --check-prefix=CHECK-SPE-LBR
+
+CHECK-SPE-LBR: PERF2BOLT-ERROR: Arm SPE mode is combined only with BasicAggregation.
diff --git a/bolt/test/perf2bolt/X86/perf2bolt-spe.test b/bolt/test/perf2bolt/X86/perf2bolt-spe.test
new file mode 100644
index 00000000000000..f31c17f411137d
--- /dev/null
+++ b/bolt/test/perf2bolt/X86/perf2bolt-spe.test
@@ -0,0 +1,9 @@
+## Check that Arm SPE mode is unavailable on X86.
+
+REQUIRES: system-linux,x86_64-linux
+
+RUN: %clang %cflags %p/../../Inputs/asm_foo.s %p/../../Inputs/asm_main.c -o %t.exe
+RUN: touch %t.empty.perf.data
+RUN: not perf2bolt -p %t.empty.perf.data -o %t.perf.boltdata --nl --spe --pa %t.exe 2>&1 | FileCheck %s
+
+CHECK: BOLT-ERROR: -spe is available only on AArch64.
diff --git a/bolt/tools/driver/llvm-bolt.cpp b/bolt/tools/driver/llvm-bolt.cpp
index efa06cd68cb997..60b813f6f858d7 100644
--- a/bolt/tools/driver/llvm-bolt.cpp
+++ b/bolt/tools/driver/llvm-bolt.cpp
@@ -51,6 +51,8 @@ static cl::opt<std::string> InputFilename(cl::Positional,
cl::Required, cl::cat(BoltCategory),
cl::sub(cl::SubCommand::getAll()));
+extern cl::opt<bool> ArmSPE;
+
static cl::opt<std::string>
InputDataFilename("data",
cl::desc("<data file>"),
@@ -245,6 +247,13 @@ int main(int argc, char **argv) {
if (Error E = RIOrErr.takeError())
report_error(opts::InputFilename, std::move(E));
RewriteInstance &RI = *RIOrErr.get();
+
+ if (opts::AggregateOnly && !RI.getBinaryContext().isAArch64() &&
+ opts::ArmSPE == 1) {
+ errs() << "BOLT-ERROR: -spe is available only on AArch64.\n";
+ exit(1);
+ }
+
if (!opts::PerfData.empty()) {
if (!opts::AggregateOnly) {
errs() << ToolName
diff --git a/bolt/unittests/Profile/CMakeLists.txt b/bolt/unittests/Profile/CMakeLists.txt
index e0aa0926b49c03..ce01c6c4b949ee 100644
--- a/bolt/unittests/Profile/CMakeLists.txt
+++ b/bolt/unittests/Profile/CMakeLists.txt
@@ -1,11 +1,25 @@
+set(LLVM_LINK_COMPONENTS
+ DebugInfoDWARF
+ Object
+ ${LLVM_TARGETS_TO_BUILD}
+ )
+
add_bolt_unittest(ProfileTests
DataAggregator.cpp
+ PerfSpeEvents.cpp
DISABLE_LLVM_LINK_LLVM_DYLIB
)
target_link_libraries(ProfileTests
PRIVATE
+ LLVMBOLTCore
LLVMBOLTProfile
+ LLVMTargetParser
+ LLVMTestingSupport
)
+foreach (tgt ${BOLT_TARGETS_TO_BUILD})
+ string(TOUPPER "${tgt}" upper)
+ target_compile_definitions(ProfileTests PRIVATE "${upper}_AVAILABLE")
+endforeach()
diff --git a/bolt/unittests/Profile/PerfSpeEvents.cpp b/bolt/unittests/Profile/PerfSpeEvents.cpp
new file mode 100644
index 00000000000000..807a3bb1e07f40
--- /dev/null
+++ b/bolt/unittests/Profile/PerfSpeEvents.cpp
@@ -0,0 +1,173 @@
+//===- bolt/unittests/Profile/PerfSpeEvents.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef AARCH64_AVAILABLE
+
+#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"
+
+using namespace llvm;
+using namespace llvm::bolt;
+using namespace llvm::object;
+using namespace llvm::ELF;
+
+namespace opts {
+extern cl::opt<std::string> ReadPerfEvents;
+} // namespace opts
+
+namespace llvm {
+namespace bolt {
+
+/// Perform checks on perf SPE branch events combined with other SPE or perf
+/// events.
+struct PerfSpeEventsTestHelper : 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.get()), {llvm::outs(), llvm::errs()}));
+ ASSERT_FALSE(!BC);
+ }
+
+ char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};
+ std::unique_ptr<ObjectFile> ObjFile;
+ std::unique_ptr<BinaryContext> BC;
+
+ /// Return true when the expected \p SampleSize profile data are generated and
+ /// contain all the \p ExpectedEventNames.
+ bool checkEvents(uint64_t PID, size_t SampleSize,
+ const StringSet<> &ExpectedEventNames) {
+ DataAggregator DA("<pseudo input>");
+ DA.ParsingBuf = opts::ReadPerfEvents;
+ DA.BC = BC.get();
+ DataAggregator::MMapInfo MMap;
+ DA.BinaryMMapInfo.insert(std::make_pair(PID, MMap));
+
+ DA.parseSpeAsBasicEvents();
+
+ for (auto &EE : ExpectedEventNames)
+ if (!DA.EventNames.contains(EE.first()))
+ return false;
+
+ return SampleSize == DA.BasicSamples.size();
+ }
+};
+
+} // namespace bolt
+} // namespace llvm
+
+// Check that DataAggregator can parseSpeAsBasicEvents for branch events when
+// combined with other event types.
+
+TEST_F(PerfSpeEventsTestHelper, SpeBranches) {
+ // Check perf input with SPE branch events.
+ // Example collection command:
+ // ```
+ // perf record -e 'arm_spe_0/branch_filter=1/u' -- BINARY
+ // ```
+
+ opts::ReadPerfEvents =
+ "1234 instructions: a002 a001\n"
+ "1234 instructions: b002 b001\n"
+ "1234 instructions: c002 c001\n"
+ "1234 instructions: d002 d001\n"
+ "1234 instructions: e002 e001\n";
+
+ EXPECT_TRUE(checkEvents(1234, 10, {"branch-spe:"}));
+}
+
+TEST_F(PerfSpeEventsTestHelper, SpeBranchesAndCycles) {
+ // Check perf input with SPE branch events and cycles.
+ // Example collection command:
+ // ```
+ // perf record -e cycles:u -e 'arm_spe_0/branch_filter=1/u' -- BINARY
+ // ```
+
+ opts::ReadPerfEvents =
+ "1234 instructions: a002 a001\n"
+ "1234 cycles:u: 0 b001\n"
+ "1234 cycles:u: 0 c001\n"
+ "1234 instructions: d002 d001\n"
+ "1234 instructions: e002 e001\n";
+
+ EXPECT_TRUE(checkEvents(1234, 8, {"branch-spe:", "cycles:u:"}));
+}
+
+TEST_F(PerfSpeEventsTestHelper, SpeAnyEventAndCycles) {
+ // Check perf input with any SPE event type and cycles.
+ // Example collection command:
+ // ```
+ // perf record -e cycles:u -e 'arm_spe_0//u' -- BINARY
+ // ```
+
+ opts::ReadPerfEvents =
+ "1234 cycles:u: 0 a001\n"
+ "1234 cycles:u: 0 b001\n"
+ "1234 instructions: 0 c001\n"
+ "1234 instructions: 0 d001\n"
+ "1234 instructions: e002 e001\n";
+
+ EXPECT_TRUE(
+ checkEvents(1234, 6, {"cycles:u:", "instruction-spe:", "branch-spe:"}));
+}
+
+TEST_F(PerfSpeEventsTestHelper, SpeNoBranchPairsRecorded) {
+ // Check perf input that has no SPE branch pairs recorded.
+ // Example collection command:
+ // ```
+ // perf record -e cycles:u -e 'arm_spe_0/load_filter=1,branch_filter=0/u' --
+ // BINARY
+ // ```
+
+ testing::internal::CaptureStderr();
+ opts::ReadPerfEvents =
+ "1234 instructions: 0 a001\n"
+ "1234 cycles:u: 0 b001\n"
+ "1234 instructions: 0 c001\n"
+ "1234 cycles:u: 0 d001\n"
+ "1234 instructions: 0 e001\n";
+
+ EXPECT_TRUE(checkEvents(1234, 5, {"instruction-spe:", "cycles:u:"}));
+
+ std::string Stderr = testing::internal::GetCapturedStderr();
+ EXPECT_EQ(Stderr, "PERF2BOLT-WARNING: no SPE branches found\n");
+}
+
+#endif
>From 61032dec95da15bb7ff646a0178e08f2bb9347e4 Mon Sep 17 00:00:00 2001
From: Paschalis Mpeis <Paschalis.Mpeis at arm.com>
Date: Fri, 20 Dec 2024 15:13:40 +0000
Subject: [PATCH 2/2] clang-format fix
---
bolt/include/bolt/Profile/DataAggregator.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bolt/include/bolt/Profile/DataAggregator.h b/bolt/include/bolt/Profile/DataAggregator.h
index be6e0fbd6347a0..25bfd053e06a51 100644
--- a/bolt/include/bolt/Profile/DataAggregator.h
+++ b/bolt/include/bolt/Profile/DataAggregator.h
@@ -303,7 +303,7 @@ class DataAggregator : public DataReader {
/// ```
/// SPE branch events will have 'ADDR' set to a branch target address while
/// other perf or SPE events will have it set to zero.
- ErrorOr<std::pair<PerfBasicSample,PerfBasicSample>> parseSpeAsBasicSamples();
+ ErrorOr<std::pair<PerfBasicSample, PerfBasicSample>> parseSpeAsBasicSamples();
/// Parse a single perf sample containing a PID associated with an IP and
/// address.
More information about the llvm-commits
mailing list