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 |
30 | static bool isPfmError(int Code) { return Code != PFM_SUCCESS; } |
31 | #endif |
32 | |
33 | bool pfmInitialize() { |
34 | #ifdef HAVE_LIBPFM |
35 | return isPfmError(pfm_initialize()); |
36 | #else |
37 | return true; |
38 | #endif |
39 | } |
40 | |
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 | |
54 | PerfEvent::~PerfEvent() { |
55 | #ifdef HAVE_LIBPFM |
56 | delete Attr; |
57 | ; |
58 | #endif |
59 | } |
60 | |
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 | |
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 | |
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 | |
100 | StringRef PerfEvent::name() const { return EventString; } |
101 | |
102 | bool PerfEvent::valid() const { return !FullQualifiedEventString.empty(); } |
103 | |
104 | const perf_event_attr *PerfEvent::attribute() const { return Attr; } |
105 | |
106 | StringRef PerfEvent::getPfmEventString() const { |
107 | return FullQualifiedEventString; |
108 | } |
109 | |
110 | ConfiguredEvent::ConfiguredEvent(PerfEvent &&EventToConfigure) |
111 | : Event(std::move(EventToConfigure)) { |
112 | assert(Event.valid()); |
113 | } |
114 | |
115 | #ifdef HAVE_LIBPFM |
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>> |
134 | 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 | |
147 | ConfiguredEvent::~ConfiguredEvent() { close(FileDescriptor); } |
148 | #else |
149 | void ConfiguredEvent::initRealEvent(pid_t ProcessID, const int GroupFD) {} |
150 | |
151 | Expected<SmallVector<int64_t>> |
152 | ConfiguredEvent::readOrError(StringRef /*unused*/) const { |
153 | return make_error<StringError>(Args: "Not implemented" , |
154 | Args: errc::function_not_supported); |
155 | } |
156 | |
157 | ConfiguredEvent::~ConfiguredEvent() = default; |
158 | #endif // HAVE_LIBPFM |
159 | |
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(args: std::move(ValEvent)); |
167 | |
168 | if (!IsDummyEvent) |
169 | initRealEvent(ProcessID); |
170 | } |
171 | |
172 | #ifdef HAVE_LIBPFM |
173 | void CounterGroup::initRealEvent(pid_t ProcessID) { |
174 | EventCounter.initRealEvent(ProcessID); |
175 | |
176 | for (auto &ValCounter : ValidationEventCounters) |
177 | ValCounter.initRealEvent(ProcessID, getFileDescriptor()); |
178 | } |
179 | |
180 | void CounterGroup::start() { |
181 | if (!IsDummyEvent) |
182 | ioctl(getFileDescriptor(), PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP); |
183 | } |
184 | |
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>> |
191 | 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>> |
199 | 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 | |
217 | int CounterGroup::numValues() const { return 1; } |
218 | #else |
219 | |
220 | void CounterGroup::initRealEvent(pid_t ProcessID) {} |
221 | |
222 | void CounterGroup::start() {} |
223 | |
224 | void CounterGroup::stop() {} |
225 | |
226 | Expected<SmallVector<int64_t, 4>> |
227 | CounterGroup::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 | |
236 | Expected<SmallVector<int64_t>> |
237 | CounterGroup::readValidationCountersOrError() const { |
238 | return SmallVector<int64_t>(0); |
239 | } |
240 | |
241 | int CounterGroup::numValues() const { return 1; } |
242 | |
243 | #endif |
244 | |
245 | } // namespace pfm |
246 | } // namespace exegesis |
247 | } // namespace llvm |
248 | |