brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.5 KiB · 585ef06 Raw
256 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "PerfHelper.h"10#include "Error.h"11#include "llvm/Config/config.h"12#include "llvm/Support/Errc.h"13#include "llvm/Support/Error.h"14#include "llvm/Support/raw_ostream.h"15#ifdef HAVE_LIBPFM16#include <perfmon/perf_event.h>17#include <perfmon/pfmlib.h>18#include <perfmon/pfmlib_perf_event.h>19#endif20 21#include <cassert>22#include <cstddef>23#include <errno.h>  // for erno24#include <string.h> // for strerror()25 26namespace llvm {27namespace exegesis {28namespace pfm {29 30#ifdef HAVE_LIBPFM31static bool isPfmError(int Code) { return Code != PFM_SUCCESS; }32#endif33 34bool pfmInitialize() {35#ifdef HAVE_LIBPFM36  return isPfmError(pfm_initialize());37#else38  return true;39#endif40}41 42void pfmTerminate() {43#ifdef HAVE_LIBPFM44  pfm_terminate();45#endif46}47 48// Performance counters may be unavailable for a number of reasons (such as49// kernel.perf_event_paranoid restriction or CPU being unknown to libpfm).50//51// Dummy event can be specified to skip interaction with real performance52// counters while still passing control to the generated code snippet.53const char *const PerfEvent::DummyEventString = "not-really-an-event";54 55PerfEvent::~PerfEvent() {56#ifdef HAVE_LIBPFM57  delete Attr;58  ;59#endif60}61 62PerfEvent::PerfEvent(PerfEvent &&Other)63    : EventString(std::move(Other.EventString)),64      FullQualifiedEventString(std::move(Other.FullQualifiedEventString)),65      Attr(Other.Attr) {66  Other.Attr = nullptr;67}68 69PerfEvent::PerfEvent(StringRef PfmEventString)70    : EventString(PfmEventString.str()), Attr(nullptr) {71  if (PfmEventString != DummyEventString)72    initRealEvent(PfmEventString);73  else74    FullQualifiedEventString = PfmEventString;75}76 77void PerfEvent::initRealEvent(StringRef PfmEventString) {78#ifdef HAVE_LIBPFM79  char *Fstr = nullptr;80  pfm_perf_encode_arg_t Arg = {};81  Attr = new perf_event_attr();82  Arg.attr = Attr;83  Arg.fstr = &Fstr;84  Arg.size = sizeof(pfm_perf_encode_arg_t);85  const int Result = pfm_get_os_event_encoding(EventString.c_str(), PFM_PLM3,86                                               PFM_OS_PERF_EVENT, &Arg);87  if (isPfmError(Result)) {88    // We don't know beforehand which counters are available (e.g. 6 uops ports89    // on Sandybridge but 8 on Haswell) so we report the missing counter without90    // crashing.91    errs() << pfm_strerror(Result) << " - cannot create event " << EventString92           << "\n";93  }94  if (Fstr) {95    FullQualifiedEventString = Fstr;96    free(Fstr);97  }98#endif99}100 101StringRef PerfEvent::name() const { return EventString; }102 103bool PerfEvent::valid() const { return !FullQualifiedEventString.empty(); }104 105const perf_event_attr *PerfEvent::attribute() const { return Attr; }106 107StringRef PerfEvent::getPfmEventString() const {108  return FullQualifiedEventString;109}110 111ConfiguredEvent::ConfiguredEvent(PerfEvent &&EventToConfigure)112    : Event(std::move(EventToConfigure)) {113  assert(Event.valid());114}115 116#ifdef HAVE_LIBPFM117void ConfiguredEvent::initRealEvent(const pid_t ProcessID, const int GroupFD) {118  const int CPU = -1;119  const uint32_t Flags = 0;120  perf_event_attr AttrCopy = *Event.attribute();121  AttrCopy.read_format =122      PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING;123  FileDescriptor = perf_event_open(&AttrCopy, ProcessID, CPU, GroupFD, Flags);124  if (FileDescriptor == -1) {125    errs() << "Unable to open event. ERRNO: " << strerror(errno)126           << ". Make sure your kernel allows user "127              "space perf monitoring.\nYou may want to try:\n$ sudo sh "128              "-c 'echo -1 > /proc/sys/kernel/perf_event_paranoid'.\n"129           << "If you are debugging and just want to execute the snippet "130              "without actually reading performance counters, "131              "pass --use-dummy-perf-counters command line option.\n";132  }133  assert(FileDescriptor != -1 && "Unable to open event");134}135 136Expected<SmallVector<int64_t>>137ConfiguredEvent::readOrError(StringRef /*unused*/) const {138  int64_t EventInfo[3] = {0, 0, 0};139  ssize_t ReadSize = ::read(FileDescriptor, &EventInfo, sizeof(EventInfo));140 141  if (ReadSize != sizeof(EventInfo))142    return make_error<StringError>("Failed to read event counter",143                                   errc::io_error);144 145  int64_t EventTimeEnabled = EventInfo[1];146  int64_t EventTimeRunning = EventInfo[2];147  if (EventTimeEnabled != EventTimeRunning)148    return make_error<PerfCounterNotFullyEnabled>();149 150  SmallVector<int64_t, 1> Result;151  Result.push_back(EventInfo[0]);152  return Result;153}154 155ConfiguredEvent::~ConfiguredEvent() { close(FileDescriptor); }156#else157void ConfiguredEvent::initRealEvent(pid_t ProcessID, const int GroupFD) {}158 159Expected<SmallVector<int64_t>>160ConfiguredEvent::readOrError(StringRef /*unused*/) const {161  return make_error<StringError>("Not implemented",162                                 errc::function_not_supported);163}164 165ConfiguredEvent::~ConfiguredEvent() = default;166#endif // HAVE_LIBPFM167 168CounterGroup::CounterGroup(PerfEvent &&E, std::vector<PerfEvent> &&ValEvents,169                           pid_t ProcessID)170    : EventCounter(std::move(E)) {171  IsDummyEvent = EventCounter.isDummyEvent();172 173  for (auto &&ValEvent : ValEvents)174    ValidationEventCounters.emplace_back(std::move(ValEvent));175 176  if (!IsDummyEvent)177    initRealEvent(ProcessID);178}179 180#ifdef HAVE_LIBPFM181void CounterGroup::initRealEvent(pid_t ProcessID) {182  EventCounter.initRealEvent(ProcessID);183 184  for (auto &ValCounter : ValidationEventCounters)185    ValCounter.initRealEvent(ProcessID, getFileDescriptor());186}187 188void CounterGroup::start() {189  if (!IsDummyEvent)190    ioctl(getFileDescriptor(), PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP);191}192 193void CounterGroup::stop() {194  if (!IsDummyEvent)195    ioctl(getFileDescriptor(), PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP);196}197 198Expected<SmallVector<int64_t, 4>>199CounterGroup::readOrError(StringRef FunctionBytes) const {200  if (!IsDummyEvent)201    return EventCounter.readOrError(FunctionBytes);202  else203    return SmallVector<int64_t, 1>(1, 42);204}205 206Expected<SmallVector<int64_t>>207CounterGroup::readValidationCountersOrError() const {208  SmallVector<int64_t, 4> Result;209  for (const auto &ValCounter : ValidationEventCounters) {210    Expected<SmallVector<int64_t>> ValueOrError =211        ValCounter.readOrError(StringRef());212 213    if (!ValueOrError)214      return ValueOrError.takeError();215 216    // Reading a validation counter will only return a single value, so it is217    // safe to only append the first value here. Also assert that this is true.218    assert(ValueOrError->size() == 1 &&219           "Validation counters should only return a single value");220    Result.push_back((*ValueOrError)[0]);221  }222  return Result;223}224 225int CounterGroup::numValues() const { return 1; }226#else227 228void CounterGroup::initRealEvent(pid_t ProcessID) {}229 230void CounterGroup::start() {}231 232void CounterGroup::stop() {}233 234Expected<SmallVector<int64_t, 4>>235CounterGroup::readOrError(StringRef /*unused*/) const {236  if (IsDummyEvent) {237    SmallVector<int64_t, 4> Result;238    Result.push_back(42);239    return Result;240  }241  return make_error<StringError>("Not implemented", errc::io_error);242}243 244Expected<SmallVector<int64_t>>245CounterGroup::readValidationCountersOrError() const {246  return SmallVector<int64_t>(0);247}248 249int CounterGroup::numValues() const { return 1; }250 251#endif252 253} // namespace pfm254} // namespace exegesis255} // namespace llvm256