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