| //===-- PerfHelper.cpp ------------------------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "PerfHelper.h" |
| #include "llvm/Config/config.h" |
| #include "llvm/Support/Errc.h" |
| #include "llvm/Support/Error.h" |
| #include "llvm/Support/raw_ostream.h" |
| #ifdef HAVE_LIBPFM |
| #include <perfmon/perf_event.h> |
| #include <perfmon/pfmlib.h> |
| #include <perfmon/pfmlib_perf_event.h> |
| #endif |
| |
| #include <cassert> |
| #include <cstddef> |
| #include <errno.h> // for erno |
| #include <string.h> // for strerror() |
| |
| namespace llvm { |
| namespace exegesis { |
| namespace pfm { |
| |
| #ifdef HAVE_LIBPFM |
| static bool isPfmError(int Code) { return Code != PFM_SUCCESS; } |
| #endif |
| |
| bool pfmInitialize() { |
| #ifdef HAVE_LIBPFM |
| return isPfmError(pfm_initialize()); |
| #else |
| return true; |
| #endif |
| } |
| |
| void pfmTerminate() { |
| #ifdef HAVE_LIBPFM |
| pfm_terminate(); |
| #endif |
| } |
| |
| PerfEvent::~PerfEvent() { |
| #ifdef HAVE_LIBPFM |
| delete Attr; |
| ; |
| #endif |
| } |
| |
| PerfEvent::PerfEvent(PerfEvent &&Other) |
| : EventString(std::move(Other.EventString)), |
| FullQualifiedEventString(std::move(Other.FullQualifiedEventString)), |
| Attr(Other.Attr) { |
| Other.Attr = nullptr; |
| } |
| |
| PerfEvent::PerfEvent(StringRef PfmEventString) |
| : EventString(PfmEventString.str()), Attr(nullptr) { |
| #ifdef HAVE_LIBPFM |
| char *Fstr = nullptr; |
| pfm_perf_encode_arg_t Arg = {}; |
| Attr = new perf_event_attr(); |
| Arg.attr = Attr; |
| Arg.fstr = &Fstr; |
| Arg.size = sizeof(pfm_perf_encode_arg_t); |
| const int Result = pfm_get_os_event_encoding(EventString.c_str(), PFM_PLM3, |
| PFM_OS_PERF_EVENT, &Arg); |
| if (isPfmError(Result)) { |
| // We don't know beforehand which counters are available (e.g. 6 uops ports |
| // on Sandybridge but 8 on Haswell) so we report the missing counter without |
| // crashing. |
| errs() << pfm_strerror(Result) << " - cannot create event " << EventString |
| << "\n"; |
| } |
| if (Fstr) { |
| FullQualifiedEventString = Fstr; |
| free(Fstr); |
| } |
| #endif |
| } |
| |
| StringRef PerfEvent::name() const { return EventString; } |
| |
| bool PerfEvent::valid() const { return !FullQualifiedEventString.empty(); } |
| |
| const perf_event_attr *PerfEvent::attribute() const { return Attr; } |
| |
| StringRef PerfEvent::getPfmEventString() const { |
| return FullQualifiedEventString; |
| } |
| |
| #ifdef HAVE_LIBPFM |
| Counter::Counter(PerfEvent &&E) : Event(std::move(E)){ |
| assert(Event.valid()); |
| const pid_t Pid = 0; // measure current process/thread. |
| const int Cpu = -1; // measure any processor. |
| const int GroupFd = -1; // no grouping of counters. |
| const uint32_t Flags = 0; |
| perf_event_attr AttrCopy = *Event.attribute(); |
| FileDescriptor = perf_event_open(&AttrCopy, Pid, Cpu, GroupFd, Flags); |
| if (FileDescriptor == -1) { |
| errs() << "Unable to open event. ERRNO: " << strerror(errno) |
| << ". Make sure your kernel allows user " |
| "space perf monitoring.\nYou may want to try:\n$ sudo sh " |
| "-c 'echo -1 > /proc/sys/kernel/perf_event_paranoid'\n"; |
| } |
| assert(FileDescriptor != -1 && "Unable to open event"); |
| } |
| |
| Counter::~Counter() { close(FileDescriptor); } |
| |
| void Counter::start() { ioctl(FileDescriptor, PERF_EVENT_IOC_RESET, 0); } |
| |
| void Counter::stop() { ioctl(FileDescriptor, PERF_EVENT_IOC_DISABLE, 0); } |
| |
| int64_t Counter::read() const { |
| auto ValueOrError = readOrError(); |
| if (ValueOrError) { |
| if (!ValueOrError.get().empty()) |
| return ValueOrError.get()[0]; |
| errs() << "Counter has no reading\n"; |
| } else |
| errs() << ValueOrError.takeError() << "\n"; |
| return -1; |
| } |
| |
| llvm::Expected<llvm::SmallVector<int64_t, 4>> |
| Counter::readOrError(StringRef /*unused*/) const { |
| int64_t Count = 0; |
| ssize_t ReadSize = ::read(FileDescriptor, &Count, sizeof(Count)); |
| if (ReadSize != sizeof(Count)) |
| return llvm::make_error<llvm::StringError>("Failed to read event counter", |
| llvm::errc::io_error); |
| llvm::SmallVector<int64_t, 4> Result; |
| Result.push_back(Count); |
| return Result; |
| } |
| |
| int Counter::numValues() const { return 1; } |
| #else |
| |
| Counter::Counter(PerfEvent &&Event) : Event(std::move(Event)) {} |
| |
| Counter::~Counter() = default; |
| |
| void Counter::start() {} |
| |
| void Counter::stop() {} |
| |
| int64_t Counter::read() const { return 42; } |
| |
| llvm::Expected<llvm::SmallVector<int64_t, 4>> |
| Counter::readOrError(StringRef /*unused*/) const { |
| return llvm::make_error<llvm::StringError>("Not implemented", |
| llvm::errc::io_error); |
| } |
| |
| int Counter::numValues() const { return 1; } |
| |
| #endif |
| |
| } // namespace pfm |
| } // namespace exegesis |
| } // namespace llvm |