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
30 static bool isPfmError(int Code) { return Code != PFM_SUCCESS; }
31 #endif
32 
33 bool pfmInitialize() {
34 #ifdef HAVE_LIBPFM
35   return isPfmError(pfm_initialize());
36 #else
37   return true;
38 #endif
39 }
40 
41 void pfmTerminate() {
42 #ifdef HAVE_LIBPFM
43   pfm_terminate();
44 #endif
45 }
46 
47 PerfEvent::~PerfEvent() {
48 #ifdef HAVE_LIBPFM
49   delete Attr;
50   ;
51 #endif
52 }
53 
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 
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 
86 StringRef PerfEvent::name() const { return EventString; }
87 
88 bool PerfEvent::valid() const { return !FullQualifiedEventString.empty(); }
89 
90 const perf_event_attr *PerfEvent::attribute() const { return Attr; }
91 
92 StringRef PerfEvent::getPfmEventString() const {
93   return FullQualifiedEventString;
94 }
95 
96 #ifdef HAVE_LIBPFM
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 
114 Counter::~Counter() { close(FileDescriptor); }
115 
116 void Counter::start() { ioctl(FileDescriptor, PERF_EVENT_IOC_RESET, 0); }
117 
118 void Counter::stop() { ioctl(FileDescriptor, PERF_EVENT_IOC_DISABLE, 0); }
119 
120 int64_t Counter::read() const {
121   auto ValueOrError = readOrError();
122   if (ValueOrError)
123     return ValueOrError.get();
124 
125   errs() << ValueOrError.takeError() << "\n";
126   return -1;
127 }
128 
129 llvm::Expected<int64_t> Counter::readOrError() const {
130   int64_t Count = 0;
131   ssize_t ReadSize = ::read(FileDescriptor, &Count, sizeof(Count));
132   if (ReadSize != sizeof(Count))
133     return llvm::make_error<llvm::StringError>("Failed to read event counter",
134                                                llvm::errc::io_error);
135 
136   return Count;
137 }
138 
139 #else
140 
141 Counter::Counter(PerfEvent &&Event) : Event(std::move(Event)) {}
142 
143 Counter::~Counter() = default;
144 
145 void Counter::start() {}
146 
147 void Counter::stop() {}
148 
149 int64_t Counter::read() const { return 42; }
150 
151 llvm::Expected<int64_t> Counter::readOrError() const {
152   return llvm::make_error<llvm::StringError>("Not implemented",
153                                              llvm::errc::io_error);
154 }
155 
156 #endif
157 
158 } // namespace pfm
159 } // namespace exegesis
160 } // namespace llvm
161