1 // Copyright 2021 Google Inc. All rights reserved. 2 // 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 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 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. 14 15 #include "perf_counters.h" 16 17 #include <cstring> 18 #include <vector> 19 20 #if defined HAVE_LIBPFM 21 #include "perfmon/pfmlib.h" 22 #include "perfmon/pfmlib_perf_event.h" 23 #endif 24 25 namespace benchmark { 26 namespace internal { 27 28 constexpr size_t PerfCounterValues::kMaxCounters; 29 30 #if defined HAVE_LIBPFM 31 const bool PerfCounters::kSupported = true; 32 33 bool PerfCounters::Initialize() { return pfm_initialize() == PFM_SUCCESS; } 34 35 PerfCounters PerfCounters::Create( 36 const std::vector<std::string>& counter_names) { 37 if (counter_names.empty()) { 38 return NoCounters(); 39 } 40 if (counter_names.size() > PerfCounterValues::kMaxCounters) { 41 GetErrorLogInstance() 42 << counter_names.size() 43 << " counters were requested. The minimum is 1, the maximum is " 44 << PerfCounterValues::kMaxCounters << "\n"; 45 return NoCounters(); 46 } 47 std::vector<int> counter_ids(counter_names.size()); 48 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]; 56 if (name.empty()) { 57 GetErrorLogInstance() << "A counter name was the empty string\n"; 58 return NoCounters(); 59 } 60 pfm_perf_encode_arg_t arg{}; 61 arg.attr = &attr; 62 63 const int pfm_get = 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"; 67 return NoCounters(); 68 } 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 72 // case. 73 attr.inherit = true; 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; 80 81 int id = -1; 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; 85 ++num_retries) { 86 id = perf_event_open(&attr, 0, -1, group_id, 0); 87 if (id >= 0 || errno != EINTR) { 88 break; 89 } 90 } 91 if (id < 0) { 92 GetErrorLogInstance() 93 << "Failed to get a file descriptor for " << name << "\n"; 94 return NoCounters(); 95 } 96 97 counter_ids[i] = id; 98 } 99 if (ioctl(counter_ids[0], PERF_EVENT_IOC_ENABLE) != 0) { 100 GetErrorLogInstance() << "Failed to start counters\n"; 101 return NoCounters(); 102 } 103 104 return PerfCounters(counter_names, std::move(counter_ids)); 105 } 106 107 PerfCounters::~PerfCounters() { 108 if (counter_ids_.empty()) { 109 return; 110 } 111 ioctl(counter_ids_[0], PERF_EVENT_IOC_DISABLE); 112 for (int fd : counter_ids_) { 113 close(fd); 114 } 115 } 116 #else // defined HAVE_LIBPFM 117 const bool PerfCounters::kSupported = false; 118 119 bool PerfCounters::Initialize() { return false; } 120 121 PerfCounters PerfCounters::Create( 122 const std::vector<std::string>& counter_names) { 123 if (!counter_names.empty()) { 124 GetErrorLogInstance() << "Performance counters not supported."; 125 } 126 return NoCounters(); 127 } 128 129 PerfCounters::~PerfCounters() = default; 130 #endif // defined HAVE_LIBPFM 131 } // namespace internal 132 } // namespace benchmark 133