1 //===-- PerfHelper.cpp ------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "PerfHelper.h"
11 #include "llvm/Config/config.h"
12 #include "llvm/Support/raw_ostream.h"
13 #ifdef HAVE_LIBPFM
14 #include "perfmon/perf_event.h"
15 #include "perfmon/pfmlib.h"
16 #include "perfmon/pfmlib_perf_event.h"
17 #endif
18 #include <cassert>
19 
20 namespace llvm {
21 namespace exegesis {
22 namespace pfm {
23 
24 #ifdef HAVE_LIBPFM
25 static bool isPfmError(int Code) { return Code != PFM_SUCCESS; }
26 #endif
27 
28 bool pfmInitialize() {
29 #ifdef HAVE_LIBPFM
30   return isPfmError(pfm_initialize());
31 #else
32   return true;
33 #endif
34 }
35 
36 void pfmTerminate() {
37 #ifdef HAVE_LIBPFM
38   pfm_terminate();
39 #endif
40 }
41 
42 PerfEvent::~PerfEvent() {
43 #ifdef HAVE_LIBPFM
44   delete Attr;
45   ;
46 #endif
47 }
48 
49 PerfEvent::PerfEvent(PerfEvent &&Other)
50     : EventString(std::move(Other.EventString)),
51       FullQualifiedEventString(std::move(Other.FullQualifiedEventString)),
52       Attr(Other.Attr) {
53   Other.Attr = nullptr;
54 }
55 
56 PerfEvent::PerfEvent(llvm::StringRef PfmEventString)
57     : EventString(PfmEventString.str()), Attr(nullptr) {
58 #ifdef HAVE_LIBPFM
59   char *Fstr = nullptr;
60   pfm_perf_encode_arg_t Arg = {};
61   Attr = new perf_event_attr();
62   Arg.attr = Attr;
63   Arg.fstr = &Fstr;
64   Arg.size = sizeof(pfm_perf_encode_arg_t);
65   const int Result = pfm_get_os_event_encoding(EventString.c_str(), PFM_PLM3,
66                                                PFM_OS_PERF_EVENT, &Arg);
67   if (isPfmError(Result)) {
68     // We don't know beforehand which counters are available (e.g. 6 uops ports
69     // on Sandybridge but 8 on Haswell) so we report the missing counter without
70     // crashing.
71     llvm::errs() << pfm_strerror(Result) << " - cannot create event "
72                  << EventString << "\n";
73   }
74   if (Fstr) {
75     FullQualifiedEventString = Fstr;
76     free(Fstr);
77   }
78 #endif
79 }
80 
81 llvm::StringRef PerfEvent::name() const { return EventString; }
82 
83 bool PerfEvent::valid() const { return !FullQualifiedEventString.empty(); }
84 
85 const perf_event_attr *PerfEvent::attribute() const { return Attr; }
86 
87 llvm::StringRef PerfEvent::getPfmEventString() const {
88   return FullQualifiedEventString;
89 }
90 
91 #ifdef HAVE_LIBPFM
92 Counter::Counter(const PerfEvent &Event) {
93   assert(Event.valid());
94   const pid_t Pid = 0;    // measure current process/thread.
95   const int Cpu = -1;     // measure any processor.
96   const int GroupFd = -1; // no grouping of counters.
97   const uint32_t Flags = 0;
98   perf_event_attr AttrCopy = *Event.attribute();
99   FileDescriptor = perf_event_open(&AttrCopy, Pid, Cpu, GroupFd, Flags);
100   if (FileDescriptor == -1) {
101     llvm::errs() << "Unable to open event, make sure your kernel allows user "
102                     "space perf monitoring.\nYou may want to try:\n$ sudo sh "
103                     "-c 'echo -1 > /proc/sys/kernel/perf_event_paranoid'\n";
104   }
105   assert(FileDescriptor != -1 && "Unable to open event");
106 }
107 
108 Counter::~Counter() { close(FileDescriptor); }
109 
110 void Counter::start() { ioctl(FileDescriptor, PERF_EVENT_IOC_RESET, 0); }
111 
112 void Counter::stop() { ioctl(FileDescriptor, PERF_EVENT_IOC_DISABLE, 0); }
113 
114 int64_t Counter::read() const {
115   int64_t Count = 0;
116   ssize_t ReadSize = ::read(FileDescriptor, &Count, sizeof(Count));
117   if (ReadSize != sizeof(Count)) {
118     Count = -1;
119     llvm::errs() << "Failed to read event counter\n";
120   }
121   return Count;
122 }
123 
124 #else
125 
126 Counter::Counter(const PerfEvent &Event) {}
127 
128 Counter::~Counter() = default;
129 
130 void Counter::start() {}
131 
132 void Counter::stop() {}
133 
134 int64_t Counter::read() const { return 42; }
135 
136 #endif
137 
138 } // namespace pfm
139 } // namespace exegesis
140 } // namespace llvm
141