1 //===-- PerfHelper.cpp ------------------------------------------*- C++ -*-===//
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "PerfHelper.h"
10 #include "llvm/Config/config.h"
11 #include "llvm/Support/Errc.h"
12 #include "llvm/Support/Error.h"
13 #include "llvm/Support/raw_ostream.h"
14 #ifdef HAVE_LIBPFM
15 #include <perfmon/perf_event.h>
16 #include <perfmon/pfmlib.h>
17 #include <perfmon/pfmlib_perf_event.h>
18 #endif
19
20 #include <cassert>
21 #include <cstddef>
22 #include <errno.h> // for erno
23 #include <string.h> // for strerror()
24
25 namespace llvm {
26 namespace exegesis {
27 namespace pfm {
28
29 #ifdef HAVE_LIBPFM
isPfmError(int Code)30 static bool isPfmError(int Code) { return Code != PFM_SUCCESS; }
31 #endif
32
pfmInitialize()33 bool pfmInitialize() {
34 #ifdef HAVE_LIBPFM
35 return isPfmError(pfm_initialize());
36 #else
37 return true;
38 #endif
39 }
40
pfmTerminate()41 void pfmTerminate() {
42 #ifdef HAVE_LIBPFM
43 pfm_terminate();
44 #endif
45 }
46
47 // Performance counters may be unavailable for a number of reasons (such as
48 // kernel.perf_event_paranoid restriction or CPU being unknown to libpfm).
49 //
50 // Dummy event can be specified to skip interaction with real performance
51 // counters while still passing control to the generated code snippet.
52 const char *const PerfEvent::DummyEventString = "not-really-an-event";
53
~PerfEvent()54 PerfEvent::~PerfEvent() {
55 #ifdef HAVE_LIBPFM
56 delete Attr;
57 ;
58 #endif
59 }
60
PerfEvent(PerfEvent && Other)61 PerfEvent::PerfEvent(PerfEvent &&Other)
62 : EventString(std::move(Other.EventString)),
63 FullQualifiedEventString(std::move(Other.FullQualifiedEventString)),
64 Attr(Other.Attr) {
65 Other.Attr = nullptr;
66 }
67
PerfEvent(StringRef PfmEventString)68 PerfEvent::PerfEvent(StringRef PfmEventString)
69 : EventString(PfmEventString.str()), Attr(nullptr) {
70 if (PfmEventString != DummyEventString)
71 initRealEvent(PfmEventString);
72 else
73 FullQualifiedEventString = PfmEventString;
74 }
75
initRealEvent(StringRef PfmEventString)76 void PerfEvent::initRealEvent(StringRef PfmEventString) {
77 #ifdef HAVE_LIBPFM
78 char *Fstr = nullptr;
79 pfm_perf_encode_arg_t Arg = {};
80 Attr = new perf_event_attr();
81 Arg.attr = Attr;
82 Arg.fstr = &Fstr;
83 Arg.size = sizeof(pfm_perf_encode_arg_t);
84 const int Result = pfm_get_os_event_encoding(EventString.c_str(), PFM_PLM3,
85 PFM_OS_PERF_EVENT, &Arg);
86 if (isPfmError(Result)) {
87 // We don't know beforehand which counters are available (e.g. 6 uops ports
88 // on Sandybridge but 8 on Haswell) so we report the missing counter without
89 // crashing.
90 errs() << pfm_strerror(Result) << " - cannot create event " << EventString
91 << "\n";
92 }
93 if (Fstr) {
94 FullQualifiedEventString = Fstr;
95 free(Fstr);
96 }
97 #endif
98 }
99
name() const100 StringRef PerfEvent::name() const { return EventString; }
101
valid() const102 bool PerfEvent::valid() const { return !FullQualifiedEventString.empty(); }
103
attribute() const104 const perf_event_attr *PerfEvent::attribute() const { return Attr; }
105
getPfmEventString() const106 StringRef PerfEvent::getPfmEventString() const {
107 return FullQualifiedEventString;
108 }
109
ConfiguredEvent(PerfEvent && EventToConfigure)110 ConfiguredEvent::ConfiguredEvent(PerfEvent &&EventToConfigure)
111 : Event(std::move(EventToConfigure)) {
112 assert(Event.valid());
113 }
114
115 #ifdef HAVE_LIBPFM
initRealEvent(const pid_t ProcessID,const int GroupFD)116 void ConfiguredEvent::initRealEvent(const pid_t ProcessID, const int GroupFD) {
117 const int CPU = -1;
118 const uint32_t Flags = 0;
119 perf_event_attr AttrCopy = *Event.attribute();
120 FileDescriptor = perf_event_open(&AttrCopy, ProcessID, CPU, GroupFD, Flags);
121 if (FileDescriptor == -1) {
122 errs() << "Unable to open event. ERRNO: " << strerror(errno)
123 << ". Make sure your kernel allows user "
124 "space perf monitoring.\nYou may want to try:\n$ sudo sh "
125 "-c 'echo -1 > /proc/sys/kernel/perf_event_paranoid'.\n"
126 << "If you are debugging and just want to execute the snippet "
127 "without actually reading performance counters, "
128 "pass --use-dummy-perf-counters command line option.\n";
129 }
130 assert(FileDescriptor != -1 && "Unable to open event");
131 }
132
133 Expected<SmallVector<int64_t>>
readOrError(StringRef) const134 ConfiguredEvent::readOrError(StringRef /*unused*/) const {
135 int64_t Count = 0;
136 ssize_t ReadSize = ::read(FileDescriptor, &Count, sizeof(Count));
137
138 if (ReadSize != sizeof(Count))
139 return make_error<StringError>("Failed to read event counter",
140 errc::io_error);
141
142 SmallVector<int64_t, 1> Result;
143 Result.push_back(Count);
144 return Result;
145 }
146
~ConfiguredEvent()147 ConfiguredEvent::~ConfiguredEvent() { close(FileDescriptor); }
148 #else
initRealEvent(pid_t ProcessID,const int GroupFD)149 void ConfiguredEvent::initRealEvent(pid_t ProcessID, const int GroupFD) {}
150
151 Expected<SmallVector<int64_t>>
readOrError(StringRef) const152 ConfiguredEvent::readOrError(StringRef /*unused*/) const {
153 return make_error<StringError>("Not implemented",
154 errc::function_not_supported);
155 }
156
157 ConfiguredEvent::~ConfiguredEvent() = default;
158 #endif // HAVE_LIBPFM
159
CounterGroup(PerfEvent && E,std::vector<PerfEvent> && ValEvents,pid_t ProcessID)160 CounterGroup::CounterGroup(PerfEvent &&E, std::vector<PerfEvent> &&ValEvents,
161 pid_t ProcessID)
162 : EventCounter(std::move(E)) {
163 IsDummyEvent = EventCounter.isDummyEvent();
164
165 for (auto &&ValEvent : ValEvents)
166 ValidationEventCounters.emplace_back(std::move(ValEvent));
167
168 if (!IsDummyEvent)
169 initRealEvent(ProcessID);
170 }
171
172 #ifdef HAVE_LIBPFM
initRealEvent(pid_t ProcessID)173 void CounterGroup::initRealEvent(pid_t ProcessID) {
174 EventCounter.initRealEvent(ProcessID);
175
176 for (auto &ValCounter : ValidationEventCounters)
177 ValCounter.initRealEvent(ProcessID, getFileDescriptor());
178 }
179
start()180 void CounterGroup::start() {
181 if (!IsDummyEvent)
182 ioctl(getFileDescriptor(), PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP);
183 }
184
stop()185 void CounterGroup::stop() {
186 if (!IsDummyEvent)
187 ioctl(getFileDescriptor(), PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP);
188 }
189
190 Expected<SmallVector<int64_t, 4>>
readOrError(StringRef FunctionBytes) const191 CounterGroup::readOrError(StringRef FunctionBytes) const {
192 if (!IsDummyEvent)
193 return EventCounter.readOrError(FunctionBytes);
194 else
195 return SmallVector<int64_t, 1>(1, 42);
196 }
197
198 Expected<SmallVector<int64_t>>
readValidationCountersOrError() const199 CounterGroup::readValidationCountersOrError() const {
200 SmallVector<int64_t, 4> Result;
201 for (const auto &ValCounter : ValidationEventCounters) {
202 Expected<SmallVector<int64_t>> ValueOrError =
203 ValCounter.readOrError(StringRef());
204
205 if (!ValueOrError)
206 return ValueOrError.takeError();
207
208 // Reading a validation counter will only return a single value, so it is
209 // safe to only append the first value here. Also assert that this is true.
210 assert(ValueOrError->size() == 1 &&
211 "Validation counters should only return a single value");
212 Result.push_back((*ValueOrError)[0]);
213 }
214 return Result;
215 }
216
numValues() const217 int CounterGroup::numValues() const { return 1; }
218 #else
219
initRealEvent(pid_t ProcessID)220 void CounterGroup::initRealEvent(pid_t ProcessID) {}
221
start()222 void CounterGroup::start() {}
223
stop()224 void CounterGroup::stop() {}
225
226 Expected<SmallVector<int64_t, 4>>
readOrError(StringRef) const227 CounterGroup::readOrError(StringRef /*unused*/) const {
228 if (IsDummyEvent) {
229 SmallVector<int64_t, 4> Result;
230 Result.push_back(42);
231 return Result;
232 }
233 return make_error<StringError>("Not implemented", errc::io_error);
234 }
235
236 Expected<SmallVector<int64_t>>
readValidationCountersOrError() const237 CounterGroup::readValidationCountersOrError() const {
238 return SmallVector<int64_t>(0);
239 }
240
numValues() const241 int CounterGroup::numValues() const { return 1; }
242
243 #endif
244
245 } // namespace pfm
246 } // namespace exegesis
247 } // namespace llvm
248