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