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
+ 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 << " - " << info.desc
87
+ << " ) in pmu " << name_ << " (" << counter_ids_.size () << " /" << available_counters_ << " \n " ;
88
+
89
+ return true ;
90
+ }
91
+
92
+ private:
93
+ pfm_pmu_t pmu_id_;
94
+ const char * name_;
95
+ const char * desc_;
96
+ std::unordered_map<int , uint64_t > counter_ids_;
97
+ std::unordered_map<int , uint64_t > fixed_counter_ids_;
98
+ int available_counters_;
99
+ int available_fixed_counters_;
100
+ };
101
+
102
+ class PMURegistry {
103
+ public:
104
+ ~PMURegistry () = default ;
105
+ PMURegistry (PMURegistry&&) = default ;
106
+ PMURegistry (const PMURegistry&) = delete ;
107
+ PMURegistry& operator =(PMURegistry&&) noexcept ;
108
+ PMURegistry& operator =(const PMURegistry&) = delete ;
109
+ PMURegistry () {}
110
+
111
+ bool EnlistCounter (const std::string& name, struct perf_event_attr &attr_base) {
112
+ attr_base.size = sizeof (attr_base);
113
+ pfm_perf_encode_arg_t encoding{};
114
+ encoding.attr = &attr_base;
115
+
116
+ const auto pfm_get = pfm_get_os_event_encoding (
117
+ name.c_str (), PFM_PLM3, PFM_OS_PERF_EVENT, &encoding);
118
+ if (pfm_get != PFM_SUCCESS) {
119
+ GetErrorLogInstance () << " Unknown counter name: " << name << " \n " ;
120
+ return false ;
121
+ }
122
+
123
+ pfm_event_info_t info{};
124
+ const auto pfm_info =
125
+ pfm_get_event_info (encoding.idx , PFM_OS_PERF_EVENT, &info);
126
+ if (pfm_info != PFM_SUCCESS) {
127
+ GetErrorLogInstance ()
128
+ << " Unknown counter idx: " << encoding.idx << " (" << name << " )\n " ;
129
+ return false ;
130
+ }
131
+
132
+ // Spin-up a new per-PMU sub-registry if needed
133
+ if (pmu_registry_.find (info.pmu ) == pmu_registry_.end ()) {
134
+ pmu_registry_.emplace (info.pmu , SinglePMURegistry (info.pmu ));
135
+ }
136
+
137
+ auto & single_pmu = pmu_registry_.find (info.pmu )->second ;
138
+
139
+ return single_pmu.AddCounter (info.idx );
140
+ }
141
+
142
+ private:
143
+ std::unordered_map<pfm_pmu_t , SinglePMURegistry> pmu_registry_;
144
+ };
145
+
32
146
const bool PerfCounters::kSupported = true ;
33
147
34
148
bool PerfCounters::Initialize () { return pfm_initialize () == PFM_SUCCESS; }
@@ -38,35 +152,28 @@ PerfCounters PerfCounters::Create(
38
152
if (counter_names.empty ()) {
39
153
return NoCounters ();
40
154
}
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
- }
155
+
48
156
std::vector<int > counter_ids (counter_names.size ());
157
+ PMURegistry registry{};
49
158
50
- const int mode = PFM_PLM3; // user mode only
51
159
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
160
const auto & name = counter_names[i];
57
161
if (name.empty ()) {
58
162
GetErrorLogInstance () << " A counter name was the empty string\n " ;
59
163
return NoCounters ();
60
164
}
61
- pfm_perf_encode_arg_t arg{};
62
- arg.attr = &attr;
63
165
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 " ;
166
+ struct perf_event_attr attr {};
167
+ auto ok = registry.EnlistCounter (name, attr);
168
+
169
+ if (!ok) {
170
+ GetErrorLogInstance () << " Failed to register counter: " << name << " \n " ;
68
171
return NoCounters ();
69
172
}
173
+
174
+ const bool is_first = i == 0 ;
175
+ const int group_id = !is_first ? counter_ids[0 ] : -1 ;
176
+
70
177
attr.disabled = is_first;
71
178
// Note: the man page for perf_event_create suggests inerit = true and
72
179
// read_format = PERF_FORMAT_GROUP don't work together, but that's not the
0 commit comments