[llvm] Stop including unordered_map (NFC)
[llvm-project.git] / llvm / tools / llvm-exegesis / lib / PerfHelper.cpp
blob3ff1745e9e06294701654707c9c65ed9ad4fe227
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 //===----------------------------------------------------------------------===//
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
20 #include <cassert>
21 #include <cstddef>
22 #include <errno.h> // for erno
23 #include <string.h> // for strerror()
25 namespace llvm {
26 namespace exegesis {
27 namespace pfm {
29 #ifdef HAVE_LIBPFM
30 static bool isPfmError(int Code) { return Code != PFM_SUCCESS; }
31 #endif
33 bool pfmInitialize() {
34 #ifdef HAVE_LIBPFM
35 return isPfmError(pfm_initialize());
36 #else
37 return true;
38 #endif
41 void pfmTerminate() {
42 #ifdef HAVE_LIBPFM
43 pfm_terminate();
44 #endif
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() {
55 #ifdef HAVE_LIBPFM
56 delete Attr;
58 #endif
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;
68 PerfEvent::PerfEvent(StringRef PfmEventString)
69 : EventString(PfmEventString.str()), Attr(nullptr) {
70 if (PfmEventString != DummyEventString)
71 initRealEvent(PfmEventString);
72 else
73 FullQualifiedEventString = 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";
93 if (Fstr) {
94 FullQualifiedEventString = Fstr;
95 free(Fstr);
97 #endif
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;
113 if (!IsDummyEvent)
114 initRealEvent(E, ProcessID);
117 #ifdef HAVE_LIBPFM
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() {
137 if (!IsDummyEvent)
138 close(FileDescriptor);
141 void Counter::start() {
142 if (!IsDummyEvent)
143 ioctl(FileDescriptor, PERF_EVENT_IOC_RESET, 0);
146 void Counter::stop() {
147 if (!IsDummyEvent)
148 ioctl(FileDescriptor, PERF_EVENT_IOC_DISABLE, 0);
151 int64_t Counter::read() const {
152 auto ValueOrError = readOrError();
153 if (ValueOrError) {
154 if (!ValueOrError.get().empty())
155 return ValueOrError.get()[0];
156 errs() << "Counter has no reading\n";
157 } else
158 errs() << ValueOrError.takeError() << "\n";
159 return -1;
162 llvm::Expected<llvm::SmallVector<int64_t, 4>>
163 Counter::readOrError(StringRef /*unused*/) const {
164 int64_t Count = 0;
165 if (!IsDummyEvent) {
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);
170 } else {
171 Count = 42;
174 llvm::SmallVector<int64_t, 4> Result;
175 Result.push_back(Count);
176 return Result;
179 int Counter::numValues() const { return 1; }
180 #else
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 {
194 if (IsDummyEvent) {
195 llvm::SmallVector<int64_t, 4> Result;
196 Result.push_back(42);
197 return Result;
199 return llvm::make_error<llvm::StringError>("Not implemented",
200 llvm::errc::io_error);
203 int Counter::numValues() const { return 1; }
205 #endif
207 } // namespace pfm
208 } // namespace exegesis
209 } // namespace llvm