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