1*dbb99499SAndroid Build Coastguard Worker // Copyright 2021 Google Inc. All rights reserved.
2*dbb99499SAndroid Build Coastguard Worker //
3*dbb99499SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*dbb99499SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*dbb99499SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*dbb99499SAndroid Build Coastguard Worker //
7*dbb99499SAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0
8*dbb99499SAndroid Build Coastguard Worker //
9*dbb99499SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*dbb99499SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*dbb99499SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*dbb99499SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*dbb99499SAndroid Build Coastguard Worker // limitations under the License.
14*dbb99499SAndroid Build Coastguard Worker
15*dbb99499SAndroid Build Coastguard Worker #include "perf_counters.h"
16*dbb99499SAndroid Build Coastguard Worker
17*dbb99499SAndroid Build Coastguard Worker #include <cstring>
18*dbb99499SAndroid Build Coastguard Worker #include <memory>
19*dbb99499SAndroid Build Coastguard Worker #include <vector>
20*dbb99499SAndroid Build Coastguard Worker
21*dbb99499SAndroid Build Coastguard Worker #if defined HAVE_LIBPFM
22*dbb99499SAndroid Build Coastguard Worker #include "perfmon/pfmlib.h"
23*dbb99499SAndroid Build Coastguard Worker #include "perfmon/pfmlib_perf_event.h"
24*dbb99499SAndroid Build Coastguard Worker #endif
25*dbb99499SAndroid Build Coastguard Worker
26*dbb99499SAndroid Build Coastguard Worker namespace benchmark {
27*dbb99499SAndroid Build Coastguard Worker namespace internal {
28*dbb99499SAndroid Build Coastguard Worker
29*dbb99499SAndroid Build Coastguard Worker constexpr size_t PerfCounterValues::kMaxCounters;
30*dbb99499SAndroid Build Coastguard Worker
31*dbb99499SAndroid Build Coastguard Worker #if defined HAVE_LIBPFM
32*dbb99499SAndroid Build Coastguard Worker
Read(const std::vector<int> & leaders)33*dbb99499SAndroid Build Coastguard Worker size_t PerfCounterValues::Read(const std::vector<int>& leaders) {
34*dbb99499SAndroid Build Coastguard Worker // Create a pointer for multiple reads
35*dbb99499SAndroid Build Coastguard Worker const size_t bufsize = values_.size() * sizeof(values_[0]);
36*dbb99499SAndroid Build Coastguard Worker char* ptr = reinterpret_cast<char*>(values_.data());
37*dbb99499SAndroid Build Coastguard Worker size_t size = bufsize;
38*dbb99499SAndroid Build Coastguard Worker for (int lead : leaders) {
39*dbb99499SAndroid Build Coastguard Worker auto read_bytes = ::read(lead, ptr, size);
40*dbb99499SAndroid Build Coastguard Worker if (read_bytes >= ssize_t(sizeof(uint64_t))) {
41*dbb99499SAndroid Build Coastguard Worker // Actual data bytes are all bytes minus initial padding
42*dbb99499SAndroid Build Coastguard Worker std::size_t data_bytes =
43*dbb99499SAndroid Build Coastguard Worker static_cast<std::size_t>(read_bytes) - sizeof(uint64_t);
44*dbb99499SAndroid Build Coastguard Worker // This should be very cheap since it's in hot cache
45*dbb99499SAndroid Build Coastguard Worker std::memmove(ptr, ptr + sizeof(uint64_t), data_bytes);
46*dbb99499SAndroid Build Coastguard Worker // Increment our counters
47*dbb99499SAndroid Build Coastguard Worker ptr += data_bytes;
48*dbb99499SAndroid Build Coastguard Worker size -= data_bytes;
49*dbb99499SAndroid Build Coastguard Worker } else {
50*dbb99499SAndroid Build Coastguard Worker int err = errno;
51*dbb99499SAndroid Build Coastguard Worker GetErrorLogInstance() << "Error reading lead " << lead << " errno:" << err
52*dbb99499SAndroid Build Coastguard Worker << " " << ::strerror(err) << "\n";
53*dbb99499SAndroid Build Coastguard Worker return 0;
54*dbb99499SAndroid Build Coastguard Worker }
55*dbb99499SAndroid Build Coastguard Worker }
56*dbb99499SAndroid Build Coastguard Worker return (bufsize - size) / sizeof(uint64_t);
57*dbb99499SAndroid Build Coastguard Worker }
58*dbb99499SAndroid Build Coastguard Worker
59*dbb99499SAndroid Build Coastguard Worker const bool PerfCounters::kSupported = true;
60*dbb99499SAndroid Build Coastguard Worker
61*dbb99499SAndroid Build Coastguard Worker // Initializes libpfm only on the first call. Returns whether that single
62*dbb99499SAndroid Build Coastguard Worker // initialization was successful.
Initialize()63*dbb99499SAndroid Build Coastguard Worker bool PerfCounters::Initialize() {
64*dbb99499SAndroid Build Coastguard Worker // Function-scope static gets initialized only once on first call.
65*dbb99499SAndroid Build Coastguard Worker static const bool success = []() {
66*dbb99499SAndroid Build Coastguard Worker return pfm_initialize() == PFM_SUCCESS;
67*dbb99499SAndroid Build Coastguard Worker }();
68*dbb99499SAndroid Build Coastguard Worker return success;
69*dbb99499SAndroid Build Coastguard Worker }
70*dbb99499SAndroid Build Coastguard Worker
IsCounterSupported(const std::string & name)71*dbb99499SAndroid Build Coastguard Worker bool PerfCounters::IsCounterSupported(const std::string& name) {
72*dbb99499SAndroid Build Coastguard Worker Initialize();
73*dbb99499SAndroid Build Coastguard Worker perf_event_attr_t attr;
74*dbb99499SAndroid Build Coastguard Worker std::memset(&attr, 0, sizeof(attr));
75*dbb99499SAndroid Build Coastguard Worker pfm_perf_encode_arg_t arg;
76*dbb99499SAndroid Build Coastguard Worker std::memset(&arg, 0, sizeof(arg));
77*dbb99499SAndroid Build Coastguard Worker arg.attr = &attr;
78*dbb99499SAndroid Build Coastguard Worker const int mode = PFM_PLM3; // user mode only
79*dbb99499SAndroid Build Coastguard Worker int ret = pfm_get_os_event_encoding(name.c_str(), mode, PFM_OS_PERF_EVENT_EXT,
80*dbb99499SAndroid Build Coastguard Worker &arg);
81*dbb99499SAndroid Build Coastguard Worker return (ret == PFM_SUCCESS);
82*dbb99499SAndroid Build Coastguard Worker }
83*dbb99499SAndroid Build Coastguard Worker
Create(const std::vector<std::string> & counter_names)84*dbb99499SAndroid Build Coastguard Worker PerfCounters PerfCounters::Create(
85*dbb99499SAndroid Build Coastguard Worker const std::vector<std::string>& counter_names) {
86*dbb99499SAndroid Build Coastguard Worker if (!counter_names.empty()) {
87*dbb99499SAndroid Build Coastguard Worker Initialize();
88*dbb99499SAndroid Build Coastguard Worker }
89*dbb99499SAndroid Build Coastguard Worker
90*dbb99499SAndroid Build Coastguard Worker // Valid counters will populate these arrays but we start empty
91*dbb99499SAndroid Build Coastguard Worker std::vector<std::string> valid_names;
92*dbb99499SAndroid Build Coastguard Worker std::vector<int> counter_ids;
93*dbb99499SAndroid Build Coastguard Worker std::vector<int> leader_ids;
94*dbb99499SAndroid Build Coastguard Worker
95*dbb99499SAndroid Build Coastguard Worker // Resize to the maximum possible
96*dbb99499SAndroid Build Coastguard Worker valid_names.reserve(counter_names.size());
97*dbb99499SAndroid Build Coastguard Worker counter_ids.reserve(counter_names.size());
98*dbb99499SAndroid Build Coastguard Worker
99*dbb99499SAndroid Build Coastguard Worker const int kCounterMode = PFM_PLM3; // user mode only
100*dbb99499SAndroid Build Coastguard Worker
101*dbb99499SAndroid Build Coastguard Worker // Group leads will be assigned on demand. The idea is that once we cannot
102*dbb99499SAndroid Build Coastguard Worker // create a counter descriptor, the reason is that this group has maxed out
103*dbb99499SAndroid Build Coastguard Worker // so we set the group_id again to -1 and retry - giving the algorithm a
104*dbb99499SAndroid Build Coastguard Worker // chance to create a new group leader to hold the next set of counters.
105*dbb99499SAndroid Build Coastguard Worker int group_id = -1;
106*dbb99499SAndroid Build Coastguard Worker
107*dbb99499SAndroid Build Coastguard Worker // Loop through all performance counters
108*dbb99499SAndroid Build Coastguard Worker for (size_t i = 0; i < counter_names.size(); ++i) {
109*dbb99499SAndroid Build Coastguard Worker // we are about to push into the valid names vector
110*dbb99499SAndroid Build Coastguard Worker // check if we did not reach the maximum
111*dbb99499SAndroid Build Coastguard Worker if (valid_names.size() == PerfCounterValues::kMaxCounters) {
112*dbb99499SAndroid Build Coastguard Worker // Log a message if we maxed out and stop adding
113*dbb99499SAndroid Build Coastguard Worker GetErrorLogInstance()
114*dbb99499SAndroid Build Coastguard Worker << counter_names.size() << " counters were requested. The maximum is "
115*dbb99499SAndroid Build Coastguard Worker << PerfCounterValues::kMaxCounters << " and " << valid_names.size()
116*dbb99499SAndroid Build Coastguard Worker << " were already added. All remaining counters will be ignored\n";
117*dbb99499SAndroid Build Coastguard Worker // stop the loop and return what we have already
118*dbb99499SAndroid Build Coastguard Worker break;
119*dbb99499SAndroid Build Coastguard Worker }
120*dbb99499SAndroid Build Coastguard Worker
121*dbb99499SAndroid Build Coastguard Worker // Check if this name is empty
122*dbb99499SAndroid Build Coastguard Worker const auto& name = counter_names[i];
123*dbb99499SAndroid Build Coastguard Worker if (name.empty()) {
124*dbb99499SAndroid Build Coastguard Worker GetErrorLogInstance()
125*dbb99499SAndroid Build Coastguard Worker << "A performance counter name was the empty string\n";
126*dbb99499SAndroid Build Coastguard Worker continue;
127*dbb99499SAndroid Build Coastguard Worker }
128*dbb99499SAndroid Build Coastguard Worker
129*dbb99499SAndroid Build Coastguard Worker // Here first means first in group, ie the group leader
130*dbb99499SAndroid Build Coastguard Worker const bool is_first = (group_id < 0);
131*dbb99499SAndroid Build Coastguard Worker
132*dbb99499SAndroid Build Coastguard Worker // This struct will be populated by libpfm from the counter string
133*dbb99499SAndroid Build Coastguard Worker // and then fed into the syscall perf_event_open
134*dbb99499SAndroid Build Coastguard Worker struct perf_event_attr attr {};
135*dbb99499SAndroid Build Coastguard Worker attr.size = sizeof(attr);
136*dbb99499SAndroid Build Coastguard Worker
137*dbb99499SAndroid Build Coastguard Worker // This is the input struct to libpfm.
138*dbb99499SAndroid Build Coastguard Worker pfm_perf_encode_arg_t arg{};
139*dbb99499SAndroid Build Coastguard Worker arg.attr = &attr;
140*dbb99499SAndroid Build Coastguard Worker const int pfm_get = pfm_get_os_event_encoding(name.c_str(), kCounterMode,
141*dbb99499SAndroid Build Coastguard Worker PFM_OS_PERF_EVENT, &arg);
142*dbb99499SAndroid Build Coastguard Worker if (pfm_get != PFM_SUCCESS) {
143*dbb99499SAndroid Build Coastguard Worker GetErrorLogInstance()
144*dbb99499SAndroid Build Coastguard Worker << "Unknown performance counter name: " << name << "\n";
145*dbb99499SAndroid Build Coastguard Worker continue;
146*dbb99499SAndroid Build Coastguard Worker }
147*dbb99499SAndroid Build Coastguard Worker
148*dbb99499SAndroid Build Coastguard Worker // We then proceed to populate the remaining fields in our attribute struct
149*dbb99499SAndroid Build Coastguard Worker // Note: the man page for perf_event_create suggests inherit = true and
150*dbb99499SAndroid Build Coastguard Worker // read_format = PERF_FORMAT_GROUP don't work together, but that's not the
151*dbb99499SAndroid Build Coastguard Worker // case.
152*dbb99499SAndroid Build Coastguard Worker attr.disabled = is_first;
153*dbb99499SAndroid Build Coastguard Worker attr.inherit = true;
154*dbb99499SAndroid Build Coastguard Worker attr.pinned = is_first;
155*dbb99499SAndroid Build Coastguard Worker attr.exclude_kernel = true;
156*dbb99499SAndroid Build Coastguard Worker attr.exclude_user = false;
157*dbb99499SAndroid Build Coastguard Worker attr.exclude_hv = true;
158*dbb99499SAndroid Build Coastguard Worker
159*dbb99499SAndroid Build Coastguard Worker // Read all counters in a group in one read.
160*dbb99499SAndroid Build Coastguard Worker attr.read_format = PERF_FORMAT_GROUP; //| PERF_FORMAT_TOTAL_TIME_ENABLED |
161*dbb99499SAndroid Build Coastguard Worker // PERF_FORMAT_TOTAL_TIME_RUNNING;
162*dbb99499SAndroid Build Coastguard Worker
163*dbb99499SAndroid Build Coastguard Worker int id = -1;
164*dbb99499SAndroid Build Coastguard Worker while (id < 0) {
165*dbb99499SAndroid Build Coastguard Worker static constexpr size_t kNrOfSyscallRetries = 5;
166*dbb99499SAndroid Build Coastguard Worker // Retry syscall as it was interrupted often (b/64774091).
167*dbb99499SAndroid Build Coastguard Worker for (size_t num_retries = 0; num_retries < kNrOfSyscallRetries;
168*dbb99499SAndroid Build Coastguard Worker ++num_retries) {
169*dbb99499SAndroid Build Coastguard Worker id = perf_event_open(&attr, 0, -1, group_id, 0);
170*dbb99499SAndroid Build Coastguard Worker if (id >= 0 || errno != EINTR) {
171*dbb99499SAndroid Build Coastguard Worker break;
172*dbb99499SAndroid Build Coastguard Worker }
173*dbb99499SAndroid Build Coastguard Worker }
174*dbb99499SAndroid Build Coastguard Worker if (id < 0) {
175*dbb99499SAndroid Build Coastguard Worker // If the file descriptor is negative we might have reached a limit
176*dbb99499SAndroid Build Coastguard Worker // in the current group. Set the group_id to -1 and retry
177*dbb99499SAndroid Build Coastguard Worker if (group_id >= 0) {
178*dbb99499SAndroid Build Coastguard Worker // Create a new group
179*dbb99499SAndroid Build Coastguard Worker group_id = -1;
180*dbb99499SAndroid Build Coastguard Worker } else {
181*dbb99499SAndroid Build Coastguard Worker // At this point we have already retried to set a new group id and
182*dbb99499SAndroid Build Coastguard Worker // failed. We then give up.
183*dbb99499SAndroid Build Coastguard Worker break;
184*dbb99499SAndroid Build Coastguard Worker }
185*dbb99499SAndroid Build Coastguard Worker }
186*dbb99499SAndroid Build Coastguard Worker }
187*dbb99499SAndroid Build Coastguard Worker
188*dbb99499SAndroid Build Coastguard Worker // We failed to get a new file descriptor. We might have reached a hard
189*dbb99499SAndroid Build Coastguard Worker // hardware limit that cannot be resolved even with group multiplexing
190*dbb99499SAndroid Build Coastguard Worker if (id < 0) {
191*dbb99499SAndroid Build Coastguard Worker GetErrorLogInstance() << "***WARNING** Failed to get a file descriptor "
192*dbb99499SAndroid Build Coastguard Worker "for performance counter "
193*dbb99499SAndroid Build Coastguard Worker << name << ". Ignoring\n";
194*dbb99499SAndroid Build Coastguard Worker
195*dbb99499SAndroid Build Coastguard Worker // We give up on this counter but try to keep going
196*dbb99499SAndroid Build Coastguard Worker // as the others would be fine
197*dbb99499SAndroid Build Coastguard Worker continue;
198*dbb99499SAndroid Build Coastguard Worker }
199*dbb99499SAndroid Build Coastguard Worker if (group_id < 0) {
200*dbb99499SAndroid Build Coastguard Worker // This is a leader, store and assign it to the current file descriptor
201*dbb99499SAndroid Build Coastguard Worker leader_ids.push_back(id);
202*dbb99499SAndroid Build Coastguard Worker group_id = id;
203*dbb99499SAndroid Build Coastguard Worker }
204*dbb99499SAndroid Build Coastguard Worker // This is a valid counter, add it to our descriptor's list
205*dbb99499SAndroid Build Coastguard Worker counter_ids.push_back(id);
206*dbb99499SAndroid Build Coastguard Worker valid_names.push_back(name);
207*dbb99499SAndroid Build Coastguard Worker }
208*dbb99499SAndroid Build Coastguard Worker
209*dbb99499SAndroid Build Coastguard Worker // Loop through all group leaders activating them
210*dbb99499SAndroid Build Coastguard Worker // There is another option of starting ALL counters in a process but
211*dbb99499SAndroid Build Coastguard Worker // that would be far reaching an intrusion. If the user is using PMCs
212*dbb99499SAndroid Build Coastguard Worker // by themselves then this would have a side effect on them. It is
213*dbb99499SAndroid Build Coastguard Worker // friendlier to loop through all groups individually.
214*dbb99499SAndroid Build Coastguard Worker for (int lead : leader_ids) {
215*dbb99499SAndroid Build Coastguard Worker if (ioctl(lead, PERF_EVENT_IOC_ENABLE) != 0) {
216*dbb99499SAndroid Build Coastguard Worker // This should never happen but if it does, we give up on the
217*dbb99499SAndroid Build Coastguard Worker // entire batch as recovery would be a mess.
218*dbb99499SAndroid Build Coastguard Worker GetErrorLogInstance() << "***WARNING*** Failed to start counters. "
219*dbb99499SAndroid Build Coastguard Worker "Claring out all counters.\n";
220*dbb99499SAndroid Build Coastguard Worker
221*dbb99499SAndroid Build Coastguard Worker // Close all performance counters
222*dbb99499SAndroid Build Coastguard Worker for (int id : counter_ids) {
223*dbb99499SAndroid Build Coastguard Worker ::close(id);
224*dbb99499SAndroid Build Coastguard Worker }
225*dbb99499SAndroid Build Coastguard Worker
226*dbb99499SAndroid Build Coastguard Worker // Return an empty object so our internal state is still good and
227*dbb99499SAndroid Build Coastguard Worker // the process can continue normally without impact
228*dbb99499SAndroid Build Coastguard Worker return NoCounters();
229*dbb99499SAndroid Build Coastguard Worker }
230*dbb99499SAndroid Build Coastguard Worker }
231*dbb99499SAndroid Build Coastguard Worker
232*dbb99499SAndroid Build Coastguard Worker return PerfCounters(std::move(valid_names), std::move(counter_ids),
233*dbb99499SAndroid Build Coastguard Worker std::move(leader_ids));
234*dbb99499SAndroid Build Coastguard Worker }
235*dbb99499SAndroid Build Coastguard Worker
CloseCounters() const236*dbb99499SAndroid Build Coastguard Worker void PerfCounters::CloseCounters() const {
237*dbb99499SAndroid Build Coastguard Worker if (counter_ids_.empty()) {
238*dbb99499SAndroid Build Coastguard Worker return;
239*dbb99499SAndroid Build Coastguard Worker }
240*dbb99499SAndroid Build Coastguard Worker for (int lead : leader_ids_) {
241*dbb99499SAndroid Build Coastguard Worker ioctl(lead, PERF_EVENT_IOC_DISABLE);
242*dbb99499SAndroid Build Coastguard Worker }
243*dbb99499SAndroid Build Coastguard Worker for (int fd : counter_ids_) {
244*dbb99499SAndroid Build Coastguard Worker close(fd);
245*dbb99499SAndroid Build Coastguard Worker }
246*dbb99499SAndroid Build Coastguard Worker }
247*dbb99499SAndroid Build Coastguard Worker #else // defined HAVE_LIBPFM
Read(const std::vector<int> &)248*dbb99499SAndroid Build Coastguard Worker size_t PerfCounterValues::Read(const std::vector<int>&) { return 0; }
249*dbb99499SAndroid Build Coastguard Worker
250*dbb99499SAndroid Build Coastguard Worker const bool PerfCounters::kSupported = false;
251*dbb99499SAndroid Build Coastguard Worker
Initialize()252*dbb99499SAndroid Build Coastguard Worker bool PerfCounters::Initialize() { return false; }
253*dbb99499SAndroid Build Coastguard Worker
IsCounterSupported(const std::string &)254*dbb99499SAndroid Build Coastguard Worker bool PerfCounters::IsCounterSupported(const std::string&) { return false; }
255*dbb99499SAndroid Build Coastguard Worker
Create(const std::vector<std::string> & counter_names)256*dbb99499SAndroid Build Coastguard Worker PerfCounters PerfCounters::Create(
257*dbb99499SAndroid Build Coastguard Worker const std::vector<std::string>& counter_names) {
258*dbb99499SAndroid Build Coastguard Worker if (!counter_names.empty()) {
259*dbb99499SAndroid Build Coastguard Worker GetErrorLogInstance() << "Performance counters not supported.\n";
260*dbb99499SAndroid Build Coastguard Worker }
261*dbb99499SAndroid Build Coastguard Worker return NoCounters();
262*dbb99499SAndroid Build Coastguard Worker }
263*dbb99499SAndroid Build Coastguard Worker
CloseCounters() const264*dbb99499SAndroid Build Coastguard Worker void PerfCounters::CloseCounters() const {}
265*dbb99499SAndroid Build Coastguard Worker #endif // defined HAVE_LIBPFM
266*dbb99499SAndroid Build Coastguard Worker
PerfCountersMeasurement(const std::vector<std::string> & counter_names)267*dbb99499SAndroid Build Coastguard Worker PerfCountersMeasurement::PerfCountersMeasurement(
268*dbb99499SAndroid Build Coastguard Worker const std::vector<std::string>& counter_names)
269*dbb99499SAndroid Build Coastguard Worker : start_values_(counter_names.size()), end_values_(counter_names.size()) {
270*dbb99499SAndroid Build Coastguard Worker counters_ = PerfCounters::Create(counter_names);
271*dbb99499SAndroid Build Coastguard Worker }
272*dbb99499SAndroid Build Coastguard Worker
operator =(PerfCounters && other)273*dbb99499SAndroid Build Coastguard Worker PerfCounters& PerfCounters::operator=(PerfCounters&& other) noexcept {
274*dbb99499SAndroid Build Coastguard Worker if (this != &other) {
275*dbb99499SAndroid Build Coastguard Worker CloseCounters();
276*dbb99499SAndroid Build Coastguard Worker
277*dbb99499SAndroid Build Coastguard Worker counter_ids_ = std::move(other.counter_ids_);
278*dbb99499SAndroid Build Coastguard Worker leader_ids_ = std::move(other.leader_ids_);
279*dbb99499SAndroid Build Coastguard Worker counter_names_ = std::move(other.counter_names_);
280*dbb99499SAndroid Build Coastguard Worker }
281*dbb99499SAndroid Build Coastguard Worker return *this;
282*dbb99499SAndroid Build Coastguard Worker }
283*dbb99499SAndroid Build Coastguard Worker } // namespace internal
284*dbb99499SAndroid Build Coastguard Worker } // namespace benchmark
285