#include "perf_counters.h"
#include <cstring>
#include <memory>
#include <vector>
#if defined HAVE_LIBPFM
#include "perfmon/pfmlib.h"
#include "perfmon/pfmlib_perf_event.h"
#endif
namespace benchmark {
namespace internal {
constexpr size_t PerfCounterValues::kMaxCounters;
#if defined HAVE_LIBPFM
size_t PerfCounterValues::Read(const std::vector<int>& leaders) {
const size_t bufsize = values_.size() * sizeof(values_[0]);
char* ptr = reinterpret_cast<char*>(values_.data());
size_t size = bufsize;
for (int lead : leaders) {
auto read_bytes = ::read(lead, ptr, size);
if (read_bytes >= ssize_t(sizeof(uint64_t))) {
std::size_t data_bytes = read_bytes - sizeof(uint64_t);
std::memmove(ptr, ptr + sizeof(uint64_t), data_bytes);
ptr += data_bytes;
size -= data_bytes;
} else {
int err = errno;
GetErrorLogInstance() << "Error reading lead " << lead << " errno:" << err
<< " " << ::strerror(err) << "\n";
return 0;
}
}
return (bufsize - size) / sizeof(uint64_t);
}
const bool PerfCounters::kSupported = true;
bool PerfCounters::Initialize() {
static const bool success = []() {
return pfm_initialize() == PFM_SUCCESS;
}();
return success;
}
bool PerfCounters::IsCounterSupported(const std::string& name) {
Initialize();
perf_event_attr_t attr;
std::memset(&attr, 0, sizeof(attr));
pfm_perf_encode_arg_t arg;
std::memset(&arg, 0, sizeof(arg));
arg.attr = &attr;
const int mode = PFM_PLM3;
int ret = pfm_get_os_event_encoding(name.c_str(), mode, PFM_OS_PERF_EVENT_EXT,
&arg);
return (ret == PFM_SUCCESS);
}
PerfCounters PerfCounters::Create(
const std::vector<std::string>& counter_names) {
if (!counter_names.empty()) {
Initialize();
}
std::vector<std::string> valid_names;
std::vector<int> counter_ids;
std::vector<int> leader_ids;
valid_names.reserve(counter_names.size());
counter_ids.reserve(counter_names.size());
const int kCounterMode = PFM_PLM3;
int group_id = -1;
for (size_t i = 0; i < counter_names.size(); ++i) {
if (valid_names.size() == PerfCounterValues::kMaxCounters) {
GetErrorLogInstance()
<< counter_names.size() << " counters were requested. The maximum is "
<< PerfCounterValues::kMaxCounters << " and " << valid_names.size()
<< " were already added. All remaining counters will be ignored\n";
break;
}
const auto& name = counter_names[i];
if (name.empty()) {
GetErrorLogInstance()
<< "A performance counter name was the empty string\n";
continue;
}
const bool is_first = (group_id < 0);
struct perf_event_attr attr {};
attr.size = sizeof(attr);
pfm_perf_encode_arg_t arg{};
arg.attr = &attr;
const int pfm_get = pfm_get_os_event_encoding(name.c_str(), kCounterMode,
PFM_OS_PERF_EVENT, &arg);
if (pfm_get != PFM_SUCCESS) {
GetErrorLogInstance()
<< "Unknown performance counter name: " << name << "\n";
continue;
}
attr.disabled = is_first;
attr.inherit = true;
attr.pinned = is_first;
attr.exclude_kernel = true;
attr.exclude_user = false;
attr.exclude_hv = true;
attr.read_format = PERF_FORMAT_GROUP;
int id = -1;
while (id < 0) {
static constexpr size_t kNrOfSyscallRetries = 5;
for (size_t num_retries = 0; num_retries < kNrOfSyscallRetries;
++num_retries) {
id = perf_event_open(&attr, 0, -1, group_id, 0);
if (id >= 0 || errno != EINTR) {
break;
}
}
if (id < 0) {
if (group_id >= 0) {
group_id = -1;
} else {
break;
}
}
}
if (id < 0) {
GetErrorLogInstance() << "***WARNING** Failed to get a file descriptor "
"for performance counter "
<< name << ". Ignoring\n";
continue;
}
if (group_id < 0) {
leader_ids.push_back(id);
group_id = id;
}
counter_ids.push_back(id);
valid_names.push_back(name);
}
for (int lead : leader_ids) {
if (ioctl(lead, PERF_EVENT_IOC_ENABLE) != 0) {
GetErrorLogInstance() << "***WARNING*** Failed to start counters. "
"Claring out all counters.\n";
for (int id : counter_ids) {
::close(id);
}
return NoCounters();
}
}
return PerfCounters(std::move(valid_names), std::move(counter_ids),
std::move(leader_ids));
}
void PerfCounters::CloseCounters() const {
if (counter_ids_.empty()) {
return;
}
for (int lead : leader_ids_) {
ioctl(lead, PERF_EVENT_IOC_DISABLE);
}
for (int fd : counter_ids_) {
close(fd);
}
}
#else
size_t PerfCounterValues::Read(const std::vector<int>&) { … }
const bool PerfCounters::kSupported = …;
bool PerfCounters::Initialize() { … }
bool PerfCounters::IsCounterSupported(const std::string&) { … }
PerfCounters PerfCounters::Create(
const std::vector<std::string>& counter_names) { … }
void PerfCounters::CloseCounters() const { … }
#endif
PerfCountersMeasurement::PerfCountersMeasurement(
const std::vector<std::string>& counter_names)
: … { … }
PerfCounters& PerfCounters::operator=(PerfCounters&& other) noexcept { … }
}
}