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