19
19
#include < vector>
20
20
21
21
#if defined HAVE_LIBPFM
22
+ #include < unordered_map>
22
23
#include " perfmon/pfmlib.h"
23
24
#include " perfmon/pfmlib_perf_event.h"
24
25
#endif
25
26
26
27
namespace benchmark {
27
28
namespace internal {
28
29
29
- constexpr size_t PerfCounterValues::kMaxCounters ;
30
-
31
30
#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
+
32
145
const bool PerfCounters::kSupported = true ;
33
146
34
147
bool PerfCounters::Initialize () { return pfm_initialize () == PFM_SUCCESS; }
@@ -38,35 +151,28 @@ PerfCounters PerfCounters::Create(
38
151
if (counter_names.empty ()) {
39
152
return NoCounters ();
40
153
}
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
+
48
155
std::vector<int > counter_ids (counter_names.size ());
156
+ PMURegistry registry{};
49
157
50
- const int mode = PFM_PLM3; // user mode only
51
158
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 ;
56
159
const auto & name = counter_names[i];
57
160
if (name.empty ()) {
58
161
GetErrorLogInstance () << " A counter name was the empty string\n " ;
59
162
return NoCounters ();
60
163
}
61
- pfm_perf_encode_arg_t arg{};
62
- arg.attr = &attr;
63
164
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 " ;
68
170
return NoCounters ();
69
171
}
172
+
173
+ const bool is_first = i == 0 ;
174
+ const int group_id = !is_first ? counter_ids[0 ] : -1 ;
175
+
70
176
attr.disabled = is_first;
71
177
// Note: the man page for perf_event_create suggests inerit = true and
72
178
// read_format = PERF_FORMAT_GROUP don't work together, but that's not the
0 commit comments