• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "SendCounterPacket.hpp"
7 #include <common/include/EncodeVersion.hpp>
8 
9 #include <armnn/Exceptions.hpp>
10 #include <armnn/Conversion.hpp>
11 #include <Processes.hpp>
12 #include <armnn/utility/Assert.hpp>
13 #include <armnn/utility/NumericCast.hpp>
14 #include <common/include/Constants.hpp>
15 #include <common/include/SwTrace.hpp>
16 
17 #include <fmt/format.h>
18 
19 #include <cstring>
20 
21 namespace armnn
22 {
23 
24 namespace profiling
25 {
26 
SendStreamMetaDataPacket()27 void SendCounterPacket::SendStreamMetaDataPacket()
28 {
29     const std::string info(GetSoftwareInfo());
30     const std::string hardwareVersion(GetHardwareVersion());
31     const std::string softwareVersion(GetSoftwareVersion());
32     const std::string processName = GetProcessName().substr(0, 60);
33 
34     const uint32_t infoSize =            armnn::numeric_cast<uint32_t>(info.size()) + 1;
35     const uint32_t hardwareVersionSize = armnn::numeric_cast<uint32_t>(hardwareVersion.size()) + 1;
36     const uint32_t softwareVersionSize = armnn::numeric_cast<uint32_t>(softwareVersion.size()) + 1;
37     const uint32_t processNameSize =     armnn::numeric_cast<uint32_t>(processName.size()) + 1;
38 
39     const uint32_t sizeUint32 = sizeof(uint32_t);
40 
41     const uint32_t headerSize = 2 * sizeUint32;
42     const uint32_t bodySize = 10 * sizeUint32;
43     const uint32_t packetVersionCountSize = sizeUint32;
44 
45     // Supported Packets
46     // Packet Encoding version 1.0.0
47     // Control packet family
48     //   Stream metadata packet (packet family=0; packet id=0)
49     //   Connection Acknowledged packet ( packet family=0, packet id=1) Version 1.0.0
50     //   Counter Directory packet (packet family=0; packet id=2) Version 1.0.0
51     //   Request Counter Directory packet ( packet family=0, packet id=3) Version 1.0.0
52     //   Periodic Counter Selection packet ( packet family=0, packet id=4) Version 1.0.0
53     //   Per Job Counter Selection packet ( packet family=0, packet id=5) Version 1.0.0
54     //   Activate Timeline Reporting (packet family = 0, packet id = 6) Version 1.0.0
55     //   Deactivate Timeline Reporting (packet family = 0, packet id = 7) Version 1.0.0
56     // Counter Packet Family
57     //   Periodic Counter Capture (packet_family = 3, packet_class = 0, packet_type = 0) Version 1.0.0
58     //   Per-Job Counter Capture (packet_family = 3, packet_class = 1, packet_type = 0,1) Version  1.0.0
59     // Timeline Packet Family
60     //   Timeline Message Directory (packet_family = 1, packet_class = 0, packet_type = 0) Version 1.0.0
61     //   Timeline Message (packet_family = 1, packet_class = 0, packet_type = 1) Version 1.0.0
62     std::vector<std::pair<uint32_t, uint32_t>> packetVersions;
63     packetVersions.push_back(std::make_pair(ConstructHeader(0, 0), arm::pipe::EncodeVersion(1, 0, 0)));
64     packetVersions.push_back(std::make_pair(ConstructHeader(0, 1), arm::pipe::EncodeVersion(1, 0, 0)));
65     packetVersions.push_back(std::make_pair(ConstructHeader(0, 2), arm::pipe::EncodeVersion(1, 0, 0)));
66     packetVersions.push_back(std::make_pair(ConstructHeader(0, 3), arm::pipe::EncodeVersion(1, 0, 0)));
67     packetVersions.push_back(std::make_pair(ConstructHeader(0, 4), arm::pipe::EncodeVersion(1, 0, 0)));
68     packetVersions.push_back(std::make_pair(ConstructHeader(0, 5), arm::pipe::EncodeVersion(1, 0, 0)));
69     packetVersions.push_back(std::make_pair(ConstructHeader(0, 6), arm::pipe::EncodeVersion(1, 0, 0)));
70     packetVersions.push_back(std::make_pair(ConstructHeader(0, 7), arm::pipe::EncodeVersion(1, 0, 0)));
71     packetVersions.push_back(std::make_pair(ConstructHeader(3, 0, 0), arm::pipe::EncodeVersion(1, 0, 0)));
72     packetVersions.push_back(std::make_pair(ConstructHeader(3, 1, 0), arm::pipe::EncodeVersion(1, 0, 0)));
73     packetVersions.push_back(std::make_pair(ConstructHeader(3, 1, 1), arm::pipe::EncodeVersion(1, 0, 0)));
74     packetVersions.push_back(std::make_pair(ConstructHeader(1, 0, 0), arm::pipe::EncodeVersion(1, 0, 0)));
75     packetVersions.push_back(std::make_pair(ConstructHeader(1, 0, 1), arm::pipe::EncodeVersion(1, 0, 0)));
76     uint32_t numberOfVersions = armnn::numeric_cast<uint32_t>(packetVersions.size());
77     uint32_t packetVersionSize = armnn::numeric_cast<uint32_t>(numberOfVersions * 2 * sizeUint32);
78 
79     const uint32_t payloadSize = armnn::numeric_cast<uint32_t>(infoSize + hardwareVersionSize +
80                                                                softwareVersionSize + processNameSize +
81                                                                packetVersionCountSize + packetVersionSize);
82 
83     const uint32_t totalSize = headerSize + bodySize + payloadSize;
84     uint32_t offset = 0;
85     uint32_t reserved = 0;
86 
87     IPacketBufferPtr writeBuffer = m_BufferManager.Reserve(totalSize, reserved);
88 
89     if (writeBuffer == nullptr || reserved < totalSize)
90     {
91         CancelOperationAndThrow<BufferExhaustion>(
92             writeBuffer,
93             fmt::format("No space left in buffer. Unable to reserve ({}) bytes.", totalSize));
94     }
95 
96     try
97     {
98         // Create header
99 
100         WriteUint32(writeBuffer, offset, 0);
101         offset += sizeUint32;
102         WriteUint32(writeBuffer, offset, totalSize - headerSize);
103 
104         // Packet body
105 
106         offset += sizeUint32;
107         WriteUint32(writeBuffer, offset, arm::pipe::PIPE_MAGIC); // pipe_magic
108         offset += sizeUint32;
109         WriteUint32(writeBuffer, offset, arm::pipe::EncodeVersion(1, 0, 0)); // stream_metadata_version
110         offset += sizeUint32;
111         WriteUint32(writeBuffer, offset, MAX_METADATA_PACKET_LENGTH); // max_data_length
112         offset += sizeUint32;
113         int pid = armnnUtils::Processes::GetCurrentId();
114         WriteUint32(writeBuffer, offset, armnn::numeric_cast<uint32_t>(pid)); // pid
115         offset += sizeUint32;
116         uint32_t poolOffset = bodySize;
117         WriteUint32(writeBuffer, offset, poolOffset); // offset_info
118         offset += sizeUint32;
119         poolOffset += infoSize;
120         WriteUint32(writeBuffer, offset, poolOffset); // offset_hw_version
121         offset += sizeUint32;
122         poolOffset += hardwareVersionSize;
123         WriteUint32(writeBuffer, offset, poolOffset); // offset_sw_version
124         offset += sizeUint32;
125         poolOffset += softwareVersionSize;
126         WriteUint32(writeBuffer, offset, poolOffset); // offset_process_name
127         offset += sizeUint32;
128         poolOffset += processNameSize;
129         WriteUint32(writeBuffer, offset, poolOffset); // offset_packet_version_table
130         offset += sizeUint32;
131         WriteUint32(writeBuffer, offset, 0); // reserved
132         offset += sizeUint32;
133 
134         // Pool
135 
136         if (infoSize)
137         {
138             memcpy(&writeBuffer->GetWritableData()[offset], info.c_str(), infoSize);
139             offset += infoSize;
140         }
141 
142         memcpy(&writeBuffer->GetWritableData()[offset], hardwareVersion.c_str(), hardwareVersionSize);
143         offset += hardwareVersionSize;
144         memcpy(&writeBuffer->GetWritableData()[offset], softwareVersion.c_str(), softwareVersionSize);
145         offset += softwareVersionSize;
146         memcpy(&writeBuffer->GetWritableData()[offset], processName.c_str(), processNameSize);
147         offset += processNameSize;
148 
149         if (!packetVersions.empty())
150         {
151             // Packet Version Count
152             WriteUint32(writeBuffer, offset, numberOfVersions << 16);
153             offset += sizeUint32;
154 
155             // Packet Version Entries
156             for (std::pair<uint32_t, uint32_t>& packetVersion : packetVersions)
157             {
158                 WriteUint32(writeBuffer, offset, packetVersion.first);
159                 offset += sizeUint32;
160                 WriteUint32(writeBuffer, offset, packetVersion.second);
161                 offset += sizeUint32;
162             }
163         }
164     }
165     catch(...)
166     {
167         CancelOperationAndThrow<RuntimeException>(writeBuffer, "Error processing packet.");
168     }
169 
170     m_BufferManager.Commit(writeBuffer, totalSize, false);
171 }
172 
CreateCategoryRecord(const CategoryPtr & category,const Counters & counters,CategoryRecord & categoryRecord,std::string & errorMessage)173 bool SendCounterPacket::CreateCategoryRecord(const CategoryPtr& category,
174                                              const Counters& counters,
175                                              CategoryRecord& categoryRecord,
176                                              std::string& errorMessage)
177 {
178     ARMNN_ASSERT(category);
179 
180     const std::string& categoryName = category->m_Name;
181     ARMNN_ASSERT(!categoryName.empty());
182 
183     // Remove any duplicate counters
184     std::vector<uint16_t> categoryCounters;
185     for (size_t counterIndex = 0; counterIndex < category->m_Counters.size(); ++counterIndex)
186     {
187         uint16_t counterUid = category->m_Counters.at(counterIndex);
188         auto it = counters.find(counterUid);
189         if (it == counters.end())
190         {
191             errorMessage = fmt::format("Counter ({}) not found in category ({})",
192                                        counterUid,
193                                        category->m_Name );
194             return false;
195         }
196 
197         const CounterPtr& counter = it->second;
198 
199         if (counterUid == counter->m_MaxCounterUid)
200         {
201             categoryCounters.emplace_back(counterUid);
202         }
203     }
204     if (categoryCounters.empty())
205     {
206         errorMessage = fmt::format("No valid counters found in category ({})", categoryName);
207         return false;
208     }
209 
210     // Utils
211     const size_t uint32_t_size = sizeof(uint32_t);
212 
213     // Convert the device name into a SWTrace namestring
214     std::vector<uint32_t> categoryNameBuffer;
215     if (!arm::pipe::StringToSwTraceString<arm::pipe::SwTraceNameCharPolicy>(categoryName, categoryNameBuffer))
216     {
217         errorMessage = fmt::format("Cannot convert the name of category ({}) to an SWTrace namestring",
218                                    categoryName);
219         return false;
220     }
221 
222     // Category record word 1:
223     // 16:31 [16] event_count: number of events belonging to this category
224     // 0:15  [16] reserved: all zeros
225     const uint32_t categoryRecordWord1 = static_cast<uint32_t>(categoryCounters.size()) << 16;
226 
227     // Category record word 2:
228     // 0:31 [32] event_pointer_table_offset: offset from the beginning of the category data pool to
229     //                                       the event_pointer_table
230     const uint32_t categoryRecordWord2 = static_cast<uint32_t>(3u * uint32_t_size);
231 
232     // Process the event records
233     const size_t counterCount = categoryCounters.size();
234     std::vector<EventRecord> eventRecords(counterCount);
235     std::vector<uint32_t> eventRecordOffsets(counterCount, 0);
236     size_t eventRecordsSize = 0;
237     uint32_t eventRecordsOffset = armnn::numeric_cast<uint32_t>(
238                     (eventRecords.size() + categoryNameBuffer.size()) * uint32_t_size);
239     for (size_t counterIndex = 0, eventRecordIndex = 0, eventRecordOffsetIndex = 0;
240          counterIndex < counterCount;
241          counterIndex++, eventRecordIndex++, eventRecordOffsetIndex++)
242     {
243         uint16_t counterUid = categoryCounters.at(counterIndex);
244         auto it = counters.find(counterUid);
245         const CounterPtr& counter = it->second;
246 
247         EventRecord& eventRecord = eventRecords.at(eventRecordIndex);
248         if (!CreateEventRecord(counter, eventRecord, errorMessage))
249         {
250             return false;
251         }
252 
253         // Update the total size in words of the event records
254         eventRecordsSize += eventRecord.size();
255 
256         // Add the event record offset to the event pointer table offset field
257         eventRecordOffsets[eventRecordOffsetIndex] = eventRecordsOffset;
258         eventRecordsOffset += armnn::numeric_cast<uint32_t>(eventRecord.size() * uint32_t_size);
259     }
260 
261     // Category record word 3:
262     // 0:31 [32] name_offset (offset from the beginning of the category data pool to the name field)
263     const uint32_t categoryRecordWord3 = armnn::numeric_cast<uint32_t>(
264             (3u + eventRecordOffsets.size()) * uint32_t_size);
265 
266     // Calculate the size in words of the category record
267     const size_t categoryRecordSize = 3u +// The size of the fixed part (device + counter_set + event_count +
268                                           // reserved + event_pointer_table_offset + name_offset)
269                                       eventRecordOffsets.size() + // The size of the variable part (
270                                       categoryNameBuffer.size() + // the event pointer table + the category name
271                                       eventRecordsSize;           // including the null-terminator + the event records)
272 
273     // Allocate the necessary space for the category record
274     categoryRecord.resize(categoryRecordSize);
275 
276     ARMNN_NO_CONVERSION_WARN_BEGIN
277     // Create the category record
278     categoryRecord[0] = categoryRecordWord1; // event_count + reserved
279     categoryRecord[1] = categoryRecordWord2; // event_pointer_table_offset
280     categoryRecord[2] = categoryRecordWord3; // name_offset
281     auto offset = categoryRecord.begin() + 3u;
282     std::copy(eventRecordOffsets.begin(), eventRecordOffsets.end(), offset); // event_pointer_table
283     offset += eventRecordOffsets.size();
284     std::copy(categoryNameBuffer.begin(), categoryNameBuffer.end(), offset); // name
285     offset += categoryNameBuffer.size();
286     for (const EventRecord& eventRecord : eventRecords)
287     {
288         std::copy(eventRecord.begin(), eventRecord.end(), offset); // event_record
289         offset += eventRecord.size();
290     }
291     ARMNN_NO_CONVERSION_WARN_END
292 
293     return true;
294 }
295 
CreateDeviceRecord(const DevicePtr & device,DeviceRecord & deviceRecord,std::string & errorMessage)296 bool SendCounterPacket::CreateDeviceRecord(const DevicePtr& device,
297                                            DeviceRecord& deviceRecord,
298                                            std::string& errorMessage)
299 {
300     ARMNN_ASSERT(device);
301 
302     uint16_t deviceUid = device->m_Uid;
303     const std::string& deviceName = device->m_Name;
304     uint16_t deviceCores = device->m_Cores;
305 
306     ARMNN_ASSERT(!deviceName.empty());
307 
308     // Device record word 0:
309     // 16:31 [16] uid: the unique identifier for the device
310     // 0:15  [16] cores: the number of individual streams of counters for one or more cores of some device
311     const uint32_t deviceRecordWord0 = (static_cast<uint32_t>(deviceUid) << 16) |
312                                  (static_cast<uint32_t>(deviceCores));
313 
314     // Device record word 1:
315     // 0:31 [32] name_offset: offset from the beginning of the device record pool to the name field
316     const uint32_t deviceRecordWord1 = 8u; // The offset is always eight here, as the name field is always
317                                            // the first (and only) item in the pool and there are two device words
318 
319     // Convert the device name into a SWTrace string
320     std::vector<uint32_t> deviceNameBuffer;
321     if (!arm::pipe::StringToSwTraceString<arm::pipe::SwTraceCharPolicy>(deviceName, deviceNameBuffer))
322     {
323         errorMessage = fmt::format("Cannot convert the name of device {} ({}) to an SWTrace string",
324                                    deviceUid,
325                                    deviceName);
326         return false;
327     }
328 
329     // Calculate the size in words of the device record
330     const size_t deviceRecordSize = 2u + // The size of the fixed part (uid + cores + name_offset)
331                               deviceNameBuffer.size(); // The size of the variable part (the device name including
332                                                        // the null-terminator)
333 
334     // Allocate the necessary space for the device record
335     deviceRecord.resize(deviceRecordSize);
336 
337     // Create the device record
338     deviceRecord[0] = deviceRecordWord0; // uid + core
339     deviceRecord[1] = deviceRecordWord1; // name_offset
340     auto offset = deviceRecord.begin() + 2u;
341     std::copy(deviceNameBuffer.begin(), deviceNameBuffer.end(), offset); // name
342 
343     return true;
344 }
345 
CreateCounterSetRecord(const CounterSetPtr & counterSet,CounterSetRecord & counterSetRecord,std::string & errorMessage)346 bool SendCounterPacket::CreateCounterSetRecord(const CounterSetPtr& counterSet,
347                                                CounterSetRecord& counterSetRecord,
348                                                std::string& errorMessage)
349 {
350     ARMNN_ASSERT(counterSet);
351 
352     uint16_t counterSetUid = counterSet->m_Uid;
353     const std::string& counterSetName = counterSet->m_Name;
354     uint16_t counterSetCount = counterSet->m_Count;
355 
356     ARMNN_ASSERT(!counterSetName.empty());
357 
358     // Counter set record word 0:
359     // 16:31 [16] uid: the unique identifier for the counter_set
360     // 0:15  [16] count: the number of counters which can be active in this set at any one time
361     const uint32_t counterSetRecordWord0 = (static_cast<uint32_t>(counterSetUid) << 16) |
362                                            (static_cast<uint32_t>(counterSetCount));
363 
364     // Counter set record word 1:
365     // 0:31 [32] name_offset: offset from the beginning of the counter set pool to the name field
366     const uint32_t counterSetRecordWord1 = 8u; // The offset is always eight here, as the name field is always
367                                                // the first (and only) item in the pool after the two counter set words
368 
369     // Convert the device name into a SWTrace namestring
370     std::vector<uint32_t> counterSetNameBuffer;
371     if (!arm::pipe::StringToSwTraceString<arm::pipe::SwTraceNameCharPolicy>(counterSet->m_Name, counterSetNameBuffer))
372     {
373         errorMessage = fmt::format("Cannot convert the name of counter set {} ({}) to an SWTrace namestring",
374                                    counterSetUid,
375                                    counterSetName);
376         return false;
377     }
378 
379     // Calculate the size in words of the counter set record
380     const size_t counterSetRecordSize = 2u + // The size of the fixed part (uid + cores + name_offset)
381                                         counterSetNameBuffer.size(); // The size of the variable part (the counter set
382                                                                      // name including the null-terminator)
383 
384     // Allocate the space for the counter set record
385     counterSetRecord.resize(counterSetRecordSize);
386 
387     // Create the counter set record
388     counterSetRecord[0] = counterSetRecordWord0; // uid + core
389     counterSetRecord[1] = counterSetRecordWord1; // name_offset
390     auto offset = counterSetRecord.begin() + 2u;
391     std::copy(counterSetNameBuffer.begin(), counterSetNameBuffer.end(), offset); // name
392 
393     return true;
394 }
395 
CreateEventRecord(const CounterPtr & counter,EventRecord & eventRecord,std::string & errorMessage)396 bool SendCounterPacket::CreateEventRecord(const CounterPtr& counter,
397                                           EventRecord& eventRecord,
398                                           std::string& errorMessage)
399 {
400     ARMNN_ASSERT(counter);
401 
402     uint16_t           counterUid           = counter->m_Uid;
403     uint16_t           maxCounterUid        = counter->m_MaxCounterUid;
404     uint16_t           deviceUid            = counter->m_DeviceUid;
405     uint16_t           counterSetUid        = counter->m_CounterSetUid;
406     uint16_t           counterClass         = counter->m_Class;
407     uint16_t           counterInterpolation = counter->m_Interpolation;
408     double             counterMultiplier    = counter->m_Multiplier;
409     const std::string& counterName          = counter->m_Name;
410     const std::string& counterDescription   = counter->m_Description;
411     const std::string& counterUnits         = counter->m_Units;
412 
413     ARMNN_ASSERT(counterClass == 0 || counterClass == 1);
414     ARMNN_ASSERT(counterInterpolation == 0 || counterInterpolation == 1);
415     ARMNN_ASSERT(counterMultiplier);
416 
417     // Utils
418     const size_t uint32_t_size = sizeof(uint32_t);
419     // eventRecordBlockSize is the size of the fixed part
420     // (counter_uid + max_counter_uid + device +
421     // counter_set + class + interpolation +
422     // multiplier + name_offset + description_offset +
423     // units_offset)
424     const size_t eventRecordBlockSize = 8u;
425 
426     // Event record word 0:
427     // 16:31 [16] max_counter_uid: if the device this event is associated with has more than one core and there
428     //                             is one of these counters per core this value will be set to
429     //                             (counter_uid + cores (from device_record)) - 1.
430     //                             If there is only a single core then this value will be the same as
431     //                             the counter_uid value
432     // 0:15  [16] count_uid: unique ID for the counter. Must be unique across all counters in all categories
433     const uint32_t eventRecordWord0 = (static_cast<uint32_t>(maxCounterUid) << 16) |
434                                       (static_cast<uint32_t>(counterUid));
435 
436     // Event record word 1:
437     // 16:31 [16] device: UID of the device this event is associated with. Set to zero if the event is NOT
438     //                    associated with a device
439     // 0:15  [16] counter_set: UID of the counter_set this event is associated with. Set to zero if the event
440     //                         is NOT associated with a counter_set
441     const uint32_t eventRecordWord1 = (static_cast<uint32_t>(deviceUid) << 16) |
442                                       (static_cast<uint32_t>(counterSetUid));
443 
444     // Event record word 2:
445     // 16:31 [16] class: type describing how to treat each data point in a stream of data points
446     // 0:15  [16] interpolation: type describing how to interpolate each data point in a stream of data points
447     const uint32_t eventRecordWord2 = (static_cast<uint32_t>(counterClass) << 16) |
448                                       (static_cast<uint32_t>(counterInterpolation));
449 
450     // Event record word 3-4:
451     // 0:63 [64] multiplier: internal data stream is represented as integer values, this allows scaling of
452     //                       those values as if they are fixed point numbers. Zero is not a valid value
453     uint32_t multiplier[2] = { 0u, 0u };
454     ARMNN_ASSERT(sizeof(counterMultiplier) == sizeof(multiplier));
455     std::memcpy(multiplier, &counterMultiplier, sizeof(multiplier));
456     const uint32_t eventRecordWord3 = multiplier[0];
457     const uint32_t eventRecordWord4 = multiplier[1];
458 
459     // Event record word 5:
460     // 0:31 [32] name_offset: offset from the beginning of the event record pool to the name field
461     const uint32_t eventRecordWord5 = static_cast<uint32_t>(eventRecordBlockSize * uint32_t_size);
462 
463     // Convert the counter name into a SWTrace string
464     std::vector<uint32_t> counterNameBuffer;
465     if (!arm::pipe::StringToSwTraceString<arm::pipe::SwTraceCharPolicy>(counterName, counterNameBuffer))
466     {
467         errorMessage = fmt::format("Cannot convert the name of counter {} (name: {}) to an SWTrace string",
468                                    counterUid,
469                                    counterName);
470         return false;
471     }
472 
473     // Event record word 6:
474     // 0:31 [32] description_offset: offset from the beginning of the event record pool to the description field
475     // The size of the name buffer in bytes
476     uint32_t eventRecordWord6 =
477             static_cast<uint32_t>((counterNameBuffer.size() + eventRecordBlockSize) * uint32_t_size);
478 
479     // Convert the counter description into a SWTrace string
480     std::vector<uint32_t> counterDescriptionBuffer;
481     if (!arm::pipe::StringToSwTraceString<arm::pipe::SwTraceCharPolicy>(counterDescription, counterDescriptionBuffer))
482     {
483         errorMessage = fmt::format("Cannot convert the description of counter {} (description: {}) "
484                                    "to an SWTrace string",
485                                    counterUid,
486                                    counterName);
487         return false;
488     }
489 
490     // Event record word 7:
491     // 0:31 [32] units_offset: (optional) offset from the beginning of the event record pool to the units field.
492     //                         An offset value of zero indicates this field is not provided
493     bool includeUnits = !counterUnits.empty();
494     // The size of the description buffer in bytes
495     const uint32_t eventRecordWord7 = includeUnits ?
496                                 eventRecordWord6 +
497                                 armnn::numeric_cast<uint32_t>(counterDescriptionBuffer.size()
498                                 * uint32_t_size) :
499                                 0;
500 
501     // Convert the counter units into a SWTrace namestring (optional)
502     std::vector<uint32_t> counterUnitsBuffer;
503     if (includeUnits)
504     {
505         // Convert the counter units into a SWTrace namestring
506         if (!arm::pipe::StringToSwTraceString<arm::pipe::SwTraceNameCharPolicy>(counterUnits, counterUnitsBuffer))
507         {
508             errorMessage = fmt::format("Cannot convert the units of counter {} (units: {}) to an SWTrace string",
509                                        counterUid,
510                                        counterName);
511             return false;
512         }
513     }
514 
515     // Calculate the size in words of the event record
516     const size_t eventRecordSize = eventRecordBlockSize +
517                                    counterNameBuffer.size() +        // The size of the variable part (the counter name,
518                                    counterDescriptionBuffer.size() + // description and units
519                                    counterUnitsBuffer.size();        // including the null-terminator)
520 
521     // Allocate the space for the event record
522     eventRecord.resize(eventRecordSize);
523 
524     ARMNN_NO_CONVERSION_WARN_BEGIN
525     // Create the event record
526     eventRecord[0] = eventRecordWord0; // max_counter_uid + counter_uid
527     eventRecord[1] = eventRecordWord1; // device + counter_set
528     eventRecord[2] = eventRecordWord2; // class + interpolation
529     eventRecord[3] = eventRecordWord3; // multiplier
530     eventRecord[4] = eventRecordWord4; // multiplier
531     eventRecord[5] = eventRecordWord5; // name_offset
532     eventRecord[6] = eventRecordWord6; // description_offset
533     eventRecord[7] = eventRecordWord7; // units_offset
534     auto offset = eventRecord.begin() + 8u;
535     std::copy(counterNameBuffer.begin(), counterNameBuffer.end(), offset); // name
536     offset += counterNameBuffer.size();
537     std::copy(counterDescriptionBuffer.begin(), counterDescriptionBuffer.end(), offset); // description
538     if (includeUnits)
539     {
540         offset += counterDescriptionBuffer.size();
541         std::copy(counterUnitsBuffer.begin(), counterUnitsBuffer.end(), offset); // units
542     }
543     ARMNN_NO_CONVERSION_WARN_END
544 
545     return true;
546 }
547 
SendCounterDirectoryPacket(const ICounterDirectory & counterDirectory)548 void SendCounterPacket::SendCounterDirectoryPacket(const ICounterDirectory& counterDirectory)
549 {
550     // Get the amount of data that needs to be put into the packet
551     const uint16_t categoryCount    = counterDirectory.GetCategoryCount();
552     const uint16_t deviceCount      = counterDirectory.GetDeviceCount();
553     const uint16_t counterSetCount  = counterDirectory.GetCounterSetCount();
554 
555     // Utils
556     const size_t uint32_t_size = sizeof(uint32_t);
557     const size_t packetHeaderSize = 2u;
558     const size_t bodyHeaderSize = 6u;
559     const uint32_t bodyHeaderSizeBytes = bodyHeaderSize * uint32_t_size;
560 
561     // Initialize the offset for the pointer tables
562     uint32_t pointerTableOffset = 0;
563 
564     // --------------
565     // Device records
566     // --------------
567 
568     // Process device records
569     std::vector<DeviceRecord> deviceRecords(deviceCount);
570     const Devices& devices = counterDirectory.GetDevices();
571     std::vector<uint32_t> deviceRecordOffsets(deviceCount, 0); // device_records_pointer_table
572     size_t deviceRecordsSize = 0;
573     size_t deviceIndex = 0;
574     size_t deviceRecordOffsetIndex = 0;
575 
576     pointerTableOffset = armnn::numeric_cast<uint32_t>(deviceCount * uint32_t_size +
577                                                        counterSetCount * uint32_t_size +
578                                                        categoryCount   * uint32_t_size);
579     for (auto it = devices.begin(); it != devices.end(); it++)
580     {
581         const DevicePtr& device = it->second;
582         DeviceRecord& deviceRecord = deviceRecords.at(deviceIndex);
583 
584         std::string errorMessage;
585         if (!CreateDeviceRecord(device, deviceRecord, errorMessage))
586         {
587             CancelOperationAndThrow<RuntimeException>(errorMessage);
588         }
589 
590         // Update the total size in words of the device records
591         deviceRecordsSize += deviceRecord.size();
592 
593         // Add the device record offset to the device records pointer table offset field
594         deviceRecordOffsets[deviceRecordOffsetIndex] = pointerTableOffset;
595         pointerTableOffset += armnn::numeric_cast<uint32_t>(deviceRecord.size() * uint32_t_size);
596 
597         deviceIndex++;
598         deviceRecordOffsetIndex++;
599     }
600 
601     // -------------------
602     // Counter set records
603     // -------------------
604 
605     // Process counter set records
606     std::vector<CounterSetRecord> counterSetRecords(counterSetCount);
607     const CounterSets& counterSets = counterDirectory.GetCounterSets();
608     std::vector<uint32_t> counterSetRecordOffsets(counterSetCount, 0); // counter_set_records_pointer_table
609     size_t counterSetRecordsSize = 0;
610     size_t counterSetIndex = 0;
611     size_t counterSetRecordOffsetIndex = 0;
612 
613     pointerTableOffset -= armnn::numeric_cast<uint32_t>(deviceCount * uint32_t_size);
614     for (auto it = counterSets.begin(); it != counterSets.end(); it++)
615     {
616         const CounterSetPtr& counterSet = it->second;
617         CounterSetRecord& counterSetRecord = counterSetRecords.at(counterSetIndex);
618 
619         std::string errorMessage;
620         if (!CreateCounterSetRecord(counterSet, counterSetRecord, errorMessage))
621         {
622             CancelOperationAndThrow<RuntimeException>(errorMessage);
623         }
624 
625         // Update the total size in words of the counter set records
626         counterSetRecordsSize += counterSetRecord.size();
627 
628         // Add the counter set record offset to the counter set records pointer table offset field
629         counterSetRecordOffsets[counterSetRecordOffsetIndex] = pointerTableOffset;
630         pointerTableOffset += armnn::numeric_cast<uint32_t>(counterSetRecord.size() * uint32_t_size);
631 
632         counterSetIndex++;
633         counterSetRecordOffsetIndex++;
634     }
635 
636     // ----------------
637     // Category records
638     // ----------------
639 
640     // Process category records
641     std::vector<CategoryRecord> categoryRecords(categoryCount);
642     const Categories& categories = counterDirectory.GetCategories();
643     std::vector<uint32_t> categoryRecordOffsets(categoryCount, 0); // category_records_pointer_table
644     size_t categoryRecordsSize = 0;
645     size_t categoryIndex = 0;
646     size_t categoryRecordOffsetIndex = 0;
647 
648     pointerTableOffset -= armnn::numeric_cast<uint32_t>(counterSetCount * uint32_t_size);
649     for (auto it = categories.begin(); it != categories.end(); it++)
650     {
651         const CategoryPtr& category = *it;
652         CategoryRecord& categoryRecord = categoryRecords.at(categoryIndex);
653 
654         std::string errorMessage;
655         if (!CreateCategoryRecord(category, counterDirectory.GetCounters(), categoryRecord, errorMessage))
656         {
657             CancelOperationAndThrow<RuntimeException>(errorMessage);
658         }
659 
660         // Update the total size in words of the category records
661         categoryRecordsSize += categoryRecord.size();
662 
663         // Add the category record offset to the category records pointer table offset field
664         categoryRecordOffsets[categoryRecordOffsetIndex] = pointerTableOffset;
665         pointerTableOffset += armnn::numeric_cast<uint32_t>(categoryRecord.size() * uint32_t_size);
666 
667         categoryIndex++;
668         categoryRecordOffsetIndex++;
669     }
670 
671     // Calculate the length in words of the counter directory packet's data (excludes the packet header size)
672     const size_t counterDirectoryPacketDataLength =
673                  bodyHeaderSize +                 // The size of the body header
674                  deviceRecordOffsets.size() +     // The size of the device records pointer table
675                  counterSetRecordOffsets.size() + // The size of counter set pointer table
676                  categoryRecordOffsets.size() +   // The size of category records pointer table
677                  deviceRecordsSize +              // The total size of the device records
678                  counterSetRecordsSize +          // The total size of the counter set records
679                  categoryRecordsSize;             // The total size of the category records
680 
681     // Calculate the size in words of the counter directory packet (the data length plus the packet header size)
682     const size_t counterDirectoryPacketSize = packetHeaderSize +                // The size of the packet header
683                                               counterDirectoryPacketDataLength; // The data length
684 
685     // Allocate the necessary space for the counter directory packet
686     std::vector<uint32_t> counterDirectoryPacket(counterDirectoryPacketSize, 0);
687 
688     // -------------
689     // Packet header
690     // -------------
691 
692     // Packet header word 0:
693     // 26:31 [6]  packet_family: control Packet Family
694     // 16:25 [10] packet_id: packet identifier
695     // 8:15  [8]  reserved: all zeros
696     // 0:7   [8]  reserved: all zeros
697     uint32_t packetFamily = 0;
698     uint32_t packetId = 2;
699     uint32_t packetHeaderWord0 = ((packetFamily & 0x3F) << 26) | ((packetId & 0x3FF) << 16);
700 
701     // Packet header word 1:
702     // 0:31 [32] data_length: length of data, in bytes
703     uint32_t packetHeaderWord1 = armnn::numeric_cast<uint32_t>(
704             counterDirectoryPacketDataLength * uint32_t_size);
705 
706     // Create the packet header
707     uint32_t packetHeader[2]
708     {
709         packetHeaderWord0, // packet_family + packet_id + reserved + reserved
710         packetHeaderWord1  // data_length
711     };
712 
713     // -----------
714     // Body header
715     // -----------
716 
717     // Body header word 0:
718     // 16:31 [16] device_records_count: number of entries in the device_records_pointer_table
719     // 0:15  [16] reserved: all zeros
720     const uint32_t bodyHeaderWord0 = static_cast<uint32_t>(deviceCount) << 16;
721 
722     // Body header word 1:
723     // 0:31 [32] device_records_pointer_table_offset: offset to the device_records_pointer_table
724     const uint32_t bodyHeaderWord1 = bodyHeaderSizeBytes; // The offset is always the bodyHeaderSize,
725                                                           // as the device record pointer table field
726                                                           // is always the first item in the pool
727 
728     // Body header word 2:
729     // 16:31 [16] counter_set_count: number of entries in the counter_set_pointer_table
730     // 0:15  [16] reserved: all zeros
731     const uint32_t bodyHeaderWord2 = static_cast<uint32_t>(counterSetCount) << 16;
732 
733     // Body header word 3:
734     // 0:31 [32] counter_set_pointer_table_offset: offset to the counter_set_pointer_table
735     const uint32_t bodyHeaderWord3 = armnn::numeric_cast<uint32_t>(deviceRecordOffsets.size() *
736                                                                    uint32_t_size +       // The size of the
737                                                                    bodyHeaderSizeBytes); // device records pointer table
738 
739     // Body header word 4:
740     // 16:31 [16] categories_count: number of entries in the categories_pointer_table
741     // 0:15  [16] reserved: all zeros
742     const uint32_t bodyHeaderWord4 = static_cast<uint32_t>(categoryCount) << 16;
743 
744     // Body header word 3:
745     // 0:31 [32] categories_pointer_table_offset: offset to the categories_pointer_table
746     const uint32_t bodyHeaderWord5 =
747                    armnn::numeric_cast<uint32_t>(
748                        deviceRecordOffsets.size() * uint32_t_size +     // The size of the device records
749                        counterSetRecordOffsets.size() * uint32_t_size   // pointer table, plus the size of
750                        +  bodyHeaderSizeBytes);                         // the counter set pointer table
751 
752     // Create the body header
753     const uint32_t bodyHeader[bodyHeaderSize]
754     {
755         bodyHeaderWord0, // device_records_count + reserved
756         bodyHeaderWord1, // device_records_pointer_table_offset
757         bodyHeaderWord2, // counter_set_count + reserved
758         bodyHeaderWord3, // counter_set_pointer_table_offset
759         bodyHeaderWord4, // categories_count + reserved
760         bodyHeaderWord5  // categories_pointer_table_offset
761     };
762 
763     ARMNN_NO_CONVERSION_WARN_BEGIN
764     // Create the counter directory packet
765     auto counterDirectoryPacketOffset = counterDirectoryPacket.begin();
766     // packet_header
767     std::copy(packetHeader, packetHeader + packetHeaderSize, counterDirectoryPacketOffset);
768     counterDirectoryPacketOffset += packetHeaderSize;
769     // body_header
770     std::copy(bodyHeader, bodyHeader + bodyHeaderSize, counterDirectoryPacketOffset);
771     counterDirectoryPacketOffset += bodyHeaderSize;
772     // device_records_pointer_table
773     std::copy(deviceRecordOffsets.begin(), deviceRecordOffsets.end(), counterDirectoryPacketOffset);
774     counterDirectoryPacketOffset += deviceRecordOffsets.size();
775     // counter_set_pointer_table
776     std::copy(counterSetRecordOffsets.begin(), counterSetRecordOffsets.end(), counterDirectoryPacketOffset);
777     counterDirectoryPacketOffset += counterSetRecordOffsets.size();
778     // category_pointer_table
779     std::copy(categoryRecordOffsets.begin(), categoryRecordOffsets.end(), counterDirectoryPacketOffset);
780     counterDirectoryPacketOffset += categoryRecordOffsets.size();
781     // device_records
782     for (const DeviceRecord& deviceRecord : deviceRecords)
783     {
784         std::copy(deviceRecord.begin(), deviceRecord.end(), counterDirectoryPacketOffset); // device_record
785         counterDirectoryPacketOffset += deviceRecord.size();
786     }
787     // counter_set_records
788     for (const CounterSetRecord& counterSetRecord : counterSetRecords)
789     {
790         std::copy(counterSetRecord.begin(), counterSetRecord.end(), counterDirectoryPacketOffset); // counter_set_record
791         counterDirectoryPacketOffset += counterSetRecord.size();
792     }
793     // category_records
794     for (const CategoryRecord& categoryRecord : categoryRecords)
795     {
796         std::copy(categoryRecord.begin(), categoryRecord.end(), counterDirectoryPacketOffset); // category_record
797         counterDirectoryPacketOffset += categoryRecord.size();
798     }
799     ARMNN_NO_CONVERSION_WARN_END
800 
801     // Calculate the total size in bytes of the counter directory packet
802     uint32_t totalSize = armnn::numeric_cast<uint32_t>(counterDirectoryPacketSize * uint32_t_size);
803 
804     // Reserve space in the buffer for the packet
805     uint32_t reserved = 0;
806     IPacketBufferPtr writeBuffer = m_BufferManager.Reserve(totalSize, reserved);
807 
808     if (writeBuffer == nullptr || reserved < totalSize)
809     {
810         CancelOperationAndThrow<BufferExhaustion>(
811             writeBuffer,
812             fmt::format("No space left in buffer. Unable to reserve ({}) bytes.", totalSize));
813     }
814 
815     // Offset for writing to the buffer
816     uint32_t offset = 0;
817 
818     // Write the counter directory packet to the buffer
819     for (uint32_t counterDirectoryPacketWord : counterDirectoryPacket)
820     {
821         WriteUint32(writeBuffer, offset, counterDirectoryPacketWord);
822         offset += armnn::numeric_cast<uint32_t>(uint32_t_size);
823     }
824 
825     m_BufferManager.Commit(writeBuffer, totalSize);
826 }
827 
SendPeriodicCounterCapturePacket(uint64_t timestamp,const IndexValuePairsVector & values)828 void SendCounterPacket::SendPeriodicCounterCapturePacket(uint64_t timestamp, const IndexValuePairsVector& values)
829 {
830     uint32_t uint16_t_size = sizeof(uint16_t);
831     uint32_t uint32_t_size = sizeof(uint32_t);
832     uint32_t uint64_t_size = sizeof(uint64_t);
833 
834     uint32_t packetFamily = 3;
835     uint32_t packetClass = 0;
836     uint32_t packetType = 0;
837     uint32_t headerSize = 2 * uint32_t_size;
838     uint32_t bodySize = uint64_t_size + armnn::numeric_cast<uint32_t>(values.size()) * (uint16_t_size + uint32_t_size);
839     uint32_t totalSize = headerSize + bodySize;
840     uint32_t offset = 0;
841     uint32_t reserved = 0;
842 
843     IPacketBufferPtr writeBuffer = m_BufferManager.Reserve(totalSize, reserved);
844 
845     if (writeBuffer == nullptr || reserved < totalSize)
846     {
847         CancelOperationAndThrow<BufferExhaustion>(
848             writeBuffer,
849             fmt::format("No space left in buffer. Unable to reserve ({}) bytes.", totalSize));
850     }
851 
852     // Create header.
853     WriteUint32(writeBuffer,
854                 offset,
855                 ((packetFamily & 0x0000003F) << 26) |
856                 ((packetClass  & 0x0000007F) << 19) |
857                 ((packetType   & 0x00000007) << 16));
858     offset += uint32_t_size;
859     WriteUint32(writeBuffer, offset, bodySize);
860 
861     // Copy captured Timestamp.
862     offset += uint32_t_size;
863     WriteUint64(writeBuffer, offset, timestamp);
864 
865     // Copy selectedCounterIds.
866     offset += uint64_t_size;
867     for (const auto& pair: values)
868     {
869         WriteUint16(writeBuffer, offset, pair.counterId);
870         offset += uint16_t_size;
871         WriteUint32(writeBuffer, offset, pair.counterValue);
872         offset += uint32_t_size;
873     }
874 
875     m_BufferManager.Commit(writeBuffer, totalSize);
876 }
877 
SendPeriodicCounterSelectionPacket(uint32_t capturePeriod,const std::vector<uint16_t> & selectedCounterIds)878 void SendCounterPacket::SendPeriodicCounterSelectionPacket(uint32_t capturePeriod,
879                                                            const std::vector<uint16_t>& selectedCounterIds)
880 {
881     uint32_t uint16_t_size = sizeof(uint16_t);
882     uint32_t uint32_t_size = sizeof(uint32_t);
883 
884     uint32_t packetFamily = 0;
885     uint32_t packetId = 4;
886     uint32_t headerSize = 2 * uint32_t_size;
887     uint32_t bodySize = uint32_t_size + armnn::numeric_cast<uint32_t>(selectedCounterIds.size()) * uint16_t_size;
888     uint32_t totalSize = headerSize + bodySize;
889     uint32_t offset = 0;
890     uint32_t reserved = 0;
891 
892     IPacketBufferPtr writeBuffer = m_BufferManager.Reserve(totalSize, reserved);
893 
894     if (writeBuffer == nullptr || reserved < totalSize)
895     {
896         CancelOperationAndThrow<BufferExhaustion>(
897             writeBuffer,
898             fmt::format("No space left in buffer. Unable to reserve ({}) bytes.", totalSize));
899     }
900 
901     // Create header.
902     WriteUint32(writeBuffer, offset, ((packetFamily & 0x3F) << 26) | ((packetId & 0x3FF) << 16));
903     offset += uint32_t_size;
904     WriteUint32(writeBuffer, offset, bodySize);
905 
906     // Copy capturePeriod.
907     offset += uint32_t_size;
908     WriteUint32(writeBuffer, offset, capturePeriod);
909 
910     // Copy selectedCounterIds.
911     offset += uint32_t_size;
912     for(const uint16_t& id: selectedCounterIds)
913     {
914         WriteUint16(writeBuffer, offset, id);
915         offset += uint16_t_size;
916     }
917 
918     m_BufferManager.Commit(writeBuffer, totalSize);
919 }
920 
921 } // namespace profiling
922 
923 } // namespace armnn
924