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