1 //
2 // Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6 #include <common/include/CounterDirectory.hpp>
7 #include <common/include/CommonProfilingUtils.hpp>
8
9 #include <common/include/Assert.hpp>
10 #include <common/include/SwTrace.hpp>
11
12 #include <fmt/format.h>
13
14 namespace arm
15 {
16
17 namespace pipe
18 {
19
RegisterCategory(const std::string & categoryName)20 const Category* CounterDirectory::RegisterCategory(const std::string& categoryName)
21 {
22 // Check that the given category name is valid
23 if (categoryName.empty() ||
24 !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceNameCharPolicy>(categoryName))
25 {
26 throw arm::pipe::InvalidArgumentException("Trying to register a category with an invalid name");
27 }
28
29 // Check that the given category is not already registered
30 if (IsCategoryRegistered(categoryName))
31 {
32 throw arm::pipe::InvalidArgumentException(fmt::format(
33 "Trying to register a category already registered (\"{}\")",
34 categoryName));
35 }
36
37 // Create the category
38 CategoryPtr category = std::make_unique<Category>(categoryName);
39 ARM_PIPE_ASSERT(category);
40
41 // Get the raw category pointer
42 const Category* categoryPtr = category.get();
43 ARM_PIPE_ASSERT(categoryPtr);
44
45 // Register the category
46 m_Categories.insert(std::move(category));
47
48 return categoryPtr;
49 }
50
RegisterDevice(const std::string & deviceName,uint16_t cores,const arm::pipe::Optional<std::string> & parentCategoryName)51 const Device* CounterDirectory::RegisterDevice(const std::string& deviceName,
52 uint16_t cores,
53 const arm::pipe::Optional<std::string>& parentCategoryName)
54 {
55 // Check that the given device name is valid
56 if (deviceName.empty() ||
57 !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceCharPolicy>(deviceName))
58 {
59 throw arm::pipe::InvalidArgumentException("Trying to register a device with an invalid name");
60 }
61
62 // Check that a device with the given name is not already registered
63 if (IsDeviceRegistered(deviceName))
64 {
65 throw arm::pipe::InvalidArgumentException(fmt::format(
66 "Trying to register a device already registered (\"{}\")",
67 deviceName));
68 }
69
70 // Check that a category with the given (optional) parent category name is already registered
71 if (parentCategoryName.has_value())
72 {
73 // Get the (optional) parent category name
74 const std::string& parentCategoryNameValue = parentCategoryName.value();
75 if (parentCategoryNameValue.empty())
76 {
77 throw arm::pipe::InvalidArgumentException(
78 fmt::format("Trying to connect a device (name: \"{}\") to an invalid "
79 "parent category (name: \"{}\")",
80 deviceName,
81 parentCategoryNameValue));
82 }
83
84 // Check that the given parent category is already registered
85 auto categoryIt = FindCategory(parentCategoryNameValue);
86 if (categoryIt == m_Categories.end())
87 {
88 throw arm::pipe::InvalidArgumentException(
89 fmt::format("Trying to connect a device (name: \"{}\") to a parent category that "
90 "is not registered (name: \"{}\")",
91 deviceName,
92 parentCategoryNameValue));
93 }
94 }
95
96 // Get the device UID
97 uint16_t deviceUid = GetNextUid();
98
99 // Create the device
100 DevicePtr device = std::make_unique<Device>(deviceUid, deviceName, cores);
101 ARM_PIPE_ASSERT(device);
102
103 // Get the raw device pointer
104 const Device* devicePtr = device.get();
105 ARM_PIPE_ASSERT(devicePtr);
106
107 // Register the device
108 m_Devices.insert(std::make_pair(deviceUid, std::move(device)));
109
110 return devicePtr;
111 }
112
RegisterCounterSet(const std::string & counterSetName,uint16_t count,const arm::pipe::Optional<std::string> & parentCategoryName)113 const CounterSet* CounterDirectory::RegisterCounterSet(const std::string& counterSetName,
114 uint16_t count,
115 const arm::pipe::Optional<std::string>& parentCategoryName)
116 {
117 // Check that the given counter set name is valid
118 if (counterSetName.empty() ||
119 !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceNameCharPolicy>(counterSetName))
120 {
121 throw arm::pipe::InvalidArgumentException("Trying to register a counter set with an invalid name");
122 }
123
124 // Check that a counter set with the given name is not already registered
125 if (IsCounterSetRegistered(counterSetName))
126 {
127 throw arm::pipe::InvalidArgumentException(
128 fmt::format("Trying to register a counter set already registered (\"{}\")", counterSetName));
129 }
130
131 // Peek the next UID, do not get an actual valid UID just now as we don't want to waste a good UID in case
132 // the registration fails. We'll get a proper one once we're sure that the counter set can be registered
133 uint16_t counterSetUidPeek = GetNextUid(true);
134
135 // Check that a category with the given (optional) parent category name is already registered
136 if (parentCategoryName.has_value())
137 {
138 // Get the (optional) parent category name
139 const std::string& parentCategoryNameValue = parentCategoryName.value();
140 if (parentCategoryNameValue.empty())
141 {
142 throw arm::pipe::InvalidArgumentException(
143 fmt::format("Trying to connect a counter set (UID: {}) to an invalid "
144 "parent category (name: \"{}\")",
145 counterSetUidPeek,
146 parentCategoryNameValue));
147 }
148
149 // Check that the given parent category is already registered
150 auto it = FindCategory(parentCategoryNameValue);
151 if (it == m_Categories.end())
152 {
153 throw arm::pipe::InvalidArgumentException(
154 fmt::format("Trying to connect a counter set (UID: {}) to a parent category "
155 "that is not registered (name: \"{}\")",
156 counterSetUidPeek,
157 parentCategoryNameValue));
158 }
159 }
160
161 // Get the counter set UID
162 uint16_t counterSetUid = GetNextUid();
163 ARM_PIPE_ASSERT(counterSetUid == counterSetUidPeek);
164
165 // Create the counter set
166 CounterSetPtr counterSet = std::make_unique<CounterSet>(counterSetUid, counterSetName, count);
167 ARM_PIPE_ASSERT(counterSet);
168
169 // Get the raw counter set pointer
170 const CounterSet* counterSetPtr = counterSet.get();
171 ARM_PIPE_ASSERT(counterSetPtr);
172
173 // Register the counter set
174 m_CounterSets.insert(std::make_pair(counterSetUid, std::move(counterSet)));
175
176 return counterSetPtr;
177 }
178
RegisterCounter(const std::string & applicationName,const uint16_t uid,const std::string & parentCategoryName,uint16_t counterClass,uint16_t interpolation,double multiplier,const std::string & name,const std::string & description,const arm::pipe::Optional<std::string> & units,const arm::pipe::Optional<uint16_t> & numberOfCores,const arm::pipe::Optional<uint16_t> & deviceUid,const arm::pipe::Optional<uint16_t> & counterSetUid)179 const Counter* CounterDirectory::RegisterCounter(const std::string& applicationName,
180 const uint16_t uid,
181 const std::string& parentCategoryName,
182 uint16_t counterClass,
183 uint16_t interpolation,
184 double multiplier,
185 const std::string& name,
186 const std::string& description,
187 const arm::pipe::Optional<std::string>& units,
188 const arm::pipe::Optional<uint16_t>& numberOfCores,
189 const arm::pipe::Optional<uint16_t>& deviceUid,
190 const arm::pipe::Optional<uint16_t>& counterSetUid)
191 {
192 // Check that the given parent category name is valid
193 if (parentCategoryName.empty() ||
194 !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceNameCharPolicy>(parentCategoryName))
195 {
196 throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid parent category name");
197 }
198
199 // Check that the given class is valid
200 if (counterClass != 0 && counterClass != 1)
201 {
202 throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid class");
203 }
204
205 // Check that the given interpolation is valid
206 if (interpolation != 0 && interpolation != 1)
207 {
208 throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid interpolation");
209 }
210
211 // Check that the given multiplier is valid
212 if (multiplier == .0f)
213 {
214 throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid multiplier");
215 }
216
217 // Check that the given name is valid
218 if (name.empty() ||
219 !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceCharPolicy>(name))
220 {
221 throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid name");
222 }
223
224 // Check that the given description is valid
225 if (description.empty() ||
226 !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceCharPolicy>(description))
227 {
228 throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid description");
229 }
230
231 // Check that the given units are valid
232 if (units.has_value()
233 && !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceNameCharPolicy>(units.value()))
234 {
235 throw arm::pipe::InvalidArgumentException("Trying to register a counter with a invalid units");
236 }
237
238 // Check that the given parent category is registered
239 auto categoryIt = FindCategory(parentCategoryName);
240 if (categoryIt == m_Categories.end())
241 {
242 throw arm::pipe::InvalidArgumentException(
243 fmt::format("Trying to connect a counter to a category that is not registered (name: \"{}\")",
244 parentCategoryName));
245 }
246
247 // Get the parent category
248 const CategoryPtr& parentCategory = *categoryIt;
249 ARM_PIPE_ASSERT(parentCategory);
250
251 // Check that a counter with the given name is not already registered within the parent category
252 const std::vector<uint16_t>& parentCategoryCounters = parentCategory->m_Counters;
253 for (uint16_t parentCategoryCounterUid : parentCategoryCounters)
254 {
255 const Counter* parentCategoryCounter = GetCounter(parentCategoryCounterUid);
256 ARM_PIPE_ASSERT(parentCategoryCounter);
257
258 if (parentCategoryCounter->m_Name == name)
259 {
260 throw arm::pipe::InvalidArgumentException(
261 fmt::format("Trying to register a counter to category \"{}\" with a name that "
262 "is already used within that category (name: \"{}\")",
263 parentCategoryName,
264 name));
265 }
266 }
267
268 // Check that a counter set with the given (optional) UID is already registered
269 uint16_t counterSetUidValue = counterSetUid.has_value() ? counterSetUid.value() : 0;
270 if (counterSetUidValue > 0)
271 {
272 // Check that the (optional) counter set is already registered
273 if (!IsCounterSetRegistered(counterSetUidValue))
274 {
275 throw InvalidArgumentException(
276 fmt::format("Trying to connect a counter to a counter set that is "
277 "not registered (counter set UID: {})",
278 counterSetUidValue));
279 }
280 }
281
282 // Get the number of cores (this call may throw)
283 uint16_t deviceUidValue = deviceUid.has_value() ? deviceUid.value() : 0;
284 uint16_t deviceCores = GetNumberOfCores(numberOfCores, deviceUidValue);
285
286 // Get the counter UIDs and calculate the max counter UID
287 std::vector<uint16_t> counterUids = GetNextCounterUids(uid, deviceCores);
288 ARM_PIPE_ASSERT(!counterUids.empty());
289 uint16_t maxCounterUid = deviceCores <= 1 ? counterUids.front() : counterUids.back();
290
291 // Get the counter units
292 const std::string unitsValue = units.has_value() ? units.value() : "";
293
294 // Create the counter
295 CounterPtr counter = std::make_shared<Counter>(applicationName,
296 counterUids.front(),
297 maxCounterUid,
298 counterClass,
299 interpolation,
300 multiplier,
301 name,
302 description,
303 unitsValue,
304 deviceUidValue,
305 counterSetUidValue);
306 ARM_PIPE_ASSERT(counter);
307
308 // Get the raw counter pointer
309 const Counter* counterPtr = counter.get();
310 ARM_PIPE_ASSERT(counterPtr);
311
312 // Process multiple counters if necessary
313 for (uint16_t counterUid : counterUids)
314 {
315 // Connect the counter to the parent category
316 parentCategory->m_Counters.push_back(counterUid);
317
318 // Register the counter
319 m_Counters.insert(std::make_pair(counterUid, counter));
320 }
321
322 return counterPtr;
323 }
324
GetCategory(const std::string & categoryName) const325 const Category* CounterDirectory::GetCategory(const std::string& categoryName) const
326 {
327 auto it = FindCategory(categoryName);
328 if (it == m_Categories.end())
329 {
330 return nullptr;
331 }
332
333 const Category* category = it->get();
334 ARM_PIPE_ASSERT(category);
335
336 return category;
337 }
338
GetDevice(uint16_t deviceUid) const339 const Device* CounterDirectory::GetDevice(uint16_t deviceUid) const
340 {
341 auto it = FindDevice(deviceUid);
342 if (it == m_Devices.end())
343 {
344 return nullptr;
345 }
346
347 const Device* device = it->second.get();
348 ARM_PIPE_ASSERT(device);
349 ARM_PIPE_ASSERT(device->m_Uid == deviceUid);
350
351 return device;
352 }
353
GetCounterSet(uint16_t counterSetUid) const354 const CounterSet* CounterDirectory::GetCounterSet(uint16_t counterSetUid) const
355 {
356 auto it = FindCounterSet(counterSetUid);
357 if (it == m_CounterSets.end())
358 {
359 return nullptr;
360 }
361
362 const CounterSet* counterSet = it->second.get();
363 ARM_PIPE_ASSERT(counterSet);
364 ARM_PIPE_ASSERT(counterSet->m_Uid == counterSetUid);
365
366 return counterSet;
367 }
368
GetCounter(uint16_t counterUid) const369 const Counter* CounterDirectory::GetCounter(uint16_t counterUid) const
370 {
371 auto it = FindCounter(counterUid);
372 if (it == m_Counters.end())
373 {
374 return nullptr;
375 }
376
377 const Counter* counter = it->second.get();
378 ARM_PIPE_ASSERT(counter);
379 ARM_PIPE_ASSERT(counter->m_Uid <= counterUid);
380 ARM_PIPE_ASSERT(counter->m_Uid <= counter->m_MaxCounterUid);
381
382 return counter;
383 }
384
IsCategoryRegistered(const std::string & categoryName) const385 bool CounterDirectory::IsCategoryRegistered(const std::string& categoryName) const
386 {
387 auto it = FindCategory(categoryName);
388
389 return it != m_Categories.end();
390 }
391
IsDeviceRegistered(uint16_t deviceUid) const392 bool CounterDirectory::IsDeviceRegistered(uint16_t deviceUid) const
393 {
394 auto it = FindDevice(deviceUid);
395
396 return it != m_Devices.end();
397 }
398
IsDeviceRegistered(const std::string & deviceName) const399 bool CounterDirectory::IsDeviceRegistered(const std::string& deviceName) const
400 {
401 auto it = FindDevice(deviceName);
402
403 return it != m_Devices.end();
404 }
405
IsCounterSetRegistered(uint16_t counterSetUid) const406 bool CounterDirectory::IsCounterSetRegistered(uint16_t counterSetUid) const
407 {
408 auto it = FindCounterSet(counterSetUid);
409
410 return it != m_CounterSets.end();
411 }
412
IsCounterSetRegistered(const std::string & counterSetName) const413 bool CounterDirectory::IsCounterSetRegistered(const std::string& counterSetName) const
414 {
415 auto it = FindCounterSet(counterSetName);
416
417 return it != m_CounterSets.end();
418 }
419
IsCounterRegistered(uint16_t counterUid) const420 bool CounterDirectory::IsCounterRegistered(uint16_t counterUid) const
421 {
422 auto it = FindCounter(counterUid);
423
424 return it != m_Counters.end();
425 }
426
IsCounterRegistered(const std::string & counterName) const427 bool CounterDirectory::IsCounterRegistered(const std::string& counterName) const
428 {
429 auto it = FindCounter(counterName);
430
431 return it != m_Counters.end();
432 }
433
Clear()434 void CounterDirectory::Clear()
435 {
436 // Clear all the counter directory contents
437 m_Categories.clear();
438 m_Devices.clear();
439 m_CounterSets.clear();
440 m_Counters.clear();
441 }
442
FindCategory(const std::string & categoryName) const443 CategoriesIt CounterDirectory::FindCategory(const std::string& categoryName) const
444 {
445 return std::find_if(m_Categories.begin(), m_Categories.end(), [&categoryName](const CategoryPtr& category)
446 {
447 ARM_PIPE_ASSERT(category);
448
449 return category->m_Name == categoryName;
450 });
451 }
452
FindDevice(uint16_t deviceUid) const453 DevicesIt CounterDirectory::FindDevice(uint16_t deviceUid) const
454 {
455 return m_Devices.find(deviceUid);
456 }
457
FindDevice(const std::string & deviceName) const458 DevicesIt CounterDirectory::FindDevice(const std::string& deviceName) const
459 {
460 return std::find_if(m_Devices.begin(), m_Devices.end(), [&deviceName](const auto& pair)
461 {
462 ARM_PIPE_ASSERT(pair.second);
463 ARM_PIPE_ASSERT(pair.second->m_Uid == pair.first);
464
465 return pair.second->m_Name == deviceName;
466 });
467 }
468
FindCounterSet(uint16_t counterSetUid) const469 CounterSetsIt CounterDirectory::FindCounterSet(uint16_t counterSetUid) const
470 {
471 return m_CounterSets.find(counterSetUid);
472 }
473
FindCounterSet(const std::string & counterSetName) const474 CounterSetsIt CounterDirectory::FindCounterSet(const std::string& counterSetName) const
475 {
476 return std::find_if(m_CounterSets.begin(), m_CounterSets.end(), [&counterSetName](const auto& pair)
477 {
478 ARM_PIPE_ASSERT(pair.second);
479 ARM_PIPE_ASSERT(pair.second->m_Uid == pair.first);
480
481 return pair.second->m_Name == counterSetName;
482 });
483 }
484
FindCounter(uint16_t counterUid) const485 CountersIt CounterDirectory::FindCounter(uint16_t counterUid) const
486 {
487 return m_Counters.find(counterUid);
488 }
489
FindCounter(const std::string & counterName) const490 CountersIt CounterDirectory::FindCounter(const std::string& counterName) const
491 {
492 return std::find_if(m_Counters.begin(), m_Counters.end(), [&counterName](const auto& pair)
493 {
494 ARM_PIPE_ASSERT(pair.second);
495 ARM_PIPE_ASSERT(pair.first >= pair.second->m_Uid && pair.first <= pair.second->m_MaxCounterUid);
496
497 return pair.second->m_Name == counterName;
498 });
499 }
500
GetNumberOfCores(const arm::pipe::Optional<uint16_t> & numberOfCores,uint16_t deviceUid)501 uint16_t CounterDirectory::GetNumberOfCores(const arm::pipe::Optional<uint16_t>& numberOfCores,
502 uint16_t deviceUid)
503 {
504 // To get the number of cores, apply the following rules:
505 //
506 // 1. If numberOfCores is set then take it as the deviceCores value
507 // 2. If numberOfCores is not set then check to see if this counter is directly associated with a device,
508 // if so then that devices number of cores is taken as the deviceCores value
509 // 3. If none of the above holds then set deviceCores to zero
510
511 // 1. If numberOfCores is set then take it as the deviceCores value
512 if (numberOfCores.has_value())
513 {
514 // Get the number of cores
515 return numberOfCores.value();
516 }
517
518 // 2. If numberOfCores is not set then check to see if this counter is directly associated with a device,
519 // if so then that devices number of cores is taken as the deviceCores value
520 if (deviceUid > 0)
521 {
522 // Check that the (optional) device is already registered
523 auto deviceIt = FindDevice(deviceUid);
524 if (deviceIt == m_Devices.end())
525 {
526 throw arm::pipe::InvalidArgumentException(
527 fmt::format("Trying to connect a counter to a device that is not registered (device UID {})",
528 deviceUid));
529 }
530
531 // Get the associated device
532 const DevicePtr& device = deviceIt->second;
533 ARM_PIPE_ASSERT(device);
534
535
536 // Get the number of cores of the associated device
537 return device->m_Cores;
538 }
539
540 // 3. If none of the above holds then set deviceCores to zero
541 return 0;
542 }
543
544 } // namespace pipe
545
546 } // namespace arm
547