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
~PerfEvent()47 PerfEvent::~PerfEvent() {
48 #ifdef HAVE_LIBPFM
49 delete Attr;
50 ;
51 #endif
52 }
53
PerfEvent(PerfEvent && Other)54 PerfEvent::PerfEvent(PerfEvent &&Other)
55 : EventString(std::move(Other.EventString)),
56 FullQualifiedEventString(std::move(Other.FullQualifiedEventString)),
57 Attr(Other.Attr) {
58 Other.Attr = nullptr;
59 }
60
PerfEvent(StringRef PfmEventString)61 PerfEvent::PerfEvent(StringRef PfmEventString)
62 : EventString(PfmEventString.str()), Attr(nullptr) {
63 #ifdef HAVE_LIBPFM
64 char *Fstr = nullptr;
65 pfm_perf_encode_arg_t Arg = {};
66 Attr = new perf_event_attr();
67 Arg.attr = Attr;
68 Arg.fstr = &Fstr;
69 Arg.size = sizeof(pfm_perf_encode_arg_t);
70 const int Result = pfm_get_os_event_encoding(EventString.c_str(), PFM_PLM3,
71 PFM_OS_PERF_EVENT, &Arg);
72 if (isPfmError(Result)) {
73 // We don't know beforehand which counters are available (e.g. 6 uops ports
74 // on Sandybridge but 8 on Haswell) so we report the missing counter without
75 // crashing.
76 errs() << pfm_strerror(Result) << " - cannot create event " << EventString
77 << "\n";
78 }
79 if (Fstr) {
80 FullQualifiedEventString = Fstr;
81 free(Fstr);
82 }
83 #endif
84 }
85
name() const86 StringRef PerfEvent::name() const { return EventString; }
87
valid() const88 bool PerfEvent::valid() const { return !FullQualifiedEventString.empty(); }
89
attribute() const90 const perf_event_attr *PerfEvent::attribute() const { return Attr; }
91
getPfmEventString() const92 StringRef PerfEvent::getPfmEventString() const {
93 return FullQualifiedEventString;
94 }
95
96 #ifdef HAVE_LIBPFM
Counter(PerfEvent && E)97 Counter::Counter(PerfEvent &&E) : Event(std::move(E)){
98 assert(Event.valid());
99 const pid_t Pid = 0; // measure current process/thread.
100 const int Cpu = -1; // measure any processor.
101 const int GroupFd = -1; // no grouping of counters.
102 const uint32_t Flags = 0;
103 perf_event_attr AttrCopy = *Event.attribute();
104 FileDescriptor = perf_event_open(&AttrCopy, Pid, Cpu, GroupFd, Flags);
105 if (FileDescriptor == -1) {
106 errs() << "Unable to open event. ERRNO: " << strerror(errno)
107 << ". Make sure your kernel allows user "
108 "space perf monitoring.\nYou may want to try:\n$ sudo sh "
109 "-c 'echo -1 > /proc/sys/kernel/perf_event_paranoid'\n";
110 }
111 assert(FileDescriptor != -1 && "Unable to open event");
112 }
113
~Counter()114 Counter::~Counter() { close(FileDescriptor); }
115
start()116 void Counter::start() { ioctl(FileDescriptor, PERF_EVENT_IOC_RESET, 0); }
117
stop()118 void Counter::stop() { ioctl(FileDescriptor, PERF_EVENT_IOC_DISABLE, 0); }
119
read() const120 int64_t Counter::read() const {
121 auto ValueOrError = readOrError();
122 if (ValueOrError) {
123 if (!ValueOrError.get().empty())
124 return ValueOrError.get()[0];
125 errs() << "Counter has no reading\n";
126 } else
127 errs() << ValueOrError.takeError() << "\n";
128 return -1;
129 }
130
131 llvm::Expected<llvm::SmallVector<int64_t, 4>>
readOrError(StringRef) const132 Counter::readOrError(StringRef /*unused*/) const {
133 int64_t Count = 0;
134 ssize_t ReadSize = ::read(FileDescriptor, &Count, sizeof(Count));
135 if (ReadSize != sizeof(Count))
136 return llvm::make_error<llvm::StringError>("Failed to read event counter",
137 llvm::errc::io_error);
138 llvm::SmallVector<int64_t, 4> Result;
139 Result.push_back(Count);
140 return Result;
141 }
142
numValues() const143 int Counter::numValues() const { return 1; }
144 #else
145
Counter(PerfEvent && Event)146 Counter::Counter(PerfEvent &&Event) : Event(std::move(Event)) {}
147
148 Counter::~Counter() = default;
149
start()150 void Counter::start() {}
151
stop()152 void Counter::stop() {}
153
read() const154 int64_t Counter::read() const { return 42; }
155
156 llvm::Expected<llvm::SmallVector<int64_t, 4>>
readOrError(StringRef) const157 Counter::readOrError(StringRef /*unused*/) const {
158 return llvm::make_error<llvm::StringError>("Not implemented",
159 llvm::errc::io_error);
160 }
161
numValues() const162 int Counter::numValues() const { return 1; }
163
164 #endif
165
166 } // namespace pfm
167 } // namespace exegesis
168 } // namespace llvm
169