1 //===-- PerfHelper.cpp ------------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
15 #include <perfmon/perf_event.h>
16 #include <perfmon/pfmlib.h>
17 #include <perfmon/pfmlib_perf_event.h>
22 #include <errno.h> // for erno
23 #include <string.h> // for strerror()
30 static bool isPfmError(int Code
) { return Code
!= PFM_SUCCESS
; }
33 bool pfmInitialize() {
35 return isPfmError(pfm_initialize());
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).
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";
54 PerfEvent::~PerfEvent() {
61 PerfEvent::PerfEvent(PerfEvent
&&Other
)
62 : EventString(std::move(Other
.EventString
)),
63 FullQualifiedEventString(std::move(Other
.FullQualifiedEventString
)),
68 PerfEvent::PerfEvent(StringRef PfmEventString
)
69 : EventString(PfmEventString
.str()), Attr(nullptr) {
70 if (PfmEventString
!= DummyEventString
)
71 initRealEvent(PfmEventString
);
73 FullQualifiedEventString
= PfmEventString
;
76 void PerfEvent::initRealEvent(StringRef PfmEventString
) {
79 pfm_perf_encode_arg_t Arg
= {};
80 Attr
= new perf_event_attr();
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
90 errs() << pfm_strerror(Result
) << " - cannot create event " << EventString
94 FullQualifiedEventString
= Fstr
;
100 StringRef
PerfEvent::name() const { return EventString
; }
102 bool PerfEvent::valid() const { return !FullQualifiedEventString
.empty(); }
104 const perf_event_attr
*PerfEvent::attribute() const { return Attr
; }
106 StringRef
PerfEvent::getPfmEventString() const {
107 return FullQualifiedEventString
;
110 Counter::Counter(PerfEvent
&&E
, pid_t ProcessID
) : Event(std::move(E
)) {
111 assert(Event
.valid());
112 IsDummyEvent
= Event
.name() == PerfEvent::DummyEventString
;
114 initRealEvent(E
, ProcessID
);
118 void Counter::initRealEvent(const PerfEvent
&E
, pid_t ProcessID
) {
119 const int Cpu
= -1; // measure any processor.
120 const int GroupFd
= -1; // no grouping of counters.
121 const uint32_t Flags
= 0;
122 perf_event_attr AttrCopy
= *Event
.attribute();
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";
133 assert(FileDescriptor
!= -1 && "Unable to open event");
136 Counter::~Counter() {
138 close(FileDescriptor
);
141 void Counter::start() {
143 ioctl(FileDescriptor
, PERF_EVENT_IOC_RESET
, 0);
146 void Counter::stop() {
148 ioctl(FileDescriptor
, PERF_EVENT_IOC_DISABLE
, 0);
151 int64_t Counter::read() const {
152 auto ValueOrError
= readOrError();
154 if (!ValueOrError
.get().empty())
155 return ValueOrError
.get()[0];
156 errs() << "Counter has no reading\n";
158 errs() << ValueOrError
.takeError() << "\n";
162 llvm::Expected
<llvm::SmallVector
<int64_t, 4>>
163 Counter::readOrError(StringRef
/*unused*/) const {
166 ssize_t ReadSize
= ::read(FileDescriptor
, &Count
, sizeof(Count
));
167 if (ReadSize
!= sizeof(Count
))
168 return llvm::make_error
<llvm::StringError
>("Failed to read event counter",
169 llvm::errc::io_error
);
174 llvm::SmallVector
<int64_t, 4> Result
;
175 Result
.push_back(Count
);
179 int Counter::numValues() const { return 1; }
182 void Counter::initRealEvent(const PerfEvent
&, pid_t ProcessID
) {}
184 Counter::~Counter() = default;
186 void Counter::start() {}
188 void Counter::stop() {}
190 int64_t Counter::read() const { return 42; }
192 llvm::Expected
<llvm::SmallVector
<int64_t, 4>>
193 Counter::readOrError(StringRef
/*unused*/) const {
195 llvm::SmallVector
<int64_t, 4> Result
;
196 Result
.push_back(42);
199 return llvm::make_error
<llvm::StringError
>("Not implemented",
200 llvm::errc::io_error
);
203 int Counter::numValues() const { return 1; }
208 } // namespace exegesis