1919#include < vector>
2020
2121#if defined HAVE_LIBPFM
22+ #include < unordered_map>
23+
2224#include " perfmon/pfmlib.h"
2325#include " perfmon/pfmlib_perf_event.h"
2426#endif
2527
2628namespace benchmark {
2729namespace internal {
2830
29- constexpr size_t PerfCounterValues::kMaxCounters ;
30-
3131#if defined HAVE_LIBPFM
32+
33+ class SinglePMURegistry {
34+ public:
35+ ~SinglePMURegistry () = default ;
36+ SinglePMURegistry (SinglePMURegistry&&) = default ;
37+ SinglePMURegistry (const SinglePMURegistry&) = delete ;
38+ SinglePMURegistry& operator =(SinglePMURegistry&&) noexcept ;
39+ SinglePMURegistry& operator =(const SinglePMURegistry&) = delete ;
40+
41+ SinglePMURegistry (pfm_pmu_t pmu_id)
42+ : pmu_id_(pmu_id), available_counters_(0 ), available_fixed_counters_(0 ) {
43+ pfm_pmu_info_t pmu_info{};
44+ const auto pfm_pmu = pfm_get_pmu_info (pmu_id, &pmu_info);
45+
46+ if (pfm_pmu != PFM_SUCCESS) {
47+ GetErrorLogInstance () << " Unknown pmu: " << pmu_id << " \n " ;
48+ return ;
49+ }
50+
51+ name_ = pmu_info.name ;
52+ desc_ = pmu_info.desc ;
53+ available_counters_ = pmu_info.num_cntrs ;
54+ available_fixed_counters_ = pmu_info.num_fixed_cntrs ;
55+
56+ BM_VLOG (1 ) << " PMU: " << pmu_id << " " << name_ << " " << desc_ << " \n " ;
57+ BM_VLOG (1 ) << " counters: " << available_counters_
58+ << " fixed: " << available_fixed_counters_ << " \n " ;
59+ }
60+
61+ const char * name () const { return name_; }
62+
63+ bool AddCounter (int event_id) {
64+ pfm_event_info_t info{};
65+ const auto pfm_event_info =
66+ pfm_get_event_info (event_id, PFM_OS_PERF_EVENT, &info);
67+
68+ if (pfm_event_info != PFM_SUCCESS) {
69+ GetErrorLogInstance () << " Unknown event id: " << event_id << " \n " ;
70+ return false ;
71+ }
72+
73+ assert (info.pmu == pmu_id_);
74+
75+ if (counter_ids_.find (event_id) != counter_ids_.end ()) return true ;
76+
77+ assert (std::numeric_limits<int >::max () > counter_ids_.size ());
78+ if (static_cast <int >(counter_ids_.size ()) >= available_counters_ - 1 ) {
79+ GetErrorLogInstance () << " Maximal number of counters for PMU " << name_
80+ << " (" << available_counters_ << " ) reached.\n " ;
81+ return false ;
82+ }
83+
84+ counter_ids_.emplace (event_id, info.code );
85+
86+ BM_VLOG (2 ) << " Registered counter: " << event_id << " (" << info.name
87+ << " - " << info.desc << " ) in pmu " << name_ << " ("
88+ << counter_ids_.size () << " /" << available_counters_ << " \n " ;
89+
90+ return true ;
91+ }
92+
93+ private:
94+ pfm_pmu_t pmu_id_;
95+ const char * name_;
96+ const char * desc_;
97+ std::unordered_map<int , uint64_t > counter_ids_;
98+ std::unordered_map<int , uint64_t > fixed_counter_ids_;
99+ int available_counters_;
100+ int available_fixed_counters_;
101+ };
102+
103+ class PMURegistry {
104+ public:
105+ ~PMURegistry () = default ;
106+ PMURegistry (PMURegistry&&) = default ;
107+ PMURegistry (const PMURegistry&) = delete ;
108+ PMURegistry& operator =(PMURegistry&&) noexcept ;
109+ PMURegistry& operator =(const PMURegistry&) = delete ;
110+ PMURegistry () {}
111+
112+ bool EnlistCounter (const std::string& name,
113+ struct perf_event_attr & attr_base) {
114+ attr_base.size = sizeof (attr_base);
115+ pfm_perf_encode_arg_t encoding{};
116+ encoding.attr = &attr_base;
117+
118+ const auto pfm_get = pfm_get_os_event_encoding (
119+ name.c_str (), PFM_PLM3, PFM_OS_PERF_EVENT, &encoding);
120+ if (pfm_get != PFM_SUCCESS) {
121+ GetErrorLogInstance () << " Unknown counter name: " << name << " \n " ;
122+ return false ;
123+ }
124+
125+ pfm_event_info_t info{};
126+ const auto pfm_info =
127+ pfm_get_event_info (encoding.idx , PFM_OS_PERF_EVENT, &info);
128+ if (pfm_info != PFM_SUCCESS) {
129+ GetErrorLogInstance ()
130+ << " Unknown counter idx: " << encoding.idx << " (" << name << " )\n " ;
131+ return false ;
132+ }
133+
134+ // Spin-up a new per-PMU sub-registry if needed
135+ if (pmu_registry_.find (info.pmu ) == pmu_registry_.end ()) {
136+ pmu_registry_.emplace (info.pmu , SinglePMURegistry (info.pmu ));
137+ }
138+
139+ auto & single_pmu = pmu_registry_.find (info.pmu )->second ;
140+
141+ return single_pmu.AddCounter (info.idx );
142+ }
143+
144+ private:
145+ std::unordered_map<pfm_pmu_t , SinglePMURegistry> pmu_registry_;
146+ };
147+
32148const bool PerfCounters::kSupported = true ;
33149
34150bool PerfCounters::Initialize () { return pfm_initialize () == PFM_SUCCESS; }
@@ -38,35 +154,28 @@ PerfCounters PerfCounters::Create(
38154 if (counter_names.empty ()) {
39155 return NoCounters ();
40156 }
41- if (counter_names.size () > PerfCounterValues::kMaxCounters ) {
42- GetErrorLogInstance ()
43- << counter_names.size ()
44- << " counters were requested. The minimum is 1, the maximum is "
45- << PerfCounterValues::kMaxCounters << " \n " ;
46- return NoCounters ();
47- }
157+
48158 std::vector<int > counter_ids (counter_names.size ());
159+ PMURegistry registry{};
49160
50- const int mode = PFM_PLM3; // user mode only
51161 for (size_t i = 0 ; i < counter_names.size (); ++i) {
52- const bool is_first = i == 0 ;
53- struct perf_event_attr attr {};
54- attr.size = sizeof (attr);
55- const int group_id = !is_first ? counter_ids[0 ] : -1 ;
56162 const auto & name = counter_names[i];
57163 if (name.empty ()) {
58164 GetErrorLogInstance () << " A counter name was the empty string\n " ;
59165 return NoCounters ();
60166 }
61- pfm_perf_encode_arg_t arg{};
62- arg.attr = &attr;
63167
64- const int pfm_get =
65- pfm_get_os_event_encoding (name.c_str (), mode, PFM_OS_PERF_EVENT, &arg);
66- if (pfm_get != PFM_SUCCESS) {
67- GetErrorLogInstance () << " Unknown counter name: " << name << " \n " ;
168+ struct perf_event_attr attr {};
169+ auto ok = registry.EnlistCounter (name, attr);
170+
171+ if (!ok) {
172+ GetErrorLogInstance () << " Failed to register counter: " << name << " \n " ;
68173 return NoCounters ();
69174 }
175+
176+ const bool is_first = i == 0 ;
177+ const int group_id = !is_first ? counter_ids[0 ] : -1 ;
178+
70179 attr.disabled = is_first;
71180 // Note: the man page for perf_event_create suggests inerit = true and
72181 // read_format = PERF_FORMAT_GROUP don't work together, but that's not the
0 commit comments