Home | History | Annotate | Line # | Download | only in lib
      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     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>>
    132 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 
    143 int Counter::numValues() const { return 1; }
    144 #else
    145 
    146 Counter::Counter(PerfEvent &&Event) : Event(std::move(Event)) {}
    147 
    148 Counter::~Counter() = default;
    149 
    150 void Counter::start() {}
    151 
    152 void Counter::stop() {}
    153 
    154 int64_t Counter::read() const { return 42; }
    155 
    156 llvm::Expected<llvm::SmallVector<int64_t, 4>>
    157 Counter::readOrError(StringRef /*unused*/) const {
    158   return llvm::make_error<llvm::StringError>("Not implemented",
    159                                              llvm::errc::io_error);
    160 }
    161 
    162 int Counter::numValues() const { return 1; }
    163 
    164 #endif
    165 
    166 } // namespace pfm
    167 } // namespace exegesis
    168 } // namespace llvm
    169