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