1 // Copyright 2021 Google Inc. All rights reserved.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 #include "perf_counters.h"
20 #if defined HAVE_LIBPFM
21 #include "perfmon/pfmlib.h"
22 #include "perfmon/pfmlib_perf_event.h"
28 constexpr size_t PerfCounterValues::kMaxCounters
;
30 #if defined HAVE_LIBPFM
31 const bool PerfCounters::kSupported
= true;
33 bool PerfCounters::Initialize() { return pfm_initialize() == PFM_SUCCESS
; }
35 PerfCounters
PerfCounters::Create(
36 const std::vector
<std::string
>& counter_names
) {
37 if (counter_names
.empty()) {
40 if (counter_names
.size() > PerfCounterValues::kMaxCounters
) {
42 << counter_names
.size()
43 << " counters were requested. The minimum is 1, the maximum is "
44 << PerfCounterValues::kMaxCounters
<< "\n";
47 std::vector
<int> counter_ids(counter_names
.size());
49 const int mode
= PFM_PLM3
; // user mode only
50 for (size_t i
= 0; i
< counter_names
.size(); ++i
) {
51 const bool is_first
= i
== 0;
52 struct perf_event_attr attr
{};
53 attr
.size
= sizeof(attr
);
54 const int group_id
= !is_first
? counter_ids
[0] : -1;
55 const auto& name
= counter_names
[i
];
57 GetErrorLogInstance() << "A counter name was the empty string\n";
60 pfm_perf_encode_arg_t arg
{};
64 pfm_get_os_event_encoding(name
.c_str(), mode
, PFM_OS_PERF_EVENT
, &arg
);
65 if (pfm_get
!= PFM_SUCCESS
) {
66 GetErrorLogInstance() << "Unknown counter name: " << name
<< "\n";
69 attr
.disabled
= is_first
;
70 // Note: the man page for perf_event_create suggests inerit = true and
71 // read_format = PERF_FORMAT_GROUP don't work together, but that's not the
74 attr
.pinned
= is_first
;
75 attr
.exclude_kernel
= true;
76 attr
.exclude_user
= false;
77 attr
.exclude_hv
= true;
78 // Read all counters in one read.
79 attr
.read_format
= PERF_FORMAT_GROUP
;
82 static constexpr size_t kNrOfSyscallRetries
= 5;
83 // Retry syscall as it was interrupted often (b/64774091).
84 for (size_t num_retries
= 0; num_retries
< kNrOfSyscallRetries
;
86 id
= perf_event_open(&attr
, 0, -1, group_id
, 0);
87 if (id
>= 0 || errno
!= EINTR
) {
93 << "Failed to get a file descriptor for " << name
<< "\n";
99 if (ioctl(counter_ids
[0], PERF_EVENT_IOC_ENABLE
) != 0) {
100 GetErrorLogInstance() << "Failed to start counters\n";
104 return PerfCounters(counter_names
, std::move(counter_ids
));
107 PerfCounters::~PerfCounters() {
108 if (counter_ids_
.empty()) {
111 ioctl(counter_ids_
[0], PERF_EVENT_IOC_DISABLE
);
112 for (int fd
: counter_ids_
) {
116 #else // defined HAVE_LIBPFM
117 const bool PerfCounters::kSupported
= false;
119 bool PerfCounters::Initialize() { return false; }
121 PerfCounters
PerfCounters::Create(
122 const std::vector
<std::string
>& counter_names
) {
123 if (!counter_names
.empty()) {
124 GetErrorLogInstance() << "Performance counters not supported.";
129 PerfCounters::~PerfCounters() = default;
130 #endif // defined HAVE_LIBPFM
131 } // namespace internal
132 } // namespace benchmark