blob: bf73fec84fd2c4f622bb8c6a1ab6fe6ad3f01134 [file] [log] [blame]
Moritz Pflanzer45634b42017-08-30 12:48:18 +01001/*
2 * Copyright (c) 2017 ARM Limited.
3 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24#include "MaliCounter.h"
25
26namespace arm_compute
27{
28namespace test
29{
30namespace framework
31{
32namespace
33{
34struct MaliHWInfo
35{
36 unsigned mp_count;
37 unsigned gpu_id;
38 unsigned r_value;
39 unsigned p_value;
40 unsigned core_mask;
41};
42
43MaliHWInfo get_mali_hw_info(const char *path)
44{
45 int fd = open(path, O_RDWR); // NOLINT
46
47 if(fd < 0)
48 {
49 throw std::runtime_error("Failed to get HW info.");
50 }
51
52 {
53 mali_userspace::uku_version_check_args version_check_args; // NOLINT
54 version_check_args.header.id = mali_userspace::UKP_FUNC_ID_CHECK_VERSION; // NOLINT
55 version_check_args.major = 10;
56 version_check_args.minor = 2;
57
58 if(mali_userspace::mali_ioctl(fd, version_check_args) != 0)
59 {
60 throw std::runtime_error("Failed to check version.");
61 close(fd);
62 }
63 }
64
65 {
66 mali_userspace::kbase_uk_hwcnt_reader_set_flags flags; // NOLINT
67 memset(&flags, 0, sizeof(flags));
68 flags.header.id = mali_userspace::KBASE_FUNC_SET_FLAGS; // NOLINT
69 flags.create_flags = mali_userspace::BASE_CONTEXT_CREATE_KERNEL_FLAGS;
70
71 if(mali_userspace::mali_ioctl(fd, flags) != 0)
72 {
73 throw std::runtime_error("Failed settings flags ioctl.");
74 close(fd);
75 }
76 }
77
78 {
79 mali_userspace::kbase_uk_gpuprops props; // NOLINT
80 props.header.id = mali_userspace::KBASE_FUNC_GPU_PROPS_REG_DUMP; // NOLINT
81
82 if(mali_ioctl(fd, props) != 0)
83 {
84 throw std::runtime_error("Failed settings flags ioctl.");
85 close(fd);
86 }
87
88 MaliHWInfo hw_info; // NOLINT
89 memset(&hw_info, 0, sizeof(hw_info));
90 hw_info.gpu_id = props.props.core_props.product_id;
91 hw_info.r_value = props.props.core_props.major_revision;
92 hw_info.p_value = props.props.core_props.minor_revision;
93
94 for(unsigned int i = 0; i < props.props.coherency_info.num_core_groups; ++i)
95 {
96 hw_info.core_mask |= props.props.coherency_info.group[i].core_mask;
97 }
98
99 hw_info.mp_count = __builtin_popcountll(hw_info.core_mask);
100
101 close(fd);
102
103 return hw_info;
104 }
105}
106} // namespace
107
108MaliCounter::MaliCounter()
109{
110 _counters =
111 {
112 { "GPU_ACTIVE", TypedMeasurement<uint64_t>(0, "cycles") },
113 };
114
115 _core_counters =
116 {
117 { "ARITH_WORDS", { "Arithmetic pipe", std::map<int, uint64_t>(), "instructions" } },
118 { "LS_ISSUE", { "LS pipe", std::map<int, uint64_t>(), "instructions" } },
119 { "TEX_ISSUE", { "Texture pipe", std::map<int, uint64_t>(), "instructions" } },
120 { "COMPUTE_ACTIVE", { "Compute core", std::map<int, uint64_t>(), "cycles" } },
121 { "FRAG_ACTIVE", { "Fragment core", std::map<int, uint64_t>(), "cycles" } },
122 };
123
124 init();
125}
126
127MaliCounter::~MaliCounter()
128{
129 term();
130}
131
132void MaliCounter::init()
133{
134 term();
135
136 MaliHWInfo hw_info = get_mali_hw_info(_device);
137
138 _num_cores = hw_info.mp_count;
139
140 _fd = open(_device, O_RDWR | O_CLOEXEC | O_NONBLOCK); // NOLINT
141
142 if(_fd < 0)
143 {
144 throw std::runtime_error("Failed to open /dev/mali0.");
145 }
146
147 {
148 mali_userspace::kbase_uk_hwcnt_reader_version_check_args check; // NOLINT
149 memset(&check, 0, sizeof(check));
150
151 if(mali_userspace::mali_ioctl(_fd, check) != 0)
152 {
153 throw std::runtime_error("Failed to get ABI version.");
154 }
155 else if(check.major < 10)
156 {
157 throw std::runtime_error("Unsupported ABI version 10.");
158 }
159 }
160
161 {
162 mali_userspace::kbase_uk_hwcnt_reader_set_flags flags; // NOLINT
163 memset(&flags, 0, sizeof(flags));
164 flags.header.id = mali_userspace::KBASE_FUNC_SET_FLAGS; // NOLINT
165 flags.create_flags = mali_userspace::BASE_CONTEXT_CREATE_KERNEL_FLAGS;
166
167 if(mali_userspace::mali_ioctl(_fd, flags) != 0)
168 {
169 throw std::runtime_error("Failed settings flags ioctl.");
170 }
171 }
172
173 {
174 mali_userspace::kbase_uk_hwcnt_reader_setup setup; // NOLINT
175 memset(&setup, 0, sizeof(setup));
176 setup.header.id = mali_userspace::KBASE_FUNC_HWCNT_READER_SETUP; // NOLINT
177 setup.buffer_count = _buffer_count;
178 setup.jm_bm = -1;
179 setup.shader_bm = -1;
180 setup.tiler_bm = -1;
181 setup.mmu_l2_bm = -1;
182 setup.fd = -1;
183
184 if(mali_userspace::mali_ioctl(_fd, setup) != 0)
185 {
186 throw std::runtime_error("Failed setting hwcnt reader ioctl.");
187 }
188
189 _hwc_fd = setup.fd;
190 }
191
192 {
193 uint32_t api_version = ~mali_userspace::HWCNT_READER_API;
194
195 if(ioctl(_hwc_fd, mali_userspace::KBASE_HWCNT_READER_GET_API_VERSION, &api_version) != 0) // NOLINT
196 {
197 throw std::runtime_error("Could not determine hwcnt reader API.");
198 }
199 else if(api_version != mali_userspace::HWCNT_READER_API)
200 {
201 throw std::runtime_error("Invalid API version.");
202 }
203 }
204
205 if(ioctl(_hwc_fd, mali_userspace::KBASE_HWCNT_READER_GET_BUFFER_SIZE, &_buffer_size) != 0) // NOLINT
206 {
207 throw std::runtime_error("Failed to get buffer size.");
208 }
209
210 if(ioctl(_hwc_fd, mali_userspace::KBASE_HWCNT_READER_GET_HWVER, &_hw_ver) != 0) // NOLINT
211 {
212 throw std::runtime_error("Could not determine HW version.");
213 }
214
215 if(_hw_ver < 5)
216 {
217 throw std::runtime_error("Unsupported HW version.");
218 }
219
220 _sample_data = static_cast<uint8_t *>(mmap(nullptr, _buffer_count * _buffer_size, PROT_READ, MAP_PRIVATE, _hwc_fd, 0));
221
222 if(_sample_data == MAP_FAILED) // NOLINT
223 {
224 throw std::runtime_error("Failed to map sample data.");
225 }
226
227 auto product = std::find_if(std::begin(mali_userspace::products), std::end(mali_userspace::products), [&](const mali_userspace::CounterMapping & cm)
228 {
229 return (cm.product_mask & hw_info.gpu_id) == cm.product_id;
230 });
231
232 if(product != std::end(mali_userspace::products))
233 {
234 _names_lut = product->names_lut;
235 }
236 else
237 {
238 throw std::runtime_error("Could not identify GPU.");
239 }
240
241 _raw_counter_buffer.resize(_buffer_size / sizeof(uint32_t));
242
243 // Build core remap table.
244 _core_index_remap.clear();
245 _core_index_remap.reserve(hw_info.mp_count);
246
247 unsigned int mask = hw_info.core_mask;
248
249 while(mask != 0)
250 {
251 unsigned int bit = __builtin_ctz(mask);
252 _core_index_remap.push_back(bit);
253 mask &= ~(1u << bit);
254 }
255}
256
257void MaliCounter::term()
258{
259 if(_sample_data != nullptr)
260 {
261 munmap(_sample_data, _buffer_count * _buffer_size);
262 _sample_data = nullptr;
263 }
264
265 if(_hwc_fd >= 0)
266 {
267 close(_hwc_fd);
268 _hwc_fd = -1;
269 }
270
271 if(_fd >= 0)
272 {
273 close(_fd);
274 _fd = -1;
275 }
276}
277
278void MaliCounter::sample_counters()
279{
280 if(ioctl(_hwc_fd, mali_userspace::KBASE_HWCNT_READER_DUMP, 0) != 0)
281 {
282 throw std::runtime_error("Could not sample hardware counters.");
283 }
284}
285
286void MaliCounter::wait_next_event()
287{
288 pollfd poll_fd; // NOLINT
289 poll_fd.fd = _hwc_fd;
290 poll_fd.events = POLLIN;
291
292 const int count = poll(&poll_fd, 1, -1);
293
294 if(count < 0)
295 {
296 throw std::runtime_error("poll() failed.");
297 }
298
299 if((poll_fd.revents & POLLIN) != 0)
300 {
301 mali_userspace::kbase_hwcnt_reader_metadata meta; // NOLINT
302
303 if(ioctl(_hwc_fd, mali_userspace::KBASE_HWCNT_READER_GET_BUFFER, &meta) != 0) // NOLINT
304 {
305 throw std::runtime_error("Failed READER_GET_BUFFER.");
306 }
307
308 memcpy(_raw_counter_buffer.data(), _sample_data + _buffer_size * meta.buffer_idx, _buffer_size);
309 _timestamp = meta.timestamp;
310
311 if(ioctl(_hwc_fd, mali_userspace::KBASE_HWCNT_READER_PUT_BUFFER, &meta) != 0) // NOLINT
312 {
313 throw std::runtime_error("Failed READER_PUT_BUFFER.");
314 }
315 }
316 else if((poll_fd.revents & POLLHUP) != 0)
317 {
318 throw std::runtime_error("HWC hung up.");
319 }
320}
321
322const uint32_t *MaliCounter::get_counters() const
323{
324 return _raw_counter_buffer.data();
325}
326
327const uint32_t *MaliCounter::get_counters(mali_userspace::MaliCounterBlockName block, int core) const
328{
329 switch(block)
330 {
331 case mali_userspace::MALI_NAME_BLOCK_JM:
332 return _raw_counter_buffer.data() + mali_userspace::MALI_NAME_BLOCK_SIZE * 0;
333 case mali_userspace::MALI_NAME_BLOCK_MMU:
334 return _raw_counter_buffer.data() + mali_userspace::MALI_NAME_BLOCK_SIZE * 2;
335 case mali_userspace::MALI_NAME_BLOCK_TILER:
336 return _raw_counter_buffer.data() + mali_userspace::MALI_NAME_BLOCK_SIZE * 1;
337 default:
338 if(core < 0)
339 {
340 std::runtime_error("Invalid core number.");
341 }
342
343 return _raw_counter_buffer.data() + mali_userspace::MALI_NAME_BLOCK_SIZE * (3 + _core_index_remap[core]);
344 }
345}
346
347int MaliCounter::find_counter_index_by_name(mali_userspace::MaliCounterBlockName block, const char *name)
348{
349 const char *const *names = &_names_lut[mali_userspace::MALI_NAME_BLOCK_SIZE * block];
350
351 for(int i = 0; i < mali_userspace::MALI_NAME_BLOCK_SIZE; ++i)
352 {
353 if(strstr(names[i], name) != nullptr)
354 {
355 return i;
356 }
357 }
358
359 return -1;
360}
361
362void MaliCounter::start()
363{
364 sample_counters();
365 wait_next_event();
366 _start_time = _timestamp;
367}
368
369void MaliCounter::stop()
370{
371 sample_counters();
372 wait_next_event();
373
374 const auto counter = get_counters(mali_userspace::MALI_NAME_BLOCK_JM);
375 _counters.at("GPU_ACTIVE").value = counter[find_counter_index_by_name(mali_userspace::MALI_NAME_BLOCK_JM, "GPU_ACTIVE")];
376
377 const int arith_index = find_counter_index_by_name(mali_userspace::MALI_NAME_BLOCK_SHADER, "ARITH_WORDS");
378 const int ls_index = find_counter_index_by_name(mali_userspace::MALI_NAME_BLOCK_SHADER, "LS_ISSUE");
379 const int tex_index = find_counter_index_by_name(mali_userspace::MALI_NAME_BLOCK_SHADER, "TEX_ISSUE");
380 const int compute_index = find_counter_index_by_name(mali_userspace::MALI_NAME_BLOCK_SHADER, "COMPUTE_ACTIVE");
381 const int frag_index = find_counter_index_by_name(mali_userspace::MALI_NAME_BLOCK_SHADER, "FRAG_ACTIVE");
382
383 // Shader core counters can be averaged if desired, but here we don't.
384 for(int core = 0; core < _num_cores; ++core)
385 {
386 const auto sc_counter = get_counters(mali_userspace::MALI_NAME_BLOCK_SHADER, core);
387
388 _core_counters.at("ARITH_WORDS").values[core] = sc_counter[arith_index];
389 _core_counters.at("LS_ISSUE").values[core] = sc_counter[ls_index];
390 _core_counters.at("TEX_ISSUE").values[core] = sc_counter[tex_index];
391 _core_counters.at("COMPUTE_ACTIVE").values[core] = sc_counter[compute_index];
392 _core_counters.at("FRAG_ACTIVE").values[core] = sc_counter[frag_index];
393 }
394
395 _stop_time = _timestamp;
396}
397
398std::string MaliCounter::id() const
399{
400 return "Mali Counter";
401}
402
403Instrument::MeasurementsMap MaliCounter::measurements() const
404{
405 MeasurementsMap measurements
406 {
407 { "Timespan", TypedMeasurement<uint64_t>(_stop_time - _start_time, "ns") },
408 { "GPU active", _counters.at("GPU_ACTIVE") },
409 };
410
411 for(const auto &counter : _core_counters)
412 {
413 for(const auto &core : counter.second.values)
414 {
415 measurements.emplace(counter.second.name + " #" + support::cpp11::to_string(core.first), TypedMeasurement<uint64_t>(core.second, counter.second.unit));
416 }
417 }
418
419 return measurements;
420}
421} // namespace framework
422} // namespace test
423} // namespace arm_compute