xref: /llvm-project/llvm/tools/llvm-exegesis/lib/PerfHelper.cpp (revision faf675ce34ee1e2c6105e9a816f220412fd2f8d5)
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