1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
//===- 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;
extern cl::opt<bool> ArmSPE;
} // namespace opts
namespace llvm {
namespace bolt {
/// Perform checks on perf SPE branch events.
struct PerfSpeEventsTestHelper : public testing::Test {
void SetUp() override {
initalizeLLVM();
prepareElf();
initializeBOLT();
}
protected:
using Trace = DataAggregator::Trace;
using TakenBranchInfo = DataAggregator::TakenBranchInfo;
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;
/// Helper function to export lists to show the mismatch.
void reportBrStackEventMismatch(
const std::vector<std::pair<Trace, TakenBranchInfo>> &Traces,
const std::vector<std::pair<Trace, TakenBranchInfo>> &ExpectedSamples) {
llvm::errs() << "Traces items: \n";
for (const auto &[Trace, BI] : Traces)
llvm::errs() << "{" << Trace.Branch << ", " << Trace.From << ","
<< Trace.To << ", " << BI.TakenCount << ", "
<< BI.MispredCount << "}" << "\n";
llvm::errs() << "Expected items: \n";
for (const auto &[Trace, BI] : ExpectedSamples)
llvm::errs() << "{" << Trace.Branch << ", " << Trace.From << ", "
<< Trace.To << ", " << BI.TakenCount << ", "
<< BI.MispredCount << "}" << "\n";
}
/// Parse and check SPE brstack as LBR.
void parseAndCheckBrstackEvents(
uint64_t PID,
const std::vector<std::pair<Trace, TakenBranchInfo>> &ExpectedSamples) {
DataAggregator DA("<pseudo input>");
DA.ParsingBuf = opts::ReadPerfEvents;
DA.BC = BC.get();
DataAggregator::MMapInfo MMap;
DA.BinaryMMapInfo.insert(std::make_pair(PID, MMap));
DA.parseBranchEvents();
EXPECT_EQ(DA.Traces.size(), ExpectedSamples.size());
if (DA.Traces.size() != ExpectedSamples.size())
reportBrStackEventMismatch(DA.Traces, ExpectedSamples);
const auto TracesBegin = DA.Traces.begin();
const auto TracesEnd = DA.Traces.end();
for (const auto &BI : ExpectedSamples) {
auto it = find_if(TracesBegin, TracesEnd,
[&BI](const auto &Tr) { return Tr.first == BI.first; });
EXPECT_NE(it, TracesEnd);
EXPECT_EQ(it->second.MispredCount, BI.second.MispredCount);
EXPECT_EQ(it->second.TakenCount, BI.second.TakenCount);
}
}
};
} // namespace bolt
} // namespace llvm
TEST_F(PerfSpeEventsTestHelper, SpeBranchesWithBrstack) {
// Check perf input with SPE branch events as brstack format.
// Example collection command:
// ```
// perf record -e 'arm_spe_0/branch_filter=1/u' -- BINARY
// ```
// How Bolt extracts the branch events:
// ```
// perf script -F pid,brstack --itrace=bl
// ```
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";
// ExpectedSamples contains the aggregated information about
// a branch {{Branch From, To}, {TakenCount, MispredCount}}.
// Consider this example trace: {{0xd123, 0xd456, Trace::BR_ONLY},
// {2,2}}. This entry has a TakenCount = 2, as we have two samples for
// (0xd123, 0xd456) in our input. It also has MispredsCount = 2,
// as 'M' misprediction flag appears in both cases. BR_ONLY means
// the trace only contains branch data.
std::vector<std::pair<Trace, TakenBranchInfo>> ExpectedSamples = {
{{0xa001, 0xa002, Trace::BR_ONLY}, {1, 0}},
{{0xb001, 0xb002, Trace::BR_ONLY}, {1, 0}},
{{0xc456, 0xc789, Trace::BR_ONLY}, {2, 1}},
{{0xd123, 0xd456, Trace::BR_ONLY}, {2, 2}},
{{0xe001, 0xe002, Trace::BR_ONLY}, {1, 0}},
{{0xf001, 0xf002, Trace::BR_ONLY}, {1, 1}}};
parseAndCheckBrstackEvents(1234, ExpectedSamples);
}
#endif
|