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