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
25namespace llvm {
26namespace exegesis {
27namespace pfm {
28
29#ifdef HAVE_LIBPFM
30static bool isPfmError(int Code) { return Code != PFM_SUCCESS; }
31#endif
32
33bool pfmInitialize() {
34#ifdef HAVE_LIBPFM
35 return isPfmError(pfm_initialize());
36#else
37 return true;
38#endif
39}
40
41void 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.
52const char *const PerfEvent::DummyEventString = "not-really-an-event";
53
54PerfEvent::~PerfEvent() {
55#ifdef HAVE_LIBPFM
56 delete Attr;
57 ;
58#endif
59}
60
61PerfEvent::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
68PerfEvent::PerfEvent(StringRef PfmEventString)
69 : EventString(PfmEventString.str()), Attr(nullptr) {
70 if (PfmEventString != DummyEventString)
71 initRealEvent(PfmEventString);
72 else
73 FullQualifiedEventString = PfmEventString;
74}
75
76void 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
100StringRef PerfEvent::name() const { return EventString; }
101
102bool PerfEvent::valid() const { return !FullQualifiedEventString.empty(); }
103
104const perf_event_attr *PerfEvent::attribute() const { return Attr; }
105
106StringRef PerfEvent::getPfmEventString() const {
107 return FullQualifiedEventString;
108}
109
110ConfiguredEvent::ConfiguredEvent(PerfEvent &&EventToConfigure)
111 : Event(std::move(EventToConfigure)) {
112 assert(Event.valid());
113}
114
115#ifdef HAVE_LIBPFM
116void 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
133Expected<SmallVector<int64_t>>
134ConfiguredEvent::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
147ConfiguredEvent::~ConfiguredEvent() { close(FileDescriptor); }
148#else
149void ConfiguredEvent::initRealEvent(pid_t ProcessID, const int GroupFD) {}
150
151Expected<SmallVector<int64_t>>
152ConfiguredEvent::readOrError(StringRef /*unused*/) const {
153 return make_error<StringError>(Args: "Not implemented",
154 Args: errc::function_not_supported);
155}
156
157ConfiguredEvent::~ConfiguredEvent() = default;
158#endif // HAVE_LIBPFM
159
160CounterGroup::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(args: std::move(ValEvent));
167
168 if (!IsDummyEvent)
169 initRealEvent(ProcessID);
170}
171
172#ifdef HAVE_LIBPFM
173void CounterGroup::initRealEvent(pid_t ProcessID) {
174 EventCounter.initRealEvent(ProcessID);
175
176 for (auto &ValCounter : ValidationEventCounters)
177 ValCounter.initRealEvent(ProcessID, getFileDescriptor());
178}
179
180void CounterGroup::start() {
181 if (!IsDummyEvent)
182 ioctl(getFileDescriptor(), PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP);
183}
184
185void CounterGroup::stop() {
186 if (!IsDummyEvent)
187 ioctl(getFileDescriptor(), PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP);
188}
189
190Expected<SmallVector<int64_t, 4>>
191CounterGroup::readOrError(StringRef FunctionBytes) const {
192 if (!IsDummyEvent)
193 return EventCounter.readOrError(FunctionBytes);
194 else
195 return SmallVector<int64_t, 1>(1, 42);
196}
197
198Expected<SmallVector<int64_t>>
199CounterGroup::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
217int CounterGroup::numValues() const { return 1; }
218#else
219
220void CounterGroup::initRealEvent(pid_t ProcessID) {}
221
222void CounterGroup::start() {}
223
224void CounterGroup::stop() {}
225
226Expected<SmallVector<int64_t, 4>>
227CounterGroup::readOrError(StringRef /*unused*/) const {
228 if (IsDummyEvent) {
229 SmallVector<int64_t, 4> Result;
230 Result.push_back(Elt: 42);
231 return Result;
232 }
233 return make_error<StringError>(Args: "Not implemented", Args: errc::io_error);
234}
235
236Expected<SmallVector<int64_t>>
237CounterGroup::readValidationCountersOrError() const {
238 return SmallVector<int64_t>(0);
239}
240
241int CounterGroup::numValues() const { return 1; }
242
243#endif
244
245} // namespace pfm
246} // namespace exegesis
247} // namespace llvm
248