1919#include < vector>
2020
2121#if defined HAVE_LIBPFM
22+ #include < unordered_map>
2223#include " perfmon/pfmlib.h"
2324#include " perfmon/pfmlib_perf_event.h"
2425#endif
2526
2627namespace benchmark {
2728namespace internal {
2829
29- constexpr size_t PerfCounterValues::kMaxCounters ;
30-
3130#if defined HAVE_LIBPFM
31+
32+ class SinglePMURegistry {
33+ public:
34+ ~SinglePMURegistry () = default ;
35+ SinglePMURegistry (SinglePMURegistry&&) = default ;
36+ SinglePMURegistry (const SinglePMURegistry&) = delete ;
37+ SinglePMURegistry& operator =(SinglePMURegistry&&) noexcept ;
38+ SinglePMURegistry& operator =(const SinglePMURegistry&) = delete ;
39+
40+ SinglePMURegistry (pfm_pmu_t pmu_id)
41+ : pmu_id_(pmu_id), available_counters_(0 ), available_fixed_counters_(0 ) {
42+ {
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_ << " fixed: " << available_fixed_counters_ << " \n " ;
58+ }
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+ if (counter_ids_.size () >= available_counters_ - 1 ) {
78+ GetErrorLogInstance () << " Maximal number of counters for PMU " << name_
79+ << " (" << available_counters_ << " ) reached.\n " ;
80+ return false ;
81+ }
82+
83+ counter_ids_.emplace (event_id, info.code );
84+
85+ BM_VLOG (2 ) << " Registered counter: " << event_id << " (" << info.name << " - " << info.desc
86+ << " ) in pmu " << name_ << " (" << counter_ids_.size () << " /" << available_counters_ << " \n " ;
87+
88+ return true ;
89+ }
90+
91+ private:
92+ pfm_pmu_t pmu_id_;
93+ const char * name_;
94+ const char * desc_;
95+ std::unordered_map<int , uint64_t > counter_ids_;
96+ std::unordered_map<int , uint64_t > fixed_counter_ids_;
97+ uint64_t available_counters_;
98+ uint64_t available_fixed_counters_;
99+ };
100+
101+ class PMURegistry {
102+ public:
103+ ~PMURegistry () = default ;
104+ PMURegistry (PMURegistry&&) = default ;
105+ PMURegistry (const PMURegistry&) = delete ;
106+ PMURegistry& operator =(PMURegistry&&) noexcept ;
107+ PMURegistry& operator =(const PMURegistry&) = delete ;
108+ PMURegistry () {}
109+
110+ bool EnlistCounter (const std::string& name, struct perf_event_attr &attr_base) {
111+ attr_base.size = sizeof (attr_base);
112+ pfm_perf_encode_arg_t encoding{};
113+ encoding.attr = &attr_base;
114+
115+ const auto pfm_get = pfm_get_os_event_encoding (
116+ name.c_str (), PFM_PLM3 , PFM_OS_PERF_EVENT , &encoding);
117+ if (pfm_get != PFM_SUCCESS ) {
118+ GetErrorLogInstance () << " Unknown counter name: " << name << " \n " ;
119+ return false ;
120+ }
121+
122+ pfm_event_info_t info{};
123+ const auto pfm_info =
124+ pfm_get_event_info (encoding.idx , PFM_OS_PERF_EVENT , &info);
125+ if (pfm_info != PFM_SUCCESS ) {
126+ GetErrorLogInstance ()
127+ << " Unknown counter idx: " << encoding.idx << " (" << name << " )\n " ;
128+ return false ;
129+ }
130+
131+ // Spin-up a new per-PMU sub-registry if needed
132+ if (pmu_registry_.find (info.pmu ) == pmu_registry_.end ()) {
133+ pmu_registry_.emplace (info.pmu , SinglePMURegistry (info.pmu ));
134+ }
135+
136+ auto & single_pmu = pmu_registry_.find (info.pmu )->second ;
137+
138+ return single_pmu.AddCounter (info.idx );
139+ }
140+
141+ private:
142+ std::unordered_map<pfm_pmu_t , SinglePMURegistry> pmu_registry_;
143+ };
144+
32145const bool PerfCounters::kSupported = true ;
33146
34147bool PerfCounters::Initialize () { return pfm_initialize () == PFM_SUCCESS ; }
@@ -38,35 +151,28 @@ PerfCounters PerfCounters::Create(
38151 if (counter_names.empty ()) {
39152 return NoCounters ();
40153 }
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- }
154+
48155 std::vector<int > counter_ids (counter_names.size ());
156+ PMURegistry registry{};
49157
50- const int mode = PFM_PLM3 ; // user mode only
51158 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 ;
56159 const auto & name = counter_names[i];
57160 if (name.empty ()) {
58161 GetErrorLogInstance () << " A counter name was the empty string\n " ;
59162 return NoCounters ();
60163 }
61- pfm_perf_encode_arg_t arg{};
62- arg.attr = &attr;
63164
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 " ;
165+ struct perf_event_attr attr {};
166+ auto ok = registry.EnlistCounter (name, attr);
167+
168+ if (!ok) {
169+ GetErrorLogInstance () << " Failed to register counter: " << name << " \n " ;
68170 return NoCounters ();
69171 }
172+
173+ const bool is_first = i == 0 ;
174+ const int group_id = !is_first ? counter_ids[0 ] : -1 ;
175+
70176 attr.disabled = is_first;
71177 // Note: the man page for perf_event_create suggests inerit = true and
72178 // read_format = PERF_FORMAT_GROUP don't work together, but that's not the
0 commit comments