//===-- 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 #include #include // for erno #include // 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> 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("Failed to read event counter", llvm::errc::io_error); llvm::SmallVector 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> Counter::readOrError(StringRef /*unused*/) const { return llvm::make_error("Not implemented", llvm::errc::io_error); } int Counter::numValues() const { return 1; } #endif } // namespace pfm } // namespace exegesis } // namespace llvm