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