253 lines · cpp
1//===- bolt/unittests/Profile/PerfSpeEvents.cpp ---------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifdef AARCH64_AVAILABLE10 11#include "bolt/Core/BinaryContext.h"12#include "bolt/Profile/DataAggregator.h"13#include "llvm/BinaryFormat/ELF.h"14#include "llvm/DebugInfo/DWARF/DWARFContext.h"15#include "llvm/Support/CommandLine.h"16#include "llvm/Support/TargetSelect.h"17#include "gtest/gtest.h"18 19using namespace llvm;20using namespace llvm::bolt;21using namespace llvm::object;22using namespace llvm::ELF;23 24namespace opts {25extern cl::opt<std::string> ReadPerfEvents;26extern cl::opt<bool> ArmSPE;27} // namespace opts28 29namespace llvm {30namespace bolt {31 32/// Perform checks on perf SPE branch events.33struct PerfSpeEventsTestHelper : public testing::Test {34 void SetUp() override {35 initalizeLLVM();36 prepareElf();37 initializeBOLT();38 }39 40protected:41 using Trace = DataAggregator::Trace;42 using TakenBranchInfo = DataAggregator::TakenBranchInfo;43 44 void initalizeLLVM() {45 llvm::InitializeAllTargetInfos();46 llvm::InitializeAllTargetMCs();47 llvm::InitializeAllAsmParsers();48 llvm::InitializeAllDisassemblers();49 llvm::InitializeAllTargets();50 llvm::InitializeAllAsmPrinters();51 }52 53 void prepareElf() {54 memcpy(ElfBuf, "\177ELF", 4);55 ELF64LE::Ehdr *EHdr = reinterpret_cast<typename ELF64LE::Ehdr *>(ElfBuf);56 EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64;57 EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB;58 EHdr->e_machine = llvm::ELF::EM_AARCH64;59 MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF");60 ObjFile = cantFail(ObjectFile::createObjectFile(Source));61 }62 63 void initializeBOLT() {64 Relocation::Arch = ObjFile->makeTriple().getArch();65 BC = cantFail(BinaryContext::createBinaryContext(66 ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),67 ObjFile->getFileName(), nullptr, /*IsPIC*/ false,68 DWARFContext::create(*ObjFile), {llvm::outs(), llvm::errs()}));69 ASSERT_FALSE(!BC);70 }71 72 char ElfBuf[sizeof(typename ELF64LE::Ehdr)] = {};73 std::unique_ptr<ObjectFile> ObjFile;74 std::unique_ptr<BinaryContext> BC;75 76 /// Helper function to export lists to show the mismatch.77 void reportBrStackEventMismatch(78 const std::vector<std::pair<Trace, TakenBranchInfo>> &Traces,79 const std::vector<std::pair<Trace, TakenBranchInfo>> &ExpectedSamples) {80 llvm::errs() << "Traces items: \n";81 for (const auto &[Trace, BI] : Traces)82 llvm::errs() << "{" << Trace.Branch << ", " << Trace.From << ","83 << Trace.To << ", " << BI.TakenCount << ", "84 << BI.MispredCount << "}" << "\n";85 86 llvm::errs() << "Expected items: \n";87 for (const auto &[Trace, BI] : ExpectedSamples)88 llvm::errs() << "{" << Trace.Branch << ", " << Trace.From << ", "89 << Trace.To << ", " << BI.TakenCount << ", "90 << BI.MispredCount << "}" << "\n";91 }92 93 /// Parse and check SPE brstack as LBR.94 void parseAndCheckBrstackEvents(95 uint64_t PID,96 const std::vector<std::pair<Trace, TakenBranchInfo>> &ExpectedSamples) {97 DataAggregator DA("<pseudo input>");98 DA.ParsingBuf = opts::ReadPerfEvents;99 DA.BC = BC.get();100 DataAggregator::MMapInfo MMap;101 DA.BinaryMMapInfo.insert(std::make_pair(PID, MMap));102 103 DA.parseBranchEvents();104 105 EXPECT_EQ(DA.Traces.size(), ExpectedSamples.size());106 if (DA.Traces.size() != ExpectedSamples.size())107 reportBrStackEventMismatch(DA.Traces, ExpectedSamples);108 109 const auto TracesBegin = DA.Traces.begin();110 const auto TracesEnd = DA.Traces.end();111 for (const auto &BI : ExpectedSamples) {112 auto it = find_if(TracesBegin, TracesEnd,113 [&BI](const auto &Tr) { return Tr.first == BI.first; });114 115 EXPECT_NE(it, TracesEnd);116 EXPECT_EQ(it->second.MispredCount, BI.second.MispredCount);117 EXPECT_EQ(it->second.TakenCount, BI.second.TakenCount);118 }119 }120};121 122} // namespace bolt123} // namespace llvm124 125TEST_F(PerfSpeEventsTestHelper, SpeBranchesWithBrstack) {126 // Check perf input with SPE branch events as brstack format.127 // Example collection command:128 // ```129 // perf record -e 'arm_spe_0/branch_filter=1/u' -- BINARY130 // ```131 // How Bolt extracts the branch events:132 // ```133 // perf script -F pid,brstack --itrace=bl134 // ```135 136 opts::ArmSPE = true;137 opts::ReadPerfEvents = " 1234 0xa001/0xa002/PN/-/-/10/COND/-\n"138 " 1234 0xb001/0xb002/P/-/-/4/RET/-\n"139 " 1234 0xc456/0xc789/P/-/-/13/-/-\n"140 " 1234 0xd123/0xd456/M/-/-/7/RET/-\n"141 " 1234 0xe001/0xe002/P/-/-/14/RET/-\n"142 " 1234 0xd123/0xd456/M/-/-/7/RET/-\n"143 " 1234 0xf001/0xf002/MN/-/-/8/COND/-\n"144 " 1234 0xc456/0xc789/M/-/-/13/-/-\n";145 146 // ExpectedSamples contains the aggregated information about147 // a branch {{Branch From, To}, {TakenCount, MispredCount}}.148 // Consider this example trace: {{0xd123, 0xd456, Trace::BR_ONLY},149 // {2,2}}. This entry has a TakenCount = 2, as we have two samples for150 // (0xd123, 0xd456) in our input. It also has MispredsCount = 2,151 // as 'M' misprediction flag appears in both cases. BR_ONLY means152 // the trace only contains branch data.153 std::vector<std::pair<Trace, TakenBranchInfo>> ExpectedSamples = {154 {{0xa001, 0xa002, Trace::BR_ONLY}, {1, 0}},155 {{0xb001, 0xb002, Trace::BR_ONLY}, {1, 0}},156 {{0xc456, 0xc789, Trace::BR_ONLY}, {2, 1}},157 {{0xd123, 0xd456, Trace::BR_ONLY}, {2, 2}},158 {{0xe001, 0xe002, Trace::BR_ONLY}, {1, 0}},159 {{0xf001, 0xf002, Trace::BR_ONLY}, {1, 1}}};160 161 parseAndCheckBrstackEvents(1234, ExpectedSamples);162}163 164TEST_F(PerfSpeEventsTestHelper, SpeBranchesWithBrstackAndPbt) {165 // Check perf input with SPE branch events as brstack format by166 // combining with the previous branch target address (named as PBT).167 // Example collection command:168 // ```169 // perf record -e 'arm_spe_0/branch_filter=1/u' -- BINARY170 // ```171 // How Bolt extracts the branch events:172 // ```173 // perf script -F pid,brstack --itrace=bl174 // ```175 176 opts::ArmSPE = true;177 opts::ReadPerfEvents =178 // "<PID> <SRC>/<DEST>/PN/-/-/10/COND/- <NULL>/<PBT>/-/-/-/0//-\n"179 " 4567 0xa002/0xa003/PN/-/-/10/COND/- 0x0/0xa001/-/-/-/0//-\n"180 " 4567 0xb002/0xb003/P/-/-/4/RET/- 0x0/0xb001/-/-/-/0//-\n"181 " 4567 0xc456/0xc789/P/-/-/13/-/- 0x0/0xc123/-/-/-/0//-\n"182 " 4567 0xd456/0xd789/M/-/-/7/RET/- 0x0/0xd123/-/-/-/0//-\n"183 " 4567 0xe005/0xe009/P/-/-/14/RET/- 0x0/0xe001/-/-/-/0//-\n"184 " 4567 0xd456/0xd789/M/-/-/7/RET/- 0x0/0xd123/-/-/-/0//-\n"185 " 4567 0xf002/0xf003/MN/-/-/8/COND/- 0x0/0xf001/-/-/-/0//-\n"186 " 4567 0xc456/0xc789/P/-/-/13/-/- 0x0/0xc123/-/-/-/0//-\n";187 188 // ExpectedSamples contains the aggregated information about189 // a branch {{From, To, TraceTo}, {TakenCount, MispredCount}}.190 // Where191 // - From: is the source address of the sampled branch operation.192 // - To: is the target address of the sampled branch operation.193 // - TraceTo could be either194 // - A 'Type = Trace::BR_ONLY', which means the trace only contains branch195 // data.196 // - Or an address, when the trace contains information about the previous197 // branch.198 //199 // When FEAT_SPE_PBT is present, Arm SPE emits two records per sample:200 // - the current branch (Spe.From/Spe.To), and201 // - the previous taken branch target (PBT) (PBT.From, PBT.To).202 //203 // Together they behave like a depth-1 branch stack where:204 // - the PBT entry is always taken205 // - the current branch entry may represent a taken branch or a fall-through206 // - the destination (Spe.To) is the architecturally executed target207 //208 // There can be fall-throughs to be inferred between the PBT entry and209 // the current branch (Spe.From), but there cannot be between current210 // branch's (Spe.From/Spe.To).211 //212 // PBT records only the target address (PBT.To), meaning we have no213 // information as the branch source (PBT.From=0x0), branch type, and the214 // prediction bit.215 //216 // Consider the trace pair:217 // {{Spe.From, Spe.To, Type}, {TK, MP}},218 // {{PBT.From, PBT.To, TraceTo}, {TK, MP}}219 // {{0xd456, 0xd789, Trace::BR_ONLY}, {2, 2}}, {{0x0, 0xd123, 0xd456}, {2, 0}}220 //221 // The first entry is the Spe record, which represents a trace from 0xd456222 // (Spe.From) to 0xd789 (Spe.To). Type = Trace::BR_ONLY, as Bolt processes the223 // current branch event first. At this point we have no information about the224 // previous trace (PBT). This entry has a TakenCount = 2, as we have two225 // samples for (0xd456, 0xd789) in our input. It also has MispredsCount = 2,226 // as 'M' misprediction flag appears in both cases.227 //228 // The second entry is the PBT record. TakenCount = 2 because the229 // (PBT.From = 0x0, PBT.To = 0xd123) branch target appears twice in the input,230 // and MispredsCount = 0 because prediction data is absent. There is no branch231 // source information, so the PBT.From field is zero (0x0). TraceTo = 0xd456232 // connect the flow from the previous taken branch at 0xd123 (PBT.To) to the233 // current source branch at 0xd456 (Spe.From), which then continues to 0xd789234 // (Spe.To).235 std::vector<std::pair<Trace, TakenBranchInfo>> ExpectedSamples = {236 {{0xa002, 0xa003, Trace::BR_ONLY}, {1, 0}},237 {{0x0, 0xa001, 0xa002}, {1, 0}},238 {{0xb002, 0xb003, Trace::BR_ONLY}, {1, 0}},239 {{0x0, 0xb001, 0xb002}, {1, 0}},240 {{0xc456, 0xc789, Trace::BR_ONLY}, {2, 0}},241 {{0x0, 0xc123, 0xc456}, {2, 0}},242 {{0xd456, 0xd789, Trace::BR_ONLY}, {2, 2}},243 {{0x0, 0xd123, 0xd456}, {2, 0}},244 {{0xe005, 0xe009, Trace::BR_ONLY}, {1, 0}},245 {{0x0, 0xe001, 0xe005}, {1, 0}},246 {{0xf002, 0xf003, Trace::BR_ONLY}, {1, 1}},247 {{0x0, 0xf001, 0xf002}, {1, 0}}};248 249 parseAndCheckBrstackEvents(4567, ExpectedSamples);250}251 252#endif253