| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #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() { return pfm_initialize() == PFM_SUCCESS; } |
|
|
| bool PerfCounters::IsCounterSupported(const std::string& name) { |
| 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) { |
| |
| 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>&) { return 0; } |
|
|
| const bool PerfCounters::kSupported = false; |
|
|
| bool PerfCounters::Initialize() { return false; } |
|
|
| bool PerfCounters::IsCounterSupported(const std::string&) { return false; } |
|
|
| PerfCounters PerfCounters::Create( |
| const std::vector<std::string>& counter_names) { |
| if (!counter_names.empty()) { |
| GetErrorLogInstance() << "Performance counters not supported."; |
| } |
| return NoCounters(); |
| } |
|
|
| void PerfCounters::CloseCounters() const {} |
| #endif |
|
|
| PerfCountersMeasurement::PerfCountersMeasurement( |
| const std::vector<std::string>& counter_names) |
| : start_values_(counter_names.size()), end_values_(counter_names.size()) { |
| counters_ = PerfCounters::Create(counter_names); |
| } |
|
|
| PerfCounters& PerfCounters::operator=(PerfCounters&& other) noexcept { |
| if (this != &other) { |
| CloseCounters(); |
|
|
| counter_ids_ = std::move(other.counter_ids_); |
| leader_ids_ = std::move(other.leader_ids_); |
| counter_names_ = std::move(other.counter_names_); |
| } |
| return *this; |
| } |
| } |
| } |
|
|