169 lines
4.7 KiB
C++
169 lines
4.7 KiB
C++
//===-- 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
|