summaryrefslogtreecommitdiff
path: root/third-party/benchmark/src/perf_counters.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third-party/benchmark/src/perf_counters.cc')
-rw-r--r--third-party/benchmark/src/perf_counters.cc132
1 files changed, 132 insertions, 0 deletions
diff --git a/third-party/benchmark/src/perf_counters.cc b/third-party/benchmark/src/perf_counters.cc
new file mode 100644
index 000000000000..4ddf0de2502c
--- /dev/null
+++ b/third-party/benchmark/src/perf_counters.cc
@@ -0,0 +1,132 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "perf_counters.h"
+
+#include <cstring>
+#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
+const bool PerfCounters::kSupported = true;
+
+bool PerfCounters::Initialize() { return pfm_initialize() == PFM_SUCCESS; }
+
+PerfCounters PerfCounters::Create(
+ const std::vector<std::string>& counter_names) {
+ if (counter_names.empty()) {
+ return NoCounters();
+ }
+ if (counter_names.size() > PerfCounterValues::kMaxCounters) {
+ GetErrorLogInstance()
+ << counter_names.size()
+ << " counters were requested. The minimum is 1, the maximum is "
+ << PerfCounterValues::kMaxCounters << "\n";
+ return NoCounters();
+ }
+ std::vector<int> counter_ids(counter_names.size());
+
+ const int mode = PFM_PLM3; // user mode only
+ for (size_t i = 0; i < counter_names.size(); ++i) {
+ const bool is_first = i == 0;
+ struct perf_event_attr attr{};
+ attr.size = sizeof(attr);
+ const int group_id = !is_first ? counter_ids[0] : -1;
+ const auto& name = counter_names[i];
+ if (name.empty()) {
+ GetErrorLogInstance() << "A counter name was the empty string\n";
+ return NoCounters();
+ }
+ pfm_perf_encode_arg_t arg{};
+ arg.attr = &attr;
+
+ const int pfm_get =
+ pfm_get_os_event_encoding(name.c_str(), mode, PFM_OS_PERF_EVENT, &arg);
+ if (pfm_get != PFM_SUCCESS) {
+ GetErrorLogInstance() << "Unknown counter name: " << name << "\n";
+ return NoCounters();
+ }
+ attr.disabled = is_first;
+ // Note: the man page for perf_event_create suggests inerit = true and
+ // read_format = PERF_FORMAT_GROUP don't work together, but that's not the
+ // case.
+ attr.inherit = true;
+ attr.pinned = is_first;
+ attr.exclude_kernel = true;
+ attr.exclude_user = false;
+ attr.exclude_hv = true;
+ // Read all counters in one read.
+ attr.read_format = PERF_FORMAT_GROUP;
+
+ int id = -1;
+ static constexpr size_t kNrOfSyscallRetries = 5;
+ // Retry syscall as it was interrupted often (b/64774091).
+ 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) {
+ GetErrorLogInstance()
+ << "Failed to get a file descriptor for " << name << "\n";
+ return NoCounters();
+ }
+
+ counter_ids[i] = id;
+ }
+ if (ioctl(counter_ids[0], PERF_EVENT_IOC_ENABLE) != 0) {
+ GetErrorLogInstance() << "Failed to start counters\n";
+ return NoCounters();
+ }
+
+ return PerfCounters(counter_names, std::move(counter_ids));
+}
+
+PerfCounters::~PerfCounters() {
+ if (counter_ids_.empty()) {
+ return;
+ }
+ ioctl(counter_ids_[0], PERF_EVENT_IOC_DISABLE);
+ for (int fd : counter_ids_) {
+ close(fd);
+ }
+}
+#else // defined HAVE_LIBPFM
+const bool PerfCounters::kSupported = false;
+
+bool PerfCounters::Initialize() { return false; }
+
+PerfCounters PerfCounters::Create(
+ const std::vector<std::string>& counter_names) {
+ if (!counter_names.empty()) {
+ GetErrorLogInstance() << "Performance counters not supported.";
+ }
+ return NoCounters();
+}
+
+PerfCounters::~PerfCounters() = default;
+#endif // defined HAVE_LIBPFM
+} // namespace internal
+} // namespace benchmark