IVGCVSW-4835 Change CounterSet and Device name offsets sizes to bytes
[platform/upstream/armnn.git] / src / profiling / test / SendCounterPacketTests.cpp
index 9a314c1..e7e2312 100644 (file)
 //
-// Copyright © 2017 Arm Ltd. All rights reserved.
+// Copyright © 2019 Arm Ltd. All rights reserved.
 // SPDX-License-Identifier: MIT
 //
 
-#include "../IBufferWrapper.hpp"
-#include "../ISendCounterPacket.hpp"
+#include "ProfilingMocks.hpp"
+#include "ProfilingTestUtils.hpp"
+#include "SendCounterPacketTests.hpp"
+
+#include <BufferManager.hpp>
+#include <CounterDirectory.hpp>
+#include <EncodeVersion.hpp>
+#include <ProfilingUtils.hpp>
+#include <SendCounterPacket.hpp>
+#include <Processes.hpp>
+
+#include <armnn/Exceptions.hpp>
+#include <armnn/Conversion.hpp>
+#include <armnn/Utils.hpp>
+
+#include <common/include/Constants.hpp>
+
 
-#include <iostream>
 #include <boost/test/unit_test.hpp>
+#include <boost/numeric/conversion/cast.hpp>
+
+#include <chrono>
+
+using namespace armnn::profiling;
+
+namespace
+{
+
+// A short delay to wait for the thread to process a packet.
+uint16_t constexpr WAIT_UNTIL_READABLE_MS = 20;
 
+void SetNotConnectedProfilingState(ProfilingStateMachine& profilingStateMachine)
+{
+    ProfilingState currentState = profilingStateMachine.GetCurrentState();
+    switch (currentState)
+    {
+    case ProfilingState::WaitingForAck:
+        profilingStateMachine.TransitionToState(ProfilingState::Active);
+        ARMNN_FALLTHROUGH;
+    case ProfilingState::Uninitialised:
+        ARMNN_FALLTHROUGH;
+    case ProfilingState::Active:
+        profilingStateMachine.TransitionToState(ProfilingState::NotConnected);
+        ARMNN_FALLTHROUGH;
+    case ProfilingState::NotConnected:
+        return;
+    default:
+        BOOST_CHECK_MESSAGE(false, "Invalid profiling state");
+    }
+}
+
+void SetWaitingForAckProfilingState(ProfilingStateMachine& profilingStateMachine)
+{
+    ProfilingState currentState = profilingStateMachine.GetCurrentState();
+    switch (currentState)
+    {
+    case ProfilingState::Uninitialised:
+        ARMNN_FALLTHROUGH;
+    case ProfilingState::Active:
+        profilingStateMachine.TransitionToState(ProfilingState::NotConnected);
+        ARMNN_FALLTHROUGH;
+    case ProfilingState::NotConnected:
+        profilingStateMachine.TransitionToState(ProfilingState::WaitingForAck);
+        ARMNN_FALLTHROUGH;
+    case ProfilingState::WaitingForAck:
+        return;
+    default:
+        BOOST_CHECK_MESSAGE(false, "Invalid profiling state");
+    }
+}
+
+void SetActiveProfilingState(ProfilingStateMachine& profilingStateMachine)
+{
+    ProfilingState currentState = profilingStateMachine.GetCurrentState();
+    switch (currentState)
+    {
+    case ProfilingState::Uninitialised:
+        profilingStateMachine.TransitionToState(ProfilingState::NotConnected);
+        ARMNN_FALLTHROUGH;
+    case ProfilingState::NotConnected:
+        profilingStateMachine.TransitionToState(ProfilingState::WaitingForAck);
+        ARMNN_FALLTHROUGH;
+    case ProfilingState::WaitingForAck:
+        profilingStateMachine.TransitionToState(ProfilingState::Active);
+        ARMNN_FALLTHROUGH;
+    case ProfilingState::Active:
+        return;
+    default:
+        BOOST_CHECK_MESSAGE(false, "Invalid profiling state");
+    }
+}
+
+} // Anonymous namespace
 
 BOOST_AUTO_TEST_SUITE(SendCounterPacketTests)
 
-using namespace armnn::profiling;
+using PacketType = MockProfilingConnection::PacketType;
+
+BOOST_AUTO_TEST_CASE(MockSendCounterPacketTest)
+{
+    MockBufferManager mockBuffer(512);
+    MockSendCounterPacket mockSendCounterPacket(mockBuffer);
+
+    mockSendCounterPacket.SendStreamMetaDataPacket();
+
+    auto packetBuffer = mockBuffer.GetReadableBuffer();
+    const char* buffer = reinterpret_cast<const char*>(packetBuffer->GetReadableData());
+
+    BOOST_TEST(strcmp(buffer, "SendStreamMetaDataPacket") == 0);
+
+    mockBuffer.MarkRead(packetBuffer);
+
+    CounterDirectory counterDirectory;
+    mockSendCounterPacket.SendCounterDirectoryPacket(counterDirectory);
+
+    packetBuffer = mockBuffer.GetReadableBuffer();
+    buffer = reinterpret_cast<const char*>(packetBuffer->GetReadableData());
+
+    BOOST_TEST(strcmp(buffer, "SendCounterDirectoryPacket") == 0);
+
+    mockBuffer.MarkRead(packetBuffer);
+
+    uint64_t timestamp = 0;
+    std::vector<CounterValue> indexValuePairs;
+
+    mockSendCounterPacket.SendPeriodicCounterCapturePacket(timestamp, indexValuePairs);
+
+    packetBuffer = mockBuffer.GetReadableBuffer();
+    buffer = reinterpret_cast<const char*>(packetBuffer->GetReadableData());
+
+    BOOST_TEST(strcmp(buffer, "SendPeriodicCounterCapturePacket") == 0);
+
+    mockBuffer.MarkRead(packetBuffer);
+
+    uint32_t capturePeriod = 0;
+    std::vector<uint16_t> selectedCounterIds;
+    mockSendCounterPacket.SendPeriodicCounterSelectionPacket(capturePeriod, selectedCounterIds);
+
+    packetBuffer = mockBuffer.GetReadableBuffer();
+    buffer = reinterpret_cast<const char*>(packetBuffer->GetReadableData());
+
+    BOOST_TEST(strcmp(buffer, "SendPeriodicCounterSelectionPacket") == 0);
+
+    mockBuffer.MarkRead(packetBuffer);
+}
+
+BOOST_AUTO_TEST_CASE(SendPeriodicCounterSelectionPacketTest)
+{
+    // Error no space left in buffer
+    MockBufferManager mockBuffer1(10);
+    SendCounterPacket sendPacket1(mockBuffer1);
+
+    uint32_t capturePeriod = 1000;
+    std::vector<uint16_t> selectedCounterIds;
+    BOOST_CHECK_THROW(sendPacket1.SendPeriodicCounterSelectionPacket(capturePeriod, selectedCounterIds),
+                      BufferExhaustion);
+
+    // Packet without any counters
+    MockBufferManager mockBuffer2(512);
+    SendCounterPacket sendPacket2(mockBuffer2);
+
+    sendPacket2.SendPeriodicCounterSelectionPacket(capturePeriod, selectedCounterIds);
+    auto readBuffer2 = mockBuffer2.GetReadableBuffer();
+
+    uint32_t headerWord0 = ReadUint32(readBuffer2, 0);
+    uint32_t headerWord1 = ReadUint32(readBuffer2, 4);
+    uint32_t period = ReadUint32(readBuffer2, 8);
+
+    BOOST_TEST(((headerWord0 >> 26) & 0x3F) == 0);  // packet family
+    BOOST_TEST(((headerWord0 >> 16) & 0x3FF) == 4); // packet id
+    BOOST_TEST(headerWord1 == 4);                   // data lenght
+    BOOST_TEST(period == 1000);                     // capture period
 
-class MockBuffer : public IBufferWrapper
+    // Full packet message
+    MockBufferManager mockBuffer3(512);
+    SendCounterPacket sendPacket3(mockBuffer3);
+
+    selectedCounterIds.reserve(5);
+    selectedCounterIds.emplace_back(100);
+    selectedCounterIds.emplace_back(200);
+    selectedCounterIds.emplace_back(300);
+    selectedCounterIds.emplace_back(400);
+    selectedCounterIds.emplace_back(500);
+    sendPacket3.SendPeriodicCounterSelectionPacket(capturePeriod, selectedCounterIds);
+    auto readBuffer3 = mockBuffer3.GetReadableBuffer();
+
+    headerWord0 = ReadUint32(readBuffer3, 0);
+    headerWord1 = ReadUint32(readBuffer3, 4);
+    period = ReadUint32(readBuffer3, 8);
+
+    BOOST_TEST(((headerWord0 >> 26) & 0x3F) == 0);  // packet family
+    BOOST_TEST(((headerWord0 >> 16) & 0x3FF) == 4); // packet id
+    BOOST_TEST(headerWord1 == 14);                  // data lenght
+    BOOST_TEST(period == 1000);                     // capture period
+
+    uint16_t counterId = 0;
+    uint32_t offset = 12;
+
+    // Counter Ids
+    for(const uint16_t& id : selectedCounterIds)
+    {
+        counterId = ReadUint16(readBuffer3, offset);
+        BOOST_TEST(counterId == id);
+        offset += 2;
+    }
+}
+
+BOOST_AUTO_TEST_CASE(SendPeriodicCounterCapturePacketTest)
 {
-public:
-    MockBuffer() : m_Buffer() {}
+    ProfilingStateMachine profilingStateMachine;
+
+    // Error no space left in buffer
+    MockBufferManager mockBuffer1(10);
+    SendCounterPacket sendPacket1(mockBuffer1);
+
+    auto captureTimestamp = std::chrono::steady_clock::now();
+    uint64_t time =  static_cast<uint64_t >(captureTimestamp.time_since_epoch().count());
+    std::vector<CounterValue> indexValuePairs;
+
+    BOOST_CHECK_THROW(sendPacket1.SendPeriodicCounterCapturePacket(time, indexValuePairs),
+                      BufferExhaustion);
+
+    // Packet without any counters
+    MockBufferManager mockBuffer2(512);
+    SendCounterPacket sendPacket2(mockBuffer2);
+
+    sendPacket2.SendPeriodicCounterCapturePacket(time, indexValuePairs);
+    auto readBuffer2 = mockBuffer2.GetReadableBuffer();
+
+    uint32_t headerWord0 = ReadUint32(readBuffer2, 0);
+    uint32_t headerWord1 = ReadUint32(readBuffer2, 4);
+    uint64_t readTimestamp = ReadUint64(readBuffer2, 8);
+
+    BOOST_TEST(((headerWord0 >> 26) & 0x0000003F) == 3); // packet family
+    BOOST_TEST(((headerWord0 >> 19) & 0x0000007F) == 0); // packet class
+    BOOST_TEST(((headerWord0 >> 16) & 0x00000007) == 0); // packet type
+    BOOST_TEST(headerWord1 == 8);                        // data length
+    BOOST_TEST(time == readTimestamp);                   // capture period
 
-    unsigned char* Reserve(unsigned int requestedSize, unsigned int& reservedSize) override
+    // Full packet message
+    MockBufferManager mockBuffer3(512);
+    SendCounterPacket sendPacket3(mockBuffer3);
+
+    indexValuePairs.reserve(5);
+    indexValuePairs.emplace_back(CounterValue{0, 100});
+    indexValuePairs.emplace_back(CounterValue{1, 200});
+    indexValuePairs.emplace_back(CounterValue{2, 300});
+    indexValuePairs.emplace_back(CounterValue{3, 400});
+    indexValuePairs.emplace_back(CounterValue{4, 500});
+    sendPacket3.SendPeriodicCounterCapturePacket(time, indexValuePairs);
+    auto readBuffer3 = mockBuffer3.GetReadableBuffer();
+
+    headerWord0 = ReadUint32(readBuffer3, 0);
+    headerWord1 = ReadUint32(readBuffer3, 4);
+    uint64_t readTimestamp2 = ReadUint64(readBuffer3, 8);
+
+    BOOST_TEST(((headerWord0 >> 26) & 0x0000003F) == 3); // packet family
+    BOOST_TEST(((headerWord0 >> 19) & 0x0000007F) == 0); // packet class
+    BOOST_TEST(((headerWord0 >> 16) & 0x00000007) == 0); // packet type
+    BOOST_TEST(headerWord1 == 38);                       // data length
+    BOOST_TEST(time == readTimestamp2);                  // capture period
+
+    uint16_t counterIndex = 0;
+    uint32_t counterValue = 100;
+    uint32_t offset = 16;
+
+    // Counter Ids
+    for (auto it = indexValuePairs.begin(), end = indexValuePairs.end(); it != end; ++it)
     {
-        if (requestedSize > m_BufferSize)
-        {
-            reservedSize = m_BufferSize;
-        }
-        else
+        // Check Counter Index
+        uint16_t readIndex = ReadUint16(readBuffer3, offset);
+        BOOST_TEST(counterIndex == readIndex);
+        counterIndex++;
+        offset += 2;
+
+        // Check Counter Value
+        uint32_t readValue = ReadUint32(readBuffer3, offset);
+        BOOST_TEST(counterValue == readValue);
+        counterValue += 100;
+        offset += 4;
+    }
+
+}
+
+BOOST_AUTO_TEST_CASE(SendStreamMetaDataPacketTest)
+{
+    using boost::numeric_cast;
+
+    uint32_t sizeUint32 = numeric_cast<uint32_t>(sizeof(uint32_t));
+
+    // Error no space left in buffer
+    MockBufferManager mockBuffer1(10);
+    SendCounterPacket sendPacket1(mockBuffer1);
+    BOOST_CHECK_THROW(sendPacket1.SendStreamMetaDataPacket(), armnn::profiling::BufferExhaustion);
+
+    // Full metadata packet
+
+    std::string processName = GetProcessName().substr(0, 60);
+
+    uint32_t infoSize =            numeric_cast<uint32_t>(GetSoftwareInfo().size()) + 1;
+    uint32_t hardwareVersionSize = numeric_cast<uint32_t>(GetHardwareVersion().size()) + 1;
+    uint32_t softwareVersionSize = numeric_cast<uint32_t>(GetSoftwareVersion().size()) + 1;
+    uint32_t processNameSize =     numeric_cast<uint32_t>(processName.size()) + 1;
+
+    uint32_t packetEntries = 6;
+
+    MockBufferManager mockBuffer2(512);
+    SendCounterPacket sendPacket2(mockBuffer2);
+    sendPacket2.SendStreamMetaDataPacket();
+    auto readBuffer2 = mockBuffer2.GetReadableBuffer();
+
+    uint32_t headerWord0 = ReadUint32(readBuffer2, 0);
+    uint32_t headerWord1 = ReadUint32(readBuffer2, sizeUint32);
+
+    BOOST_TEST(((headerWord0 >> 26) & 0x3F) == 0); // packet family
+    BOOST_TEST(((headerWord0 >> 16) & 0x3FF) == 0); // packet id
+
+    uint32_t totalLength = numeric_cast<uint32_t>(2 * sizeUint32 + 10 * sizeUint32 + infoSize + hardwareVersionSize +
+                                                  softwareVersionSize + processNameSize + sizeUint32 +
+                                                  2 * packetEntries * sizeUint32);
+
+    BOOST_TEST(headerWord1 == totalLength - (2 * sizeUint32)); // data length
+
+    uint32_t offset = sizeUint32 * 2;
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == armnnProfiling::PIPE_MAGIC); // pipe_magic
+    offset += sizeUint32;
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == EncodeVersion(1, 0, 0)); // stream_metadata_version
+    offset += sizeUint32;
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == MAX_METADATA_PACKET_LENGTH); // max_data_len
+    offset += sizeUint32;
+    int pid = armnnUtils::Processes::GetCurrentId();
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == numeric_cast<uint32_t>(pid));
+    offset += sizeUint32;
+    uint32_t poolOffset = 10 * sizeUint32;
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == poolOffset); // offset_info
+    offset += sizeUint32;
+    poolOffset += infoSize;
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == poolOffset); // offset_hw_version
+    offset += sizeUint32;
+    poolOffset += hardwareVersionSize;
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == poolOffset); // offset_sw_version
+    offset += sizeUint32;
+    poolOffset += softwareVersionSize;
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == poolOffset); // offset_process_name
+    offset += sizeUint32;
+    poolOffset += processNameSize;
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == poolOffset); // offset_packet_version_table
+    offset += sizeUint32;
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == 0); // reserved
+
+    const unsigned char* readData2 = readBuffer2->GetReadableData();
+
+    offset += sizeUint32;
+    if (infoSize)
+    {
+        BOOST_TEST(strcmp(reinterpret_cast<const char *>(&readData2[offset]), GetSoftwareInfo().c_str()) == 0);
+        offset += infoSize;
+    }
+
+    if (hardwareVersionSize)
+    {
+        BOOST_TEST(strcmp(reinterpret_cast<const char *>(&readData2[offset]), GetHardwareVersion().c_str()) == 0);
+        offset += hardwareVersionSize;
+    }
+
+    if (softwareVersionSize)
+    {
+        BOOST_TEST(strcmp(reinterpret_cast<const char *>(&readData2[offset]), GetSoftwareVersion().c_str()) == 0);
+        offset += softwareVersionSize;
+    }
+
+    if (processNameSize)
+    {
+        BOOST_TEST(strcmp(reinterpret_cast<const char *>(&readData2[offset]), GetProcessName().c_str()) == 0);
+        offset += processNameSize;
+    }
+
+    if (packetEntries)
+    {
+        BOOST_TEST((ReadUint32(readBuffer2, offset) >> 16) == packetEntries);
+        offset += sizeUint32;
+        for (uint32_t i = 0; i < packetEntries - 1; ++i)
         {
-            reservedSize = requestedSize;
+            BOOST_TEST(((ReadUint32(readBuffer2, offset) >> 26) & 0x3F) == 0);
+            BOOST_TEST(((ReadUint32(readBuffer2, offset) >> 16) & 0x3FF) == i);
+            offset += sizeUint32;
+            BOOST_TEST(ReadUint32(readBuffer2, offset) == EncodeVersion(1, 0, 0));
+            offset += sizeUint32;
         }
 
-        return m_Buffer;
+        BOOST_TEST(((ReadUint32(readBuffer2, offset) >> 26) & 0x3F) == 1);
+        BOOST_TEST(((ReadUint32(readBuffer2, offset) >> 16) & 0x3FF) == 0);
+        offset += sizeUint32;
+        BOOST_TEST(ReadUint32(readBuffer2, offset) == EncodeVersion(1, 0, 0));
+        offset += sizeUint32;
     }
 
-    void Commit(unsigned int size) override {}
+    BOOST_TEST(offset == totalLength);
+}
+
+BOOST_AUTO_TEST_CASE(CreateDeviceRecordTest)
+{
+    MockBufferManager mockBuffer(0);
+    SendCounterPacketTest sendCounterPacketTest(mockBuffer);
+
+    // Create a device for testing
+    uint16_t deviceUid = 27;
+    const std::string deviceName = "some_device";
+    uint16_t deviceCores = 3;
+    const DevicePtr device = std::make_unique<Device>(deviceUid, deviceName, deviceCores);
+
+    // Create a device record
+    SendCounterPacket::DeviceRecord deviceRecord;
+    std::string errorMessage;
+    bool result = sendCounterPacketTest.CreateDeviceRecordTest(device, deviceRecord, errorMessage);
+
+    BOOST_CHECK(result);
+    BOOST_CHECK(errorMessage.empty());
+    BOOST_CHECK(deviceRecord.size() == 6); // Size in words: header [2] + device name [4]
 
-    const unsigned char* GetReadBuffer(unsigned int& size) override
+    uint16_t deviceRecordWord0[]
     {
-        size = static_cast<unsigned int>(strlen(reinterpret_cast<const char*>(m_Buffer)) + 1);
-        return m_Buffer;
-    }
+        static_cast<uint16_t>(deviceRecord[0] >> 16),
+        static_cast<uint16_t>(deviceRecord[0])
+    };
+    BOOST_CHECK(deviceRecordWord0[0] == deviceUid); // uid
+    BOOST_CHECK(deviceRecordWord0[1] == deviceCores); // cores
+    BOOST_CHECK(deviceRecord[1] == 8); // name_offset
+    BOOST_CHECK(deviceRecord[2] == deviceName.size() + 1); // The length of the SWTrace string (name)
+    BOOST_CHECK(std::memcmp(deviceRecord.data() + 3, deviceName.data(), deviceName.size()) == 0); // name
+}
+
+BOOST_AUTO_TEST_CASE(CreateInvalidDeviceRecordTest)
+{
+    MockBufferManager mockBuffer(0);
+    SendCounterPacketTest sendCounterPacketTest(mockBuffer);
+
+    // Create a device for testing
+    uint16_t deviceUid = 27;
+    const std::string deviceName = "some€£invalid‡device";
+    uint16_t deviceCores = 3;
+    const DevicePtr device = std::make_unique<Device>(deviceUid, deviceName, deviceCores);
+
+    // Create a device record
+    SendCounterPacket::DeviceRecord deviceRecord;
+    std::string errorMessage;
+    bool result = sendCounterPacketTest.CreateDeviceRecordTest(device, deviceRecord, errorMessage);
+
+    BOOST_CHECK(!result);
+    BOOST_CHECK(!errorMessage.empty());
+    BOOST_CHECK(deviceRecord.empty());
+}
+
+BOOST_AUTO_TEST_CASE(CreateCounterSetRecordTest)
+{
+    MockBufferManager mockBuffer(0);
+    SendCounterPacketTest sendCounterPacketTest(mockBuffer);
+
+    // Create a counter set for testing
+    uint16_t counterSetUid = 27;
+    const std::string counterSetName = "some_counter_set";
+    uint16_t counterSetCount = 3421;
+    const CounterSetPtr counterSet = std::make_unique<CounterSet>(counterSetUid, counterSetName, counterSetCount);
+
+    // Create a counter set record
+    SendCounterPacket::CounterSetRecord counterSetRecord;
+    std::string errorMessage;
+    bool result = sendCounterPacketTest.CreateCounterSetRecordTest(counterSet, counterSetRecord, errorMessage);
+
+    BOOST_CHECK(result);
+    BOOST_CHECK(errorMessage.empty());
+    BOOST_CHECK(counterSetRecord.size() == 8); // Size in words: header [2] + counter set name [6]
+
+    uint16_t counterSetRecordWord0[]
+    {
+        static_cast<uint16_t>(counterSetRecord[0] >> 16),
+        static_cast<uint16_t>(counterSetRecord[0])
+    };
+    BOOST_CHECK(counterSetRecordWord0[0] == counterSetUid); // uid
+    BOOST_CHECK(counterSetRecordWord0[1] == counterSetCount); // cores
+    BOOST_CHECK(counterSetRecord[1] == 8); // name_offset
+    BOOST_CHECK(counterSetRecord[2] == counterSetName.size() + 1); // The length of the SWTrace string (name)
+    BOOST_CHECK(std::memcmp(counterSetRecord.data() + 3, counterSetName.data(), counterSetName.size()) == 0); // name
+}
+
+BOOST_AUTO_TEST_CASE(CreateInvalidCounterSetRecordTest)
+{
+    MockBufferManager mockBuffer(0);
+    SendCounterPacketTest sendCounterPacketTest(mockBuffer);
+
+    // Create a counter set for testing
+    uint16_t counterSetUid = 27;
+    const std::string counterSetName = "some invalid_counter€£set";
+    uint16_t counterSetCount = 3421;
+    const CounterSetPtr counterSet = std::make_unique<CounterSet>(counterSetUid, counterSetName, counterSetCount);
+
+    // Create a counter set record
+    SendCounterPacket::CounterSetRecord counterSetRecord;
+    std::string errorMessage;
+    bool result = sendCounterPacketTest.CreateCounterSetRecordTest(counterSet, counterSetRecord, errorMessage);
+
+    BOOST_CHECK(!result);
+    BOOST_CHECK(!errorMessage.empty());
+    BOOST_CHECK(counterSetRecord.empty());
+}
+
+BOOST_AUTO_TEST_CASE(CreateEventRecordTest)
+{
+    MockBufferManager mockBuffer(0);
+    SendCounterPacketTest sendCounterPacketTest(mockBuffer);
+
+    // Create a counter for testing
+    uint16_t counterUid = 7256;
+    uint16_t maxCounterUid = 132;
+    uint16_t deviceUid = 132;
+    uint16_t counterSetUid = 4497;
+    uint16_t counterClass = 1;
+    uint16_t counterInterpolation = 1;
+    double counterMultiplier = 1234.567f;
+    const std::string counterName = "some_valid_counter";
+    const std::string counterDescription = "a_counter_for_testing";
+    const std::string counterUnits = "Mrads2";
+    const CounterPtr counter = std::make_unique<Counter>(armnn::profiling::BACKEND_ID,
+                                                         counterUid,
+                                                         maxCounterUid,
+                                                         counterClass,
+                                                         counterInterpolation,
+                                                         counterMultiplier,
+                                                         counterName,
+                                                         counterDescription,
+                                                         counterUnits,
+                                                         deviceUid,
+                                                         counterSetUid);
+    ARMNN_ASSERT(counter);
+
+    // Create an event record
+    SendCounterPacket::EventRecord eventRecord;
+    std::string errorMessage;
+    bool result = sendCounterPacketTest.CreateEventRecordTest(counter, eventRecord, errorMessage);
+
+    BOOST_CHECK(result);
+    BOOST_CHECK(errorMessage.empty());
+    BOOST_CHECK(eventRecord.size() == 24); // Size in words: header [8] + counter name [6] + description [7] + units [3]
+
+    uint16_t eventRecordWord0[]
+    {
+        static_cast<uint16_t>(eventRecord[0] >> 16),
+        static_cast<uint16_t>(eventRecord[0])
+    };
+    uint16_t eventRecordWord1[]
+    {
+        static_cast<uint16_t>(eventRecord[1] >> 16),
+        static_cast<uint16_t>(eventRecord[1])
+    };
+    uint16_t eventRecordWord2[]
+    {
+        static_cast<uint16_t>(eventRecord[2] >> 16),
+        static_cast<uint16_t>(eventRecord[2])
+    };
+    uint32_t eventRecordWord34[]
+    {
+        eventRecord[3],
+        eventRecord[4]
+    };
+
+    BOOST_CHECK(eventRecordWord0[0] == maxCounterUid); // max_counter_uid
+    BOOST_CHECK(eventRecordWord0[1] == counterUid); // counter_uid
+    BOOST_CHECK(eventRecordWord1[0] == deviceUid); // device
+
+    BOOST_CHECK(eventRecordWord1[1] == counterSetUid); // counter_set
+    BOOST_CHECK(eventRecordWord2[0] == counterClass); // class
+    BOOST_CHECK(eventRecordWord2[1] == counterInterpolation); // interpolation
+    BOOST_CHECK(std::memcmp(eventRecordWord34, &counterMultiplier, sizeof(counterMultiplier)) == 0); // multiplier
+
+    ARMNN_NO_CONVERSION_WARN_BEGIN
+    uint32_t eventRecordBlockSize = 8u * sizeof(uint32_t);
+    uint32_t counterNameOffset = eventRecordBlockSize; // The name is the first item in pool
+    uint32_t counterDescriptionOffset = counterNameOffset + // Counter name offset
+                                        4u + // Counter name length (uint32_t)
+                                        counterName.size() + // 18u
+                                        1u + // Null-terminator
+                                        1u; // Rounding to the next word
+
+    size_t counterUnitsOffset = counterDescriptionOffset + // Counter description offset
+                                4u + // Counter description length (uint32_t)
+                                counterDescription.size() + // 21u
+                                1u + // Null-terminator
+                                2u;  // Rounding to the next word
+
+    ARMNN_NO_CONVERSION_WARN_END
+
+    BOOST_CHECK(eventRecord[5] == counterNameOffset); // name_offset
+    BOOST_CHECK(eventRecord[6] == counterDescriptionOffset); // description_offset
+    BOOST_CHECK(eventRecord[7] == counterUnitsOffset); // units_offset
+
+    // Offsets are relative to the start of the eventRecord
+    auto eventRecordPool = reinterpret_cast<unsigned char*>(eventRecord.data());
+    size_t uint32_t_size = sizeof(uint32_t);
+
+    // The length of the SWTrace string (name)
+    BOOST_CHECK(eventRecordPool[counterNameOffset] == counterName.size() + 1);
+    // The counter name
+    BOOST_CHECK(std::memcmp(eventRecordPool +
+                            counterNameOffset + // Offset
+                            uint32_t_size /* The length of the name */,
+                            counterName.data(),
+                            counterName.size()) == 0); // name
+    // The null-terminator at the end of the name
+    BOOST_CHECK(eventRecordPool[counterNameOffset + uint32_t_size + counterName.size()] == '\0');
+
+    // The length of the SWTrace string (description)
+    BOOST_CHECK(eventRecordPool[counterDescriptionOffset] == counterDescription.size() + 1);
+    // The counter description
+    BOOST_CHECK(std::memcmp(eventRecordPool +
+                            counterDescriptionOffset + // Offset
+                            uint32_t_size /* The length of the description */,
+                            counterDescription.data(),
+                            counterDescription.size()) == 0); // description
+    // The null-terminator at the end of the description
+    BOOST_CHECK(eventRecordPool[counterDescriptionOffset + uint32_t_size + counterDescription.size()] == '\0');
+
+    // The length of the SWTrace namestring (units)
+    BOOST_CHECK(eventRecordPool[counterUnitsOffset] == counterUnits.size() + 1);
+    // The counter units
+    BOOST_CHECK(std::memcmp(eventRecordPool +
+                            counterUnitsOffset + // Offset
+                            uint32_t_size /* The length of the units */,
+                            counterUnits.data(),
+                            counterUnits.size()) == 0); // units
+    // The null-terminator at the end of the units
+    BOOST_CHECK(eventRecordPool[counterUnitsOffset + uint32_t_size + counterUnits.size()] == '\0');
+}
+
+BOOST_AUTO_TEST_CASE(CreateEventRecordNoUnitsTest)
+{
+    MockBufferManager mockBuffer(0);
+    SendCounterPacketTest sendCounterPacketTest(mockBuffer);
+
+    // Create a counter for testing
+    uint16_t counterUid = 44312;
+    uint16_t maxCounterUid = 345;
+    uint16_t deviceUid = 101;
+    uint16_t counterSetUid = 34035;
+    uint16_t counterClass = 0;
+    uint16_t counterInterpolation = 1;
+    double counterMultiplier = 4435.0023f;
+    const std::string counterName = "some_valid_counter";
+    const std::string counterDescription = "a_counter_for_testing";
+    const CounterPtr counter = std::make_unique<Counter>(armnn::profiling::BACKEND_ID,
+                                                         counterUid,
+                                                         maxCounterUid,
+                                                         counterClass,
+                                                         counterInterpolation,
+                                                         counterMultiplier,
+                                                         counterName,
+                                                         counterDescription,
+                                                         "",
+                                                         deviceUid,
+                                                         counterSetUid);
+    ARMNN_ASSERT(counter);
+
+    // Create an event record
+    SendCounterPacket::EventRecord eventRecord;
+    std::string errorMessage;
+    bool result = sendCounterPacketTest.CreateEventRecordTest(counter, eventRecord, errorMessage);
+
+    BOOST_CHECK(result);
+    BOOST_CHECK(errorMessage.empty());
+    BOOST_CHECK(eventRecord.size() == 21); // Size in words: header [8] + counter name [6] + description [7]
+
+    uint16_t eventRecordWord0[]
+    {
+        static_cast<uint16_t>(eventRecord[0] >> 16),
+        static_cast<uint16_t>(eventRecord[0])
+    };
+    uint16_t eventRecordWord1[]
+    {
+        static_cast<uint16_t>(eventRecord[1] >> 16),
+        static_cast<uint16_t>(eventRecord[1])
+    };
+    uint16_t eventRecordWord2[]
+    {
+        static_cast<uint16_t>(eventRecord[2] >> 16),
+        static_cast<uint16_t>(eventRecord[2])
+    };
+    uint32_t eventRecordWord34[]
+    {
+        eventRecord[3],
+        eventRecord[4]
+    };
+    BOOST_CHECK(eventRecordWord0[0] == maxCounterUid); // max_counter_uid
+    BOOST_CHECK(eventRecordWord0[1] == counterUid); // counter_uid
+    BOOST_CHECK(eventRecordWord1[0] == deviceUid); // device
+    BOOST_CHECK(eventRecordWord1[1] == counterSetUid); // counter_set
+    BOOST_CHECK(eventRecordWord2[0] == counterClass); // class
+    BOOST_CHECK(eventRecordWord2[1] == counterInterpolation); // interpolation
+    BOOST_CHECK(std::memcmp(eventRecordWord34, &counterMultiplier, sizeof(counterMultiplier)) == 0); // multiplier
+
+    ARMNN_NO_CONVERSION_WARN_BEGIN
+    uint32_t eventRecordBlockSize = 8u * sizeof(uint32_t);
+    uint32_t counterNameOffset = eventRecordBlockSize; // The name is the first item in pool
+    uint32_t counterDescriptionOffset = counterNameOffset + // Counter name offset
+                                        4u + // Counter name length (uint32_t)
+                                        counterName.size() + // 18u
+                                        1u + // Null-terminator
+                                        1u; // Rounding to the next word
+    ARMNN_NO_CONVERSION_WARN_END
+
+    BOOST_CHECK(eventRecord[5] == counterNameOffset); // name_offset
+    BOOST_CHECK(eventRecord[6] == counterDescriptionOffset); // description_offset
+    BOOST_CHECK(eventRecord[7] == 0); // units_offset
+
+    // Offsets are relative to the start of the eventRecord
+    auto eventRecordPool = reinterpret_cast<unsigned char*>(eventRecord.data());
+    size_t uint32_t_size = sizeof(uint32_t);
+
+    // The length of the SWTrace string (name)
+    BOOST_CHECK(eventRecordPool[counterNameOffset] == counterName.size() + 1);
+    // The counter name
+    BOOST_CHECK(std::memcmp(eventRecordPool +
+                            counterNameOffset + // Offset
+                            uint32_t_size, // The length of the name
+                            counterName.data(),
+                            counterName.size()) == 0); // name
+    // The null-terminator at the end of the name
+    BOOST_CHECK(eventRecordPool[counterNameOffset + uint32_t_size + counterName.size()] == '\0');
+
+    // The length of the SWTrace string (description)
+    BOOST_CHECK(eventRecordPool[counterDescriptionOffset] == counterDescription.size() + 1);
+    // The counter description
+    BOOST_CHECK(std::memcmp(eventRecordPool +
+                            counterDescriptionOffset + // Offset
+                            uint32_t_size, // The length of the description
+                            counterDescription.data(),
+                            counterDescription.size()) == 0); // description
+    // The null-terminator at the end of the description
+    BOOST_CHECK(eventRecordPool[counterDescriptionOffset + uint32_t_size + counterDescription.size()] == '\0');
+}
+
+BOOST_AUTO_TEST_CASE(CreateInvalidEventRecordTest1)
+{
+    MockBufferManager mockBuffer(0);
+    SendCounterPacketTest sendCounterPacketTest(mockBuffer);
+
+    // Create a counter for testing
+    uint16_t counterUid = 7256;
+    uint16_t maxCounterUid = 132;
+    uint16_t deviceUid = 132;
+    uint16_t counterSetUid = 4497;
+    uint16_t counterClass = 1;
+    uint16_t counterInterpolation = 1;
+    double counterMultiplier = 1234.567f;
+    const std::string counterName = "some_invalid_counter £££"; // Invalid name
+    const std::string counterDescription = "a_counter_for_testing";
+    const std::string counterUnits = "Mrads2";
+    const CounterPtr counter = std::make_unique<Counter>(armnn::profiling::BACKEND_ID,
+                                                         counterUid,
+                                                         maxCounterUid,
+                                                         counterClass,
+                                                         counterInterpolation,
+                                                         counterMultiplier,
+                                                         counterName,
+                                                         counterDescription,
+                                                         counterUnits,
+                                                         deviceUid,
+                                                         counterSetUid);
+    ARMNN_ASSERT(counter);
 
-    void Release( unsigned int size) override {}
+    // Create an event record
+    SendCounterPacket::EventRecord eventRecord;
+    std::string errorMessage;
+    bool result = sendCounterPacketTest.CreateEventRecordTest(counter, eventRecord, errorMessage);
 
-private:
-    static const unsigned int m_BufferSize = 512;
-    unsigned char m_Buffer[m_BufferSize];
-};
+    BOOST_CHECK(!result);
+    BOOST_CHECK(!errorMessage.empty());
+    BOOST_CHECK(eventRecord.empty());
+}
+
+BOOST_AUTO_TEST_CASE(CreateInvalidEventRecordTest2)
+{
+    MockBufferManager mockBuffer(0);
+    SendCounterPacketTest sendCounterPacketTest(mockBuffer);
+
+    // Create a counter for testing
+    uint16_t counterUid = 7256;
+    uint16_t maxCounterUid = 132;
+    uint16_t deviceUid = 132;
+    uint16_t counterSetUid = 4497;
+    uint16_t counterClass = 1;
+    uint16_t counterInterpolation = 1;
+    double counterMultiplier = 1234.567f;
+    const std::string counterName = "some_invalid_counter";
+    const std::string counterDescription = "an invalid d€scription"; // Invalid description
+    const std::string counterUnits = "Mrads2";
+    const CounterPtr counter = std::make_unique<Counter>(armnn::profiling::BACKEND_ID,
+                                                         counterUid,
+                                                         maxCounterUid,
+                                                         counterClass,
+                                                         counterInterpolation,
+                                                         counterMultiplier,
+                                                         counterName,
+                                                         counterDescription,
+                                                         counterUnits,
+                                                         deviceUid,
+                                                         counterSetUid);
+    ARMNN_ASSERT(counter);
+
+    // Create an event record
+    SendCounterPacket::EventRecord eventRecord;
+    std::string errorMessage;
+    bool result = sendCounterPacketTest.CreateEventRecordTest(counter, eventRecord, errorMessage);
+
+    BOOST_CHECK(!result);
+    BOOST_CHECK(!errorMessage.empty());
+    BOOST_CHECK(eventRecord.empty());
+}
+
+BOOST_AUTO_TEST_CASE(CreateInvalidEventRecordTest3)
+{
+    MockBufferManager mockBuffer(0);
+    SendCounterPacketTest sendCounterPacketTest(mockBuffer);
+
+    // Create a counter for testing
+    uint16_t counterUid = 7256;
+    uint16_t maxCounterUid = 132;
+    uint16_t deviceUid = 132;
+    uint16_t counterSetUid = 4497;
+    uint16_t counterClass = 1;
+    uint16_t counterInterpolation = 1;
+    double counterMultiplier = 1234.567f;
+    const std::string counterName = "some_invalid_counter";
+    const std::string counterDescription = "a valid description";
+    const std::string counterUnits = "Mrad s2"; // Invalid units
+    const CounterPtr counter = std::make_unique<Counter>(armnn::profiling::BACKEND_ID,
+                                                         counterUid,
+                                                         maxCounterUid,
+                                                         counterClass,
+                                                         counterInterpolation,
+                                                         counterMultiplier,
+                                                         counterName,
+                                                         counterDescription,
+                                                         counterUnits,
+                                                         deviceUid,
+                                                         counterSetUid);
+    ARMNN_ASSERT(counter);
+
+    // Create an event record
+    SendCounterPacket::EventRecord eventRecord;
+    std::string errorMessage;
+    bool result = sendCounterPacketTest.CreateEventRecordTest(counter, eventRecord, errorMessage);
+
+    BOOST_CHECK(!result);
+    BOOST_CHECK(!errorMessage.empty());
+    BOOST_CHECK(eventRecord.empty());
+}
 
-class MockSendCounterPacket : public ISendCounterPacket
+BOOST_AUTO_TEST_CASE(CreateCategoryRecordTest)
 {
-public:
-    MockSendCounterPacket(IBufferWrapper& sendBuffer) : m_Buffer(sendBuffer) {}
+    MockBufferManager mockBuffer(0);
+    SendCounterPacketTest sendCounterPacketTest(mockBuffer);
+
+    // Create a category for testing
+    const std::string categoryName = "some_category";
+    const CategoryPtr category = std::make_unique<Category>(categoryName);
+    ARMNN_ASSERT(category);
+    category->m_Counters = { 11u, 23u, 5670u };
+
+    // Create a collection of counters
+    Counters counters;
+    counters.insert(std::make_pair<uint16_t, CounterPtr>(11,
+                                                         CounterPtr(new Counter(armnn::profiling::BACKEND_ID,
+                                                                                0,
+                                                                                11,
+                                                                                0,
+                                                                                0,
+                                                                                534.0003f,
+                                                                                "counter1",
+                                                                                "the first counter",
+                                                                                "millipi2",
+                                                                                0,
+                                                                                0))));
+    counters.insert(std::make_pair<uint16_t, CounterPtr>(23,
+                                                         CounterPtr(new Counter(armnn::profiling::BACKEND_ID,
+                                                                                1,
+                                                                                23,
+                                                                                0,
+                                                                                1,
+                                                                                534.0003f,
+                                                                                "this is counter 2",
+                                                                                "the second counter",
+                                                                                "",
+                                                                                0,
+                                                                                0))));
+    counters.insert(std::make_pair<uint16_t, CounterPtr>(5670,
+                                                         CounterPtr(new Counter(armnn::profiling::BACKEND_ID,
+                                                                                2,
+                                                                                5670,
+                                                                                0,
+                                                                                0,
+                                                                                534.0003f,
+                                                                                "and this is number 3",
+                                                                                "the third counter",
+                                                                                "blah_per_second",
+                                                                                0,
+                                                                                0))));
+    Counter* counter1 = counters.find(11)->second.get();
+    Counter* counter2 = counters.find(23)->second.get();
+    Counter* counter3 = counters.find(5670)->second.get();
+    ARMNN_ASSERT(counter1);
+    ARMNN_ASSERT(counter2);
+    ARMNN_ASSERT(counter3);
+    uint16_t categoryEventCount = boost::numeric_cast<uint16_t>(counters.size());
+
+    // Create a category record
+    SendCounterPacket::CategoryRecord categoryRecord;
+    std::string errorMessage;
+    bool result = sendCounterPacketTest.CreateCategoryRecordTest(category, counters, categoryRecord, errorMessage);
 
-    void SendStreamMetaDataPacket() override
+    BOOST_CHECK(result);
+    BOOST_CHECK(errorMessage.empty());
+    BOOST_CHECK(categoryRecord.size() == 79); // Size in words: header [3] + event pointer table [3] +
+                                              //                category name [5] + event records [68 = 22 + 20 + 26]
+
+    uint16_t categoryRecordWord1[]
     {
-        std::string message("SendStreamMetaDataPacket");
-        unsigned int reserved = 0;
-        unsigned char* buffer = m_Buffer.Reserve(1024, reserved);
-        memcpy(buffer, message.c_str(), static_cast<unsigned int>(message.size()) + 1);
+        static_cast<uint16_t>(categoryRecord[0] >> 16),
+        static_cast<uint16_t>(categoryRecord[0])
+    };
+    BOOST_CHECK(categoryRecordWord1[0] == categoryEventCount); // event_count
+    BOOST_CHECK(categoryRecordWord1[1] == 0); // reserved
+
+    size_t uint32_t_size = sizeof(uint32_t);
+
+    ARMNN_NO_CONVERSION_WARN_BEGIN
+    uint32_t categoryRecordBlockSize = 3u * uint32_t_size;
+    uint32_t eventPointerTableOffset = categoryRecordBlockSize; // The event pointer table is the first item in pool
+    uint32_t categoryNameOffset = eventPointerTableOffset + // Event pointer table offset
+                                  categoryEventCount * uint32_t_size; // The size of the event pointer table
+    ARMNN_NO_CONVERSION_WARN_END
+
+    BOOST_CHECK(categoryRecord[1] == eventPointerTableOffset); // event_pointer_table_offset
+    BOOST_CHECK(categoryRecord[2] == categoryNameOffset); // name_offset
+    // Offsets are relative to the start of the category record
+    auto categoryRecordPool = reinterpret_cast<unsigned char*>(categoryRecord.data());
+
+    // The event pointer table
+    uint32_t eventRecord0Offset = categoryRecordPool[eventPointerTableOffset + 0 * uint32_t_size];
+    uint32_t eventRecord1Offset = categoryRecordPool[eventPointerTableOffset + 1 * uint32_t_size];
+    uint32_t eventRecord2Offset = categoryRecordPool[eventPointerTableOffset + 2 * uint32_t_size];
+    BOOST_CHECK(eventRecord0Offset == 32);
+    BOOST_CHECK(eventRecord1Offset == 120);
+    BOOST_CHECK(eventRecord2Offset == 200);
+
+    // The length of the SWTrace namestring (name)
+    BOOST_CHECK(categoryRecordPool[categoryNameOffset] == categoryName.size() + 1);
+    // The category name
+    BOOST_CHECK(std::memcmp(categoryRecordPool +
+                            categoryNameOffset + // Offset
+                            uint32_t_size, // The length of the name
+                            categoryName.data(),
+                            categoryName.size()) == 0); // name
+    // The null-terminator at the end of the name
+    BOOST_CHECK(categoryRecordPool[categoryNameOffset + uint32_t_size + categoryName.size()] == '\0');
+
+    // For brevity, checking only the UIDs, max counter UIDs and names of the counters in the event records,
+    // as the event records already have a number of unit tests dedicated to them
+
+    // Counter1 UID and max counter UID
+    uint16_t eventRecord0Word0[2] = { 0u, 0u };
+    std::memcpy(eventRecord0Word0, categoryRecordPool + categoryRecordBlockSize + eventRecord0Offset,
+                sizeof(eventRecord0Word0));
+    BOOST_CHECK(eventRecord0Word0[0] == counter1->m_Uid);
+    BOOST_CHECK(eventRecord0Word0[1] == counter1->m_MaxCounterUid);
+
+    // Counter1 name
+    uint32_t counter1NameOffset = 0;
+    std::memcpy(&counter1NameOffset, categoryRecordPool  + eventRecord0Offset + 5u * uint32_t_size, uint32_t_size);
+    BOOST_CHECK(counter1NameOffset == 0);
+    // The length of the SWTrace string (name)
+    BOOST_CHECK(categoryRecordPool[eventRecord0Offset +       // Offset to the event record
+                                   categoryRecordBlockSize  + // Offset to the end of the category record block
+                                   8u * uint32_t_size +       // Offset to the event record pool
+                                   counter1NameOffset         // Offset to the name of the counter
+                                  ] == counter1->m_Name.size() + 1); // The length of the name including the
+                                                                     // null-terminator
+    // The counter1 name
+    BOOST_CHECK(std::memcmp(categoryRecordPool +      // The beginning of the category pool
+                            categoryRecordBlockSize + // Offset to the end of the category record block
+                            eventRecord0Offset +      // Offset to the event record
+                            8u * uint32_t_size +      // Offset to the event record pool
+                            counter1NameOffset +      // Offset to the name of the counter
+                            uint32_t_size,            // The length of the name
+                            counter1->m_Name.data(),
+                            counter1->m_Name.size()) == 0); // name
+    // The null-terminator at the end of the counter1 name
+    BOOST_CHECK(categoryRecordPool[eventRecord0Offset +      // Offset to the event record
+                                   categoryRecordBlockSize + // Offset to the end of the category record block
+                                   8u * uint32_t_size +      // Offset to the event record pool
+                                   counter1NameOffset +      // Offset to the name of the counter
+                                   uint32_t_size +           // The length of the name
+                                   counter1->m_Name.size()   // The name of the counter
+                                   ] == '\0');
+
+    // Counter2 name
+    uint32_t counter2NameOffset = 0;
+    std::memcpy(&counter2NameOffset, categoryRecordPool +
+                                     categoryRecordBlockSize +
+                                     eventRecord1Offset +
+                                     5u * uint32_t_size,
+                                     uint32_t_size);
+    BOOST_CHECK(counter2NameOffset == 8u * uint32_t_size );
+    // The length of the SWTrace string (name)
+
+    BOOST_CHECK(categoryRecordPool[eventRecord1Offset + // Offset to the event record
+                                   categoryRecordBlockSize +
+                                   counter2NameOffset   // Offset to the name of the counter
+                                  ] == counter2->m_Name.size() + 1); // The length of the name including the
+                                                                     // null-terminator
+    // The counter2 name
+    BOOST_CHECK(std::memcmp(categoryRecordPool +      // The beginning of the category pool
+                            categoryRecordBlockSize + // Offset to the end of the category record block
+                            eventRecord1Offset +      // Offset to the event record
+                            counter2NameOffset +      // Offset to the name of the counter
+                            uint32_t_size,            // The length of the name
+                            counter2->m_Name.data(),
+                            counter2->m_Name.size()) == 0); // name
+
+
+    // The null-terminator at the end of the counter2 name
+    BOOST_CHECK(categoryRecordPool[eventRecord1Offset +      // Offset to the event record
+                                   categoryRecordBlockSize + // Offset to the end of the category record block
+                                   counter2NameOffset +      // Offset to the name of the counter
+                                   uint32_t_size +           // The length of the name
+                                   counter2->m_Name.size()   // The name of the counter
+                                   ] == '\0');
+
+    // Counter3 name
+    uint32_t counter3NameOffset = 0;
+    std::memcpy(&counter3NameOffset, categoryRecordPool + eventRecord2Offset + 5u * uint32_t_size, uint32_t_size);
+    BOOST_CHECK(counter3NameOffset == 0);
+    // The length of the SWTrace string (name)
+    BOOST_CHECK(categoryRecordPool[eventRecord2Offset + // Offset to the event record
+                                   categoryRecordBlockSize +
+                                   8u * uint32_t_size + // Offset to the event record pool
+                                   counter3NameOffset   // Offset to the name of the counter
+                                  ] == counter3->m_Name.size() + 1); // The length of the name including the
+                                                                     // null-terminator
+    // The counter3 name
+    BOOST_CHECK(std::memcmp(categoryRecordPool + // The beginning of the category pool
+                            categoryRecordBlockSize +
+                            eventRecord2Offset + // Offset to the event record
+                            8u * uint32_t_size + // Offset to the event record pool
+                            counter3NameOffset + // Offset to the name of the counter
+                            uint32_t_size,       // The length of the name
+                            counter3->m_Name.data(),
+                            counter3->m_Name.size()) == 0); // name
+    // The null-terminator at the end of the counter3 name
+    BOOST_CHECK(categoryRecordPool[eventRecord2Offset +    // Offset to the event record
+                                   categoryRecordBlockSize +
+                                   8u * uint32_t_size +    // Offset to the event record pool
+                                   counter3NameOffset +    // Offset to the name of the counter
+                                   uint32_t_size +         // The length of the name
+                                   counter3->m_Name.size() // The name of the counter
+                                   ] == '\0');
+}
+
+BOOST_AUTO_TEST_CASE(CreateInvalidCategoryRecordTest1)
+{
+    MockBufferManager mockBuffer(0);
+    SendCounterPacketTest sendCounterPacketTest(mockBuffer);
+
+    // Create a category for testing
+    const std::string categoryName = "some invalid category";
+    const CategoryPtr category = std::make_unique<Category>(categoryName);
+    BOOST_CHECK(category);
+
+    // Create a category record
+    Counters counters;
+    SendCounterPacket::CategoryRecord categoryRecord;
+    std::string errorMessage;
+    bool result = sendCounterPacketTest.CreateCategoryRecordTest(category, counters, categoryRecord, errorMessage);
+
+    BOOST_CHECK(!result);
+    BOOST_CHECK(!errorMessage.empty());
+    BOOST_CHECK(categoryRecord.empty());
+}
+
+BOOST_AUTO_TEST_CASE(CreateInvalidCategoryRecordTest2)
+{
+    MockBufferManager mockBuffer(0);
+    SendCounterPacketTest sendCounterPacketTest(mockBuffer);
+
+    // Create a category for testing
+    const std::string categoryName = "some_category";
+    const CategoryPtr category = std::make_unique<Category>(categoryName);
+    BOOST_CHECK(category);
+    category->m_Counters = { 11u, 23u, 5670u };
+
+    // Create a collection of counters
+    Counters counters;
+    counters.insert(std::make_pair<uint16_t, CounterPtr>(11,
+                                                         CounterPtr(new Counter(armnn::profiling::BACKEND_ID,
+                                                                                11,
+                                                                                1234,
+                                                                                0,
+                                                                                1,
+                                                                                534.0003f,
+                                                                                "count€r1", // Invalid name
+                                                                                "the first counter",
+                                                                                "millipi2",
+                                                                                0,
+                                                                                0))));
+
+    Counter* counter1 = counters.find(11)->second.get();
+    BOOST_CHECK(counter1);
+
+    // Create a category record
+    SendCounterPacket::CategoryRecord categoryRecord;
+    std::string errorMessage;
+    bool result = sendCounterPacketTest.CreateCategoryRecordTest(category, counters, categoryRecord, errorMessage);
+
+    BOOST_CHECK(!result);
+    BOOST_CHECK(!errorMessage.empty());
+    BOOST_CHECK(categoryRecord.empty());
+}
+
+BOOST_AUTO_TEST_CASE(SendCounterDirectoryPacketTest1)
+{
+    // The counter directory used for testing
+    CounterDirectory counterDirectory;
+
+    // Register a device
+    const std::string device1Name = "device1";
+    const Device* device1 = nullptr;
+    BOOST_CHECK_NO_THROW(device1 = counterDirectory.RegisterDevice(device1Name, 3));
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 1);
+    BOOST_CHECK(device1);
+
+    // Register a device
+    const std::string device2Name = "device2";
+    const Device* device2 = nullptr;
+    BOOST_CHECK_NO_THROW(device2 = counterDirectory.RegisterDevice(device2Name));
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 2);
+    BOOST_CHECK(device2);
+
+    // Buffer with not enough space
+    MockBufferManager mockBuffer(10);
+    SendCounterPacket sendCounterPacket(mockBuffer);
+    BOOST_CHECK_THROW(sendCounterPacket.SendCounterDirectoryPacket(counterDirectory),
+                      armnn::profiling::BufferExhaustion);
+}
+
+BOOST_AUTO_TEST_CASE(SendCounterDirectoryPacketTest2)
+{
+    // The counter directory used for testing
+    CounterDirectory counterDirectory;
+
+    // Register a device
+    const std::string device1Name = "device1";
+    const Device* device1 = nullptr;
+    BOOST_CHECK_NO_THROW(device1 = counterDirectory.RegisterDevice(device1Name, 3));
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 1);
+    BOOST_CHECK(device1);
+
+    // Register a device
+    const std::string device2Name = "device2";
+    const Device* device2 = nullptr;
+    BOOST_CHECK_NO_THROW(device2 = counterDirectory.RegisterDevice(device2Name));
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 2);
+    BOOST_CHECK(device2);
+
+    // Register a counter set
+    const std::string counterSet1Name = "counterset1";
+    const CounterSet* counterSet1 = nullptr;
+    BOOST_CHECK_NO_THROW(counterSet1 = counterDirectory.RegisterCounterSet(counterSet1Name));
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 1);
+    BOOST_CHECK(counterSet1);
+
+    // Register a category associated to "device1" and "counterset1"
+    const std::string category1Name = "category1";
+    const Category* category1 = nullptr;
+    BOOST_CHECK_NO_THROW(category1 = counterDirectory.RegisterCategory(category1Name));
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 1);
+    BOOST_CHECK(category1);
+
+    // Register a category not associated to "device2" but no counter set
+    const std::string category2Name = "category2";
+    const Category* category2 = nullptr;
+    BOOST_CHECK_NO_THROW(category2 = counterDirectory.RegisterCategory(category2Name));
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 2);
+    BOOST_CHECK(category2);
+
+    uint16_t numberOfCores = 4;
+
+    // Register a counter associated to "category1"
+    const Counter* counter1 = nullptr;
+    BOOST_CHECK_NO_THROW(counter1 = counterDirectory.RegisterCounter(armnn::profiling::BACKEND_ID,
+                                                                     0,
+                                                                     category1Name,
+                                                                     0,
+                                                                     1,
+                                                                     123.45f,
+                                                                     "counter1",
+                                                                     "counter1description",
+                                                                     std::string("counter1units"),
+                                                                     numberOfCores));
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 4);
+    BOOST_CHECK(counter1);
+
+    // Register a counter associated to "category1"
+    const Counter* counter2 = nullptr;
+    BOOST_CHECK_NO_THROW(counter2 = counterDirectory.RegisterCounter(armnn::profiling::BACKEND_ID,
+                                                                     4,
+                                                                     category1Name,
+                                                                     1,
+                                                                     0,
+                                                                     330.1245656765f,
+                                                                     "counter2",
+                                                                     "counter2description",
+                                                                     std::string("counter2units"),
+                                                                     armnn::EmptyOptional(),
+                                                                     device2->m_Uid,
+                                                                     0));
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 5);
+    BOOST_CHECK(counter2);
+
+    // Register a counter associated to "category2"
+    const Counter* counter3 = nullptr;
+    BOOST_CHECK_NO_THROW(counter3 = counterDirectory.RegisterCounter(armnn::profiling::BACKEND_ID,
+                                                                     5,
+                                                                     category2Name,
+                                                                     1,
+                                                                     1,
+                                                                     0.0000045399f,
+                                                                     "counter3",
+                                                                     "counter3description",
+                                                                     armnn::EmptyOptional(),
+                                                                     numberOfCores,
+                                                                     device2->m_Uid,
+                                                                     counterSet1->m_Uid));
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 9);
+    BOOST_CHECK(counter3);
+
+    // Buffer with enough space
+    MockBufferManager mockBuffer(1024);
+    SendCounterPacket sendCounterPacket(mockBuffer);
+    BOOST_CHECK_NO_THROW(sendCounterPacket.SendCounterDirectoryPacket(counterDirectory));
+
+    // Get the readable buffer
+    auto readBuffer = mockBuffer.GetReadableBuffer();
+
+    // Check the packet header
+    const uint32_t packetHeaderWord0 = ReadUint32(readBuffer, 0);
+    const uint32_t packetHeaderWord1 = ReadUint32(readBuffer, 4);
+    BOOST_TEST(((packetHeaderWord0 >> 26) & 0x3F) == 0);  // packet_family
+    BOOST_TEST(((packetHeaderWord0 >> 16) & 0x3FF) == 2); // packet_id
+    BOOST_TEST(packetHeaderWord1 == 432);                 // data_length
+
+    // Check the body header
+    const uint32_t bodyHeaderWord0 = ReadUint32(readBuffer,  8);
+    const uint32_t bodyHeaderWord1 = ReadUint32(readBuffer, 12);
+    const uint32_t bodyHeaderWord2 = ReadUint32(readBuffer, 16);
+    const uint32_t bodyHeaderWord3 = ReadUint32(readBuffer, 20);
+    const uint32_t bodyHeaderWord4 = ReadUint32(readBuffer, 24);
+    const uint32_t bodyHeaderWord5 = ReadUint32(readBuffer, 28);
+    const uint16_t deviceRecordCount     = static_cast<uint16_t>(bodyHeaderWord0 >> 16);
+    const uint16_t counterSetRecordCount = static_cast<uint16_t>(bodyHeaderWord2 >> 16);
+    const uint16_t categoryRecordCount   = static_cast<uint16_t>(bodyHeaderWord4 >> 16);
+    BOOST_TEST(deviceRecordCount == 2);                      // device_records_count
+    BOOST_TEST(bodyHeaderWord1 == bodyHeaderSize * 4);           // device_records_pointer_table_offset
+    BOOST_TEST(counterSetRecordCount == 1);                  // counter_set_count
+    BOOST_TEST(bodyHeaderWord3 == 8 + bodyHeaderSize * 4);       // counter_set_pointer_table_offset
+    BOOST_TEST(categoryRecordCount == 2);                    // categories_count
+    BOOST_TEST(bodyHeaderWord5 == 12 + bodyHeaderSize * 4);      // categories_pointer_table_offset
+
+    // Check the device records pointer table
+    const uint32_t deviceRecordOffset0 = ReadUint32(readBuffer, 32);
+    const uint32_t deviceRecordOffset1 = ReadUint32(readBuffer, 36);
+    BOOST_TEST(deviceRecordOffset0 == 20); // Device record offset for "device1"
+    BOOST_TEST(deviceRecordOffset1 == 40); // Device record offset for "device2"
+
+    // Check the counter set pointer table
+    const uint32_t counterSetRecordOffset0 = ReadUint32(readBuffer, 40);
+    BOOST_TEST(counterSetRecordOffset0 == 52); // Counter set record offset for "counterset1"
+
+    // Check the category pointer table
+    const uint32_t categoryRecordOffset0 = ReadUint32(readBuffer, 44);
+    const uint32_t categoryRecordOffset1 = ReadUint32(readBuffer, 48);
+    BOOST_TEST(categoryRecordOffset0 ==  72); // Category record offset for "category1"
+    BOOST_TEST(categoryRecordOffset1 == 176); // Category record offset for "category2"
+
+    // Get the device record pool offset
+    const uint32_t uint32_t_size = sizeof(uint32_t);
+    const uint32_t packetHeaderSize = 2u * uint32_t_size;
+
+    // Device record structure/collection used for testing
+    struct DeviceRecord
+    {
+        uint16_t    uid;
+        uint16_t    cores;
+        uint32_t    name_offset;
+        uint32_t    name_length;
+        std::string name;
+    };
+    std::vector<DeviceRecord> deviceRecords;
+    const uint32_t deviceRecordsPointerTableOffset = packetHeaderSize +
+                                                     bodyHeaderWord1;     // device_records_pointer_table_offset
+
+    const unsigned char* readData = readBuffer->GetReadableData();
+
+    uint32_t offset = 0;
+    std::vector<uint32_t> data(800);
+
+    for (uint32_t i = 0; i < 800; i+=uint32_t_size)
+    {
+        data[i] = ReadUint32(readBuffer, offset);
+        offset += uint32_t_size;
     }
 
-    void SendCounterDirectoryPacket(const Category& category, const std::vector<Counter>& counters) override
+    std::vector<uint32_t> deviceRecordOffsets(deviceRecordCount);
+     offset = deviceRecordsPointerTableOffset;
+    for (uint32_t i = 0; i < deviceRecordCount; ++i)
     {
-        std::string message("SendCounterDirectoryPacket");
-        unsigned int reserved = 0;
-        unsigned char* buffer = m_Buffer.Reserve(1024, reserved);
-        memcpy(buffer, message.c_str(), static_cast<unsigned int>(message.size()) + 1);
+        // deviceRecordOffset is relative to the start of the deviceRecordsPointerTable
+        deviceRecordOffsets[i] = ReadUint32(readBuffer, offset) + deviceRecordsPointerTableOffset;
+        offset += uint32_t_size;
     }
 
-    void SendPeriodicCounterCapturePacket(uint64_t timestamp, const std::vector<uint32_t>& counterValues,
-                                          const std::vector<uint16_t>& counterUids) override
+    for (uint32_t i = 0; i < deviceRecordCount; i++)
     {
-        std::string message("SendPeriodicCounterCapturePacket");
-        unsigned int reserved = 0;
-        unsigned char* buffer = m_Buffer.Reserve(1024, reserved);
-        memcpy(buffer, message.c_str(), static_cast<unsigned int>(message.size()) + 1);
+        // Collect the data for the device record
+        const uint32_t deviceRecordWord0 = ReadUint32(readBuffer, deviceRecordOffsets[i] + 0 * uint32_t_size);
+        const uint32_t deviceRecordWord1 = ReadUint32(readBuffer, deviceRecordOffsets[i] + 1 * uint32_t_size);
+        DeviceRecord deviceRecord;
+        deviceRecord.uid = static_cast<uint16_t>(deviceRecordWord0 >> 16); // uid
+        deviceRecord.cores = static_cast<uint16_t>(deviceRecordWord0);     // cores
+        deviceRecord.name_offset = deviceRecordWord1;                      // name_offset
+
+        uint32_t deviceRecordPoolOffset = deviceRecordOffsets[i] +                  // Packet body offset
+                                          deviceRecord.name_offset; // Device name offset
+        uint32_t deviceRecordNameLength = ReadUint32(readBuffer, deviceRecordPoolOffset);
+        deviceRecord.name_length = deviceRecordNameLength; // name_length
+        unsigned char deviceRecordNameNullTerminator = // name null-terminator
+                ReadUint8(readBuffer, deviceRecordPoolOffset + uint32_t_size + deviceRecordNameLength - 1);
+        BOOST_CHECK(deviceRecordNameNullTerminator == '\0');
+        std::vector<unsigned char> deviceRecordNameBuffer(deviceRecord.name_length - 1);
+        std::memcpy(deviceRecordNameBuffer.data(),
+                    readData + deviceRecordPoolOffset + uint32_t_size, deviceRecordNameBuffer.size());
+        deviceRecord.name.assign(deviceRecordNameBuffer.begin(), deviceRecordNameBuffer.end()); // name
+
+        deviceRecords.push_back(deviceRecord);
     }
 
-    void SendPeriodicCounterSelectionPacket(uint32_t capturePeriod,
-                                            const std::vector<uint16_t>& selectedCounterIds) override
+    // Check that the device records are correct
+    BOOST_CHECK(deviceRecords.size() == 2);
+    for (const DeviceRecord& deviceRecord : deviceRecords)
     {
-        std::string message("SendPeriodicCounterSelectionPacket");
-        unsigned int reserved = 0;
-        unsigned char* buffer = m_Buffer.Reserve(1024, reserved);
-        memcpy(buffer, message.c_str(), static_cast<unsigned int>(message.size()) + 1);
-        m_Buffer.Commit(reserved);
+        const Device* device = counterDirectory.GetDevice(deviceRecord.uid);
+        BOOST_CHECK(device);
+        BOOST_CHECK(device->m_Uid   == deviceRecord.uid);
+        BOOST_CHECK(device->m_Cores == deviceRecord.cores);
+        BOOST_CHECK(device->m_Name  == deviceRecord.name);
     }
 
-    void SetReadyToRead() override
-    {}
 
-private:
-    IBufferWrapper& m_Buffer;
-};
+    // Counter set record structure/collection used for testing
+    struct CounterSetRecord
+    {
+        uint16_t    uid;
+        uint16_t    count;
+        uint32_t    name_offset;
+        uint32_t    name_length;
+        std::string name;
+    };
+    std::vector<CounterSetRecord> counterSetRecords;
+    const uint32_t counterSetRecordsPointerTableOffset = 2u * uint32_t_size + // packet_header
+                                                         bodyHeaderWord3;     // counter_set_pointer_table_offset
 
-BOOST_AUTO_TEST_CASE(MockSendCounterPacketTest)
+    offset = counterSetRecordsPointerTableOffset;
+    std::vector<uint32_t> counterSetRecordOffsets(counterSetRecordCount);
+
+    for (uint32_t i = 0; i < counterSetRecordCount; ++i)
+    {
+        // counterSetRecordOffset is relative to the start of the dcounterSetRecordsPointerTable
+        counterSetRecordOffsets[i] = ReadUint32(readBuffer, offset) + counterSetRecordsPointerTableOffset;
+        offset += uint32_t_size;
+    }
+
+    for (uint32_t i = 0; i < counterSetRecordCount; i++)
+    {
+        // Collect the data for the counter set record
+        const uint32_t counterSetRecordWord0 = ReadUint32(readBuffer, counterSetRecordOffsets[i] + 0 * uint32_t_size);
+        const uint32_t counterSetRecordWord1 = ReadUint32(readBuffer, counterSetRecordOffsets[i] + 1 * uint32_t_size);
+        CounterSetRecord counterSetRecord;
+        counterSetRecord.uid = static_cast<uint16_t>(counterSetRecordWord0 >> 16); // uid
+        counterSetRecord.count = static_cast<uint16_t>(counterSetRecordWord0);     // count
+        counterSetRecord.name_offset = counterSetRecordWord1;                      // name_offset
+
+        uint32_t counterSetRecordPoolOffset = counterSetRecordOffsets[i]  +                 // Packet body offset
+                                              counterSetRecord.name_offset; // Counter set name offset
+        uint32_t counterSetRecordNameLength = ReadUint32(readBuffer, counterSetRecordPoolOffset);
+        counterSetRecord.name_length = counterSetRecordNameLength; // name_length
+        unsigned char counterSetRecordNameNullTerminator = // name null-terminator
+                ReadUint8(readBuffer, counterSetRecordPoolOffset + uint32_t_size + counterSetRecordNameLength - 1);
+        BOOST_CHECK(counterSetRecordNameNullTerminator == '\0');
+        std::vector<unsigned char> counterSetRecordNameBuffer(counterSetRecord.name_length - 1);
+        std::memcpy(counterSetRecordNameBuffer.data(),
+                    readData + counterSetRecordPoolOffset + uint32_t_size, counterSetRecordNameBuffer.size());
+        counterSetRecord.name.assign(counterSetRecordNameBuffer.begin(), counterSetRecordNameBuffer.end()); // name
+
+        counterSetRecords.push_back(counterSetRecord);
+    }
+
+    // Check that the counter set records are correct
+    BOOST_CHECK(counterSetRecords.size() == 1);
+    for (const CounterSetRecord& counterSetRecord : counterSetRecords)
+    {
+        const CounterSet* counterSet = counterDirectory.GetCounterSet(counterSetRecord.uid);
+        BOOST_CHECK(counterSet);
+        BOOST_CHECK(counterSet->m_Uid   == counterSetRecord.uid);
+        BOOST_CHECK(counterSet->m_Count == counterSetRecord.count);
+        BOOST_CHECK(counterSet->m_Name  == counterSetRecord.name);
+    }
+
+    // Event record structure/collection used for testing
+    struct EventRecord
+    {
+        uint16_t    counter_uid;
+        uint16_t    max_counter_uid;
+        uint16_t    device;
+        uint16_t    counter_set;
+        uint16_t    counter_class;
+        uint16_t    interpolation;
+        double      multiplier;
+        uint32_t    name_offset;
+        uint32_t    name_length;
+        std::string name;
+        uint32_t    description_offset;
+        uint32_t    description_length;
+        std::string description;
+        uint32_t    units_offset;
+        uint32_t    units_length;
+        std::string units;
+    };
+    // Category record structure/collection used for testing
+    struct CategoryRecord
+    {
+        uint16_t                 event_count;
+        uint32_t                 event_pointer_table_offset;
+        uint32_t                 name_offset;
+        uint32_t                 name_length;
+        std::string              name;
+        std::vector<uint32_t>    event_pointer_table;
+        std::vector<EventRecord> event_records;
+    };
+    std::vector<CategoryRecord> categoryRecords;
+    const uint32_t categoryRecordsPointerTableOffset = 2u * uint32_t_size + // packet_header
+                                                       bodyHeaderWord5;    // categories_pointer_table_offset
+
+    offset = categoryRecordsPointerTableOffset;
+    std::vector<uint32_t> categoryRecordOffsets(categoryRecordCount);
+    for (uint32_t i = 0; i < categoryRecordCount; ++i)
+    {
+        // categoryRecordOffset is relative to the start of the categoryRecordsPointerTable
+        categoryRecordOffsets[i] = ReadUint32(readBuffer, offset) + categoryRecordsPointerTableOffset;
+        offset += uint32_t_size;
+    }
+
+    for (uint32_t i = 0; i < categoryRecordCount; i++)
+    {
+        // Collect the data for the category record
+        const uint32_t categoryRecordWord1 = ReadUint32(readBuffer, categoryRecordOffsets[i] + 0 * uint32_t_size);
+        const uint32_t categoryRecordWord2 = ReadUint32(readBuffer, categoryRecordOffsets[i] + 1 * uint32_t_size);
+        const uint32_t categoryRecordWord3 = ReadUint32(readBuffer, categoryRecordOffsets[i] + 2 * uint32_t_size);
+        CategoryRecord categoryRecord;
+        categoryRecord.event_count = static_cast<uint16_t>(categoryRecordWord1 >> 16); // event_count
+        categoryRecord.event_pointer_table_offset = categoryRecordWord2;               // event_pointer_table_offset
+        categoryRecord.name_offset = categoryRecordWord3;                              // name_offset
+
+        uint32_t categoryRecordNameLength = ReadUint32(readBuffer,
+                                                       categoryRecordOffsets[i] + categoryRecord.name_offset);
+        categoryRecord.name_length = categoryRecordNameLength; // name_length
+        unsigned char categoryRecordNameNullTerminator =
+                ReadUint8(readBuffer,
+                          categoryRecordOffsets[i] +
+                          categoryRecord.name_offset +
+                          uint32_t_size +
+                          categoryRecordNameLength - 1); // name null-terminator
+        BOOST_CHECK(categoryRecordNameNullTerminator == '\0');
+        std::vector<unsigned char> categoryRecordNameBuffer(categoryRecord.name_length - 1);
+        std::memcpy(categoryRecordNameBuffer.data(),
+                    readData +
+                    categoryRecordOffsets[i] +
+                    categoryRecord.name_offset +
+                    uint32_t_size,
+                    categoryRecordNameBuffer.size());
+        categoryRecord.name.assign(categoryRecordNameBuffer.begin(), categoryRecordNameBuffer.end()); // name
+
+        categoryRecord.event_pointer_table.resize(categoryRecord.event_count);
+        offset = categoryRecordOffsets[i] + categoryRecord.event_pointer_table_offset;
+        for (uint32_t eventOffsetIndex = 0; eventOffsetIndex < categoryRecord.event_count; ++eventOffsetIndex)
+        {
+            // eventRecordOffset is relative to the start of the event pointer table
+            categoryRecord.event_pointer_table[eventOffsetIndex] = ReadUint32(readBuffer, offset) +
+                                                                   categoryRecordOffsets[i] +
+                                                                   categoryRecord.event_pointer_table_offset;
+            offset += uint32_t_size;
+        }
+
+        for (uint32_t eventIndex = 0; eventIndex < categoryRecord.event_count; eventIndex++)
+        {
+            const uint32_t eventOffset = categoryRecord.event_pointer_table[eventIndex];
+            // Collect the data for the event record
+            const uint32_t eventRecordWord0  = ReadUint32(readBuffer, eventOffset + 0 * uint32_t_size);
+            const uint32_t eventRecordWord1  = ReadUint32(readBuffer, eventOffset + 1 * uint32_t_size);
+            const uint32_t eventRecordWord2  = ReadUint32(readBuffer, eventOffset + 2 * uint32_t_size);
+            const uint64_t eventRecordWord34 = ReadUint64(readBuffer, eventOffset + 3 * uint32_t_size);
+            const uint32_t eventRecordWord5  = ReadUint32(readBuffer, eventOffset + 5 * uint32_t_size);
+            const uint32_t eventRecordWord6  = ReadUint32(readBuffer, eventOffset + 6 * uint32_t_size);
+            const uint32_t eventRecordWord7  = ReadUint32(readBuffer, eventOffset + 7 * uint32_t_size);
+
+            EventRecord eventRecord;
+            eventRecord.counter_uid = static_cast<uint16_t>(eventRecordWord0);                     // counter_uid
+            eventRecord.max_counter_uid = static_cast<uint16_t>(eventRecordWord0 >> 16);           // max_counter_uid
+            eventRecord.device = static_cast<uint16_t>(eventRecordWord1 >> 16);                    // device
+            eventRecord.counter_set = static_cast<uint16_t>(eventRecordWord1);                     // counter_set
+            eventRecord.counter_class = static_cast<uint16_t>(eventRecordWord2 >> 16);             // class
+            eventRecord.interpolation = static_cast<uint16_t>(eventRecordWord2);                   // interpolation
+            std::memcpy(&eventRecord.multiplier, &eventRecordWord34, sizeof(eventRecord.multiplier)); // multiplier
+            eventRecord.name_offset = static_cast<uint32_t>(eventRecordWord5);                     // name_offset
+            eventRecord.description_offset = static_cast<uint32_t>(eventRecordWord6);              // description_offset
+            eventRecord.units_offset = static_cast<uint32_t>(eventRecordWord7);                    // units_offset
+
+            uint32_t eventRecordNameLength = ReadUint32(readBuffer, eventOffset + eventRecord.name_offset);
+            eventRecord.name_length = eventRecordNameLength; // name_length
+            unsigned char eventRecordNameNullTerminator =
+                    ReadUint8(readBuffer,
+                              eventOffset +
+                              eventRecord.name_offset +
+                              uint32_t_size +
+                              eventRecordNameLength - 1); // name null-terminator
+            BOOST_CHECK(eventRecordNameNullTerminator == '\0');
+            std::vector<unsigned char> eventRecordNameBuffer(eventRecord.name_length - 1);
+            std::memcpy(eventRecordNameBuffer.data(),
+                        readData +
+                        eventOffset +
+                        eventRecord.name_offset +
+                        uint32_t_size,
+                        eventRecordNameBuffer.size());
+            eventRecord.name.assign(eventRecordNameBuffer.begin(), eventRecordNameBuffer.end()); // name
+
+            uint32_t eventRecordDescriptionLength = ReadUint32(readBuffer,
+                                                               eventOffset + eventRecord.description_offset);
+            eventRecord.description_length = eventRecordDescriptionLength; // description_length
+            unsigned char eventRecordDescriptionNullTerminator =
+                    ReadUint8(readBuffer,
+                              eventOffset +
+                              eventRecord.description_offset +
+                              uint32_t_size +
+                              eventRecordDescriptionLength - 1); // description null-terminator
+            BOOST_CHECK(eventRecordDescriptionNullTerminator == '\0');
+            std::vector<unsigned char> eventRecordDescriptionBuffer(eventRecord.description_length - 1);
+            std::memcpy(eventRecordDescriptionBuffer.data(),
+                        readData +
+                        eventOffset +
+                        eventRecord.description_offset +
+                        uint32_t_size,
+                        eventRecordDescriptionBuffer.size());
+            eventRecord.description.assign(eventRecordDescriptionBuffer.begin(),
+                                           eventRecordDescriptionBuffer.end()); // description
+
+            if (eventRecord.units_offset > 0)
+            {
+                uint32_t eventRecordUnitsLength = ReadUint32(readBuffer,
+                                                             eventOffset + eventRecord.units_offset);
+                eventRecord.units_length = eventRecordUnitsLength; // units_length
+                unsigned char eventRecordUnitsNullTerminator =
+                        ReadUint8(readBuffer,
+                                  eventOffset +
+                                  eventRecord.units_offset +
+                                  uint32_t_size +
+                                  eventRecordUnitsLength - 1); // units null-terminator
+                BOOST_CHECK(eventRecordUnitsNullTerminator == '\0');
+                std::vector<unsigned char> eventRecordUnitsBuffer(eventRecord.units_length - 1);
+                std::memcpy(eventRecordUnitsBuffer.data(),
+                            readData +
+                            eventOffset +
+                            eventRecord.units_offset +
+                            uint32_t_size,
+                            eventRecordUnitsBuffer.size());
+                eventRecord.units.assign(eventRecordUnitsBuffer.begin(), eventRecordUnitsBuffer.end()); // units
+            }
+
+            categoryRecord.event_records.push_back(eventRecord);
+        }
+
+        categoryRecords.push_back(categoryRecord);
+    }
+
+    // Check that the category records are correct
+    BOOST_CHECK(categoryRecords.size() == 2);
+    for (const CategoryRecord& categoryRecord : categoryRecords)
+    {
+        const Category* category = counterDirectory.GetCategory(categoryRecord.name);
+        BOOST_CHECK(category);
+        BOOST_CHECK(category->m_Name == categoryRecord.name);
+        BOOST_CHECK(category->m_Counters.size() == categoryRecord.event_count + static_cast<size_t>(numberOfCores) -1);
+        BOOST_CHECK(category->m_Counters.size() == categoryRecord.event_count + static_cast<size_t>(numberOfCores) -1);
+
+        // Check that the event records are correct
+        for (const EventRecord& eventRecord : categoryRecord.event_records)
+        {
+            const Counter* counter = counterDirectory.GetCounter(eventRecord.counter_uid);
+            BOOST_CHECK(counter);
+            BOOST_CHECK(counter->m_MaxCounterUid == eventRecord.max_counter_uid);
+            BOOST_CHECK(counter->m_DeviceUid == eventRecord.device);
+            BOOST_CHECK(counter->m_CounterSetUid == eventRecord.counter_set);
+            BOOST_CHECK(counter->m_Class == eventRecord.counter_class);
+            BOOST_CHECK(counter->m_Interpolation == eventRecord.interpolation);
+            BOOST_CHECK(counter->m_Multiplier == eventRecord.multiplier);
+            BOOST_CHECK(counter->m_Name == eventRecord.name);
+            BOOST_CHECK(counter->m_Description == eventRecord.description);
+            BOOST_CHECK(counter->m_Units == eventRecord.units);
+        }
+    }
+}
+
+BOOST_AUTO_TEST_CASE(SendCounterDirectoryPacketTest3)
+{
+    // Using a mock counter directory that allows to register invalid objects
+    MockCounterDirectory counterDirectory;
+
+    // Register an invalid device
+    const std::string deviceName = "inv@lid dev!c€";
+    const Device* device = nullptr;
+    BOOST_CHECK_NO_THROW(device = counterDirectory.RegisterDevice(deviceName, 3));
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 1);
+    BOOST_CHECK(device);
+
+    // Buffer with enough space
+    MockBufferManager mockBuffer(1024);
+    SendCounterPacket sendCounterPacket(mockBuffer);
+    BOOST_CHECK_THROW(sendCounterPacket.SendCounterDirectoryPacket(counterDirectory), armnn::RuntimeException);
+}
+
+BOOST_AUTO_TEST_CASE(SendCounterDirectoryPacketTest4)
+{
+    // Using a mock counter directory that allows to register invalid objects
+    MockCounterDirectory counterDirectory;
+
+    // Register an invalid counter set
+    const std::string counterSetName = "inv@lid count€rs€t";
+    const CounterSet* counterSet = nullptr;
+    BOOST_CHECK_NO_THROW(counterSet = counterDirectory.RegisterCounterSet(counterSetName));
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 1);
+    BOOST_CHECK(counterSet);
+
+    // Buffer with enough space
+    MockBufferManager mockBuffer(1024);
+    SendCounterPacket sendCounterPacket(mockBuffer);
+    BOOST_CHECK_THROW(sendCounterPacket.SendCounterDirectoryPacket(counterDirectory), armnn::RuntimeException);
+}
+
+BOOST_AUTO_TEST_CASE(SendCounterDirectoryPacketTest5)
+{
+    // Using a mock counter directory that allows to register invalid objects
+    MockCounterDirectory counterDirectory;
+
+    // Register an invalid category
+    const std::string categoryName = "c@t€gory";
+    const Category* category = nullptr;
+    BOOST_CHECK_NO_THROW(category = counterDirectory.RegisterCategory(categoryName));
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 1);
+    BOOST_CHECK(category);
+
+    // Buffer with enough space
+    MockBufferManager mockBuffer(1024);
+    SendCounterPacket sendCounterPacket(mockBuffer);
+    BOOST_CHECK_THROW(sendCounterPacket.SendCounterDirectoryPacket(counterDirectory), armnn::RuntimeException);
+}
+
+BOOST_AUTO_TEST_CASE(SendCounterDirectoryPacketTest6)
+{
+    // Using a mock counter directory that allows to register invalid objects
+    MockCounterDirectory counterDirectory;
+
+    // Register an invalid device
+    const std::string deviceName = "inv@lid dev!c€";
+    const Device* device = nullptr;
+    BOOST_CHECK_NO_THROW(device = counterDirectory.RegisterDevice(deviceName, 3));
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 1);
+    BOOST_CHECK(device);
+
+    // Register an invalid counter set
+    const std::string counterSetName = "inv@lid count€rs€t";
+    const CounterSet* counterSet = nullptr;
+    BOOST_CHECK_NO_THROW(counterSet = counterDirectory.RegisterCounterSet(counterSetName));
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 1);
+    BOOST_CHECK(counterSet);
+
+    // Register an invalid category associated to an invalid device and an invalid counter set
+    const std::string categoryName = "c@t€gory";
+    const Category* category = nullptr;
+    BOOST_CHECK_NO_THROW(category = counterDirectory.RegisterCategory(categoryName));
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 1);
+    BOOST_CHECK(category);
+
+    // Buffer with enough space
+    MockBufferManager mockBuffer(1024);
+    SendCounterPacket sendCounterPacket(mockBuffer);
+    BOOST_CHECK_THROW(sendCounterPacket.SendCounterDirectoryPacket(counterDirectory), armnn::RuntimeException);
+}
+
+BOOST_AUTO_TEST_CASE(SendCounterDirectoryPacketTest7)
+{
+    // Using a mock counter directory that allows to register invalid objects
+    MockCounterDirectory counterDirectory;
+
+    // Register an valid device
+    const std::string deviceName = "valid device";
+    const Device* device = nullptr;
+    BOOST_CHECK_NO_THROW(device = counterDirectory.RegisterDevice(deviceName, 3));
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 1);
+    BOOST_CHECK(device);
+
+    // Register an valid counter set
+    const std::string counterSetName = "valid counterset";
+    const CounterSet* counterSet = nullptr;
+    BOOST_CHECK_NO_THROW(counterSet = counterDirectory.RegisterCounterSet(counterSetName));
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 1);
+    BOOST_CHECK(counterSet);
+
+    // Register an valid category associated to a valid device and a valid counter set
+    const std::string categoryName = "category";
+    const Category* category = nullptr;
+    BOOST_CHECK_NO_THROW(category = counterDirectory.RegisterCategory(categoryName));
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 1);
+    BOOST_CHECK(category);
+
+    // Register an invalid counter associated to a valid category
+    const Counter* counter = nullptr;
+    BOOST_CHECK_NO_THROW(counter = counterDirectory.RegisterCounter(armnn::profiling::BACKEND_ID,
+                                                                    0,
+                                                                    categoryName,
+                                                                    0,
+                                                                    1,
+                                                                    123.45f,
+                                                                    "counter",
+                                                                    "counter description",
+                                                                    std::string("invalid counter units"),
+                                                                    5,
+                                                                    device->m_Uid,
+                                                                    counterSet->m_Uid));
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 5);
+    BOOST_CHECK(counter);
+
+    // Buffer with enough space
+    MockBufferManager mockBuffer(1024);
+    SendCounterPacket sendCounterPacket(mockBuffer);
+    BOOST_CHECK_THROW(sendCounterPacket.SendCounterDirectoryPacket(counterDirectory), armnn::RuntimeException);
+}
+
+BOOST_AUTO_TEST_CASE(SendThreadTest0)
+{
+    ProfilingStateMachine profilingStateMachine;
+    SetActiveProfilingState(profilingStateMachine);
+
+    MockProfilingConnection mockProfilingConnection;
+    MockStreamCounterBuffer mockStreamCounterBuffer(0);
+    SendCounterPacket sendCounterPacket(mockStreamCounterBuffer);
+    SendThread sendThread(profilingStateMachine, mockStreamCounterBuffer, sendCounterPacket);
+
+    // Try to start the send thread many times, it must only start once
+
+    sendThread.Start(mockProfilingConnection);
+    BOOST_CHECK(sendThread.IsRunning());
+    sendThread.Start(mockProfilingConnection);
+    sendThread.Start(mockProfilingConnection);
+    sendThread.Start(mockProfilingConnection);
+    sendThread.Start(mockProfilingConnection);
+    BOOST_CHECK(sendThread.IsRunning());
+
+    sendThread.Stop();
+    BOOST_CHECK(!sendThread.IsRunning());
+}
+
+BOOST_AUTO_TEST_CASE(SendThreadTest1)
 {
-    unsigned int size = 0;
+    ProfilingStateMachine profilingStateMachine;
+    SetActiveProfilingState(profilingStateMachine);
+
+    unsigned int totalWrittenSize = 0;
 
-    MockBuffer mockBuffer;
-    MockSendCounterPacket sendCounterPacket(mockBuffer);
+    MockProfilingConnection mockProfilingConnection;
+    MockStreamCounterBuffer mockStreamCounterBuffer(1024);
+    SendCounterPacket sendCounterPacket(mockStreamCounterBuffer);
+    SendThread sendThread(profilingStateMachine, mockStreamCounterBuffer, sendCounterPacket);
+    sendThread.Start(mockProfilingConnection);
 
+    // Interleaving writes and reads to/from the buffer with pauses to test that the send thread actually waits for
+    // something to become available for reading
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_UNTIL_READABLE_MS));
+
+    CounterDirectory counterDirectory;
     sendCounterPacket.SendStreamMetaDataPacket();
-    const char* buffer = reinterpret_cast<const char*>(mockBuffer.GetReadBuffer(size));
 
-    BOOST_TEST(strcmp(buffer, "SendStreamMetaDataPacket") == 0);
+    totalWrittenSize += GetStreamMetaDataPacketSize();
 
-    Category category;
-    std::vector<Counter> counters;
-    sendCounterPacket.SendCounterDirectoryPacket(category, counters);
+    sendThread.SetReadyToRead();
 
-    BOOST_TEST(strcmp(buffer, "SendCounterDirectoryPacket") == 0);
+    std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_UNTIL_READABLE_MS));
 
-    uint64_t timestamp = 0;
-    std::vector<uint32_t> counterValues;
-    std::vector<uint16_t> counterUids;
-    sendCounterPacket.SendPeriodicCounterCapturePacket(timestamp, counterValues, counterUids);
+    sendCounterPacket.SendCounterDirectoryPacket(counterDirectory);
 
-    BOOST_TEST(strcmp(buffer, "SendPeriodicCounterCapturePacket") == 0);
+    // Get the size of the Counter Directory Packet
+    unsigned int counterDirectoryPacketSize = 32;
+    totalWrittenSize += counterDirectoryPacketSize;
 
-    uint32_t capturePeriod = 0;
-    std::vector<uint16_t> selectedCounterIds;
-    sendCounterPacket.SendPeriodicCounterSelectionPacket(capturePeriod, selectedCounterIds);
+    sendThread.SetReadyToRead();
 
-    BOOST_TEST(strcmp(buffer, "SendPeriodicCounterSelectionPacket") == 0);
+    std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_UNTIL_READABLE_MS));
+
+    sendCounterPacket.SendPeriodicCounterCapturePacket(123u,
+                                                       {
+                                                           {   1u,      23u },
+                                                           {  33u, 1207623u }
+                                                       });
+
+    // Get the size of the Periodic Counter Capture Packet
+    unsigned int periodicCounterCapturePacketSize = 28;
+    totalWrittenSize += periodicCounterCapturePacketSize;
+
+    sendThread.SetReadyToRead();
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_UNTIL_READABLE_MS));
+
+    sendCounterPacket.SendPeriodicCounterCapturePacket(44u,
+                                                       {
+                                                           { 211u,     923u }
+                                                       });
+
+    // Get the size of the Periodic Counter Capture Packet
+    periodicCounterCapturePacketSize = 22;
+    totalWrittenSize += periodicCounterCapturePacketSize;
+
+    sendCounterPacket.SendPeriodicCounterCapturePacket(1234u,
+                                                       {
+                                                           { 555u,      23u },
+                                                           { 556u,       6u },
+                                                           { 557u,  893454u },
+                                                           { 558u, 1456623u },
+                                                           { 559u,  571090u }
+                                                       });
+
+    // Get the size of the Periodic Counter Capture Packet
+    periodicCounterCapturePacketSize = 46;
+    totalWrittenSize += periodicCounterCapturePacketSize;
+
+    sendCounterPacket.SendPeriodicCounterCapturePacket(997u,
+                                                       {
+                                                           {  88u,      11u },
+                                                           {  96u,      22u },
+                                                           {  97u,      33u },
+                                                           { 999u,     444u }
+                                                       });
+
+    // Get the size of the Periodic Counter Capture Packet
+    periodicCounterCapturePacketSize = 40;
+    totalWrittenSize += periodicCounterCapturePacketSize;
+
+    sendThread.SetReadyToRead();
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_UNTIL_READABLE_MS));
+
+    sendCounterPacket.SendPeriodicCounterSelectionPacket(1000u, { 1345u, 254u, 4536u, 408u, 54u, 6323u, 428u, 1u, 6u });
+
+    // Get the size of the Periodic Counter Capture Packet
+    periodicCounterCapturePacketSize = 30;
+    totalWrittenSize += periodicCounterCapturePacketSize;
+
+    sendThread.SetReadyToRead();
+
+    // To test an exact value of the "read size" in the mock buffer, wait to allow the send thread to
+    // read all what's remaining in the buffer
+    std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_UNTIL_READABLE_MS));
+
+    sendThread.Stop();
+
+    BOOST_CHECK(mockStreamCounterBuffer.GetCommittedSize() == totalWrittenSize);
+    BOOST_CHECK(mockStreamCounterBuffer.GetReadableSize()  == totalWrittenSize);
+    BOOST_CHECK(mockStreamCounterBuffer.GetReadSize()      == totalWrittenSize);
+}
+
+BOOST_AUTO_TEST_CASE(SendThreadTest2)
+{
+    ProfilingStateMachine profilingStateMachine;
+    SetActiveProfilingState(profilingStateMachine);
+
+    unsigned int totalWrittenSize = 0;
+
+    MockProfilingConnection mockProfilingConnection;
+    MockStreamCounterBuffer mockStreamCounterBuffer(1024);
+    SendCounterPacket sendCounterPacket(mockStreamCounterBuffer);
+    SendThread sendThread(profilingStateMachine, mockStreamCounterBuffer, sendCounterPacket);
+    sendThread.Start(mockProfilingConnection);
+
+    // Adding many spurious "ready to read" signals throughout the test to check that the send thread is
+    // capable of handling unnecessary read requests
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_UNTIL_READABLE_MS));
+
+    sendThread.SetReadyToRead();
+
+    CounterDirectory counterDirectory;
+    sendCounterPacket.SendStreamMetaDataPacket();
+
+    totalWrittenSize += GetStreamMetaDataPacketSize();
+
+    sendThread.SetReadyToRead();
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_UNTIL_READABLE_MS));
+
+    sendCounterPacket.SendCounterDirectoryPacket(counterDirectory);
+
+    // Get the size of the Counter Directory Packet
+    unsigned int counterDirectoryPacketSize = 32;
+    totalWrittenSize += counterDirectoryPacketSize;
+
+    sendThread.SetReadyToRead();
+    sendThread.SetReadyToRead();
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_UNTIL_READABLE_MS));
+
+    sendCounterPacket.SendPeriodicCounterCapturePacket(123u,
+                                                       {
+                                                           {   1u,      23u },
+                                                           {  33u, 1207623u }
+                                                       });
+
+    // Get the size of the Periodic Counter Capture Packet
+    unsigned int periodicCounterCapturePacketSize = 28;
+    totalWrittenSize += periodicCounterCapturePacketSize;
+
+    sendThread.SetReadyToRead();
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_UNTIL_READABLE_MS));
+
+    sendThread.SetReadyToRead();
+    sendThread.SetReadyToRead();
+    sendThread.SetReadyToRead();
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_UNTIL_READABLE_MS));
+
+    sendThread.SetReadyToRead();
+    sendCounterPacket.SendPeriodicCounterCapturePacket(44u,
+                                                       {
+                                                           { 211u,     923u }
+                                                       });
+
+    // Get the size of the Periodic Counter Capture Packet
+    periodicCounterCapturePacketSize = 22;
+    totalWrittenSize += periodicCounterCapturePacketSize;
+
+    sendCounterPacket.SendPeriodicCounterCapturePacket(1234u,
+                                                       {
+                                                           { 555u,      23u },
+                                                           { 556u,       6u },
+                                                           { 557u,  893454u },
+                                                           { 558u, 1456623u },
+                                                           { 559u,  571090u }
+                                                       });
+
+    // Get the size of the Periodic Counter Capture Packet
+    periodicCounterCapturePacketSize = 46;
+    totalWrittenSize += periodicCounterCapturePacketSize;
+
+    sendThread.SetReadyToRead();
+    sendCounterPacket.SendPeriodicCounterCapturePacket(997u,
+                                                       {
+                                                           {  88u,      11u },
+                                                           {  96u,      22u },
+                                                           {  97u,      33u },
+                                                           { 999u,     444u }
+                                                       });
+
+    // Get the size of the Periodic Counter Capture Packet
+    periodicCounterCapturePacketSize = 40;
+    totalWrittenSize += periodicCounterCapturePacketSize;
+
+    sendThread.SetReadyToRead();
+    sendThread.SetReadyToRead();
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_UNTIL_READABLE_MS));
+
+    sendCounterPacket.SendPeriodicCounterSelectionPacket(1000u, { 1345u, 254u, 4536u, 408u, 54u, 6323u, 428u, 1u, 6u });
+
+    // Get the size of the Periodic Counter Capture Packet
+    periodicCounterCapturePacketSize = 30;
+    totalWrittenSize += periodicCounterCapturePacketSize;
+
+    sendThread.SetReadyToRead();
+
+    // To test an exact value of the "read size" in the mock buffer, wait to allow the send thread to
+    // read all what's remaining in the buffer
+    sendThread.Stop();
+
+    BOOST_CHECK(mockStreamCounterBuffer.GetCommittedSize() == totalWrittenSize);
+    BOOST_CHECK(mockStreamCounterBuffer.GetReadableSize()  == totalWrittenSize);
+    BOOST_CHECK(mockStreamCounterBuffer.GetReadSize()      == totalWrittenSize);
+}
+
+BOOST_AUTO_TEST_CASE(SendThreadTest3)
+{
+    ProfilingStateMachine profilingStateMachine;
+    SetActiveProfilingState(profilingStateMachine);
+
+    unsigned int totalWrittenSize = 0;
+
+    MockProfilingConnection mockProfilingConnection;
+    MockStreamCounterBuffer mockStreamCounterBuffer(1024);
+    SendCounterPacket sendCounterPacket(mockStreamCounterBuffer);
+    SendThread sendThread(profilingStateMachine, mockStreamCounterBuffer, sendCounterPacket);
+    sendThread.Start(mockProfilingConnection);
+
+    // Not using pauses or "grace periods" to stress test the send thread
+
+    sendThread.SetReadyToRead();
+
+    CounterDirectory counterDirectory;
+    sendCounterPacket.SendStreamMetaDataPacket();
+
+    totalWrittenSize += GetStreamMetaDataPacketSize();
+
+    sendThread.SetReadyToRead();
+    sendCounterPacket.SendCounterDirectoryPacket(counterDirectory);
+
+    // Get the size of the Counter Directory Packet
+    unsigned int counterDirectoryPacketSize =32;
+    totalWrittenSize += counterDirectoryPacketSize;
+
+    sendThread.SetReadyToRead();
+    sendThread.SetReadyToRead();
+    sendCounterPacket.SendPeriodicCounterCapturePacket(123u,
+                                                       {
+                                                           {   1u,      23u },
+                                                           {  33u, 1207623u }
+                                                       });
+
+    // Get the size of the Periodic Counter Capture Packet
+    unsigned int periodicCounterCapturePacketSize = 28;
+    totalWrittenSize += periodicCounterCapturePacketSize;
+
+    sendThread.SetReadyToRead();
+    sendThread.SetReadyToRead();
+    sendThread.SetReadyToRead();
+    sendThread.SetReadyToRead();
+    sendThread.SetReadyToRead();
+    sendCounterPacket.SendPeriodicCounterCapturePacket(44u,
+                                                       {
+                                                           { 211u,     923u }
+                                                       });
+
+    // Get the size of the Periodic Counter Capture Packet
+    periodicCounterCapturePacketSize = 22;
+    totalWrittenSize += periodicCounterCapturePacketSize;
+
+    sendCounterPacket.SendPeriodicCounterCapturePacket(1234u,
+                                                       {
+                                                           { 555u,      23u },
+                                                           { 556u,       6u },
+                                                           { 557u,  893454u },
+                                                           { 558u, 1456623u },
+                                                           { 559u,  571090u }
+                                                       });
+
+    // Get the size of the Periodic Counter Capture Packet
+    periodicCounterCapturePacketSize = 46;
+    totalWrittenSize += periodicCounterCapturePacketSize;
+
+    sendThread.SetReadyToRead();
+    sendThread.SetReadyToRead();
+    sendCounterPacket.SendPeriodicCounterCapturePacket(997u,
+                                                       {
+                                                           {  88u,      11u },
+                                                           {  96u,      22u },
+                                                           {  97u,      33u },
+                                                           { 999u,     444u }
+                                                       });
+
+    // Get the size of the Periodic Counter Capture Packet
+    periodicCounterCapturePacketSize = 40;
+    totalWrittenSize += periodicCounterCapturePacketSize;
+
+    sendThread.SetReadyToRead();
+    sendThread.SetReadyToRead();
+    sendCounterPacket.SendPeriodicCounterSelectionPacket(1000u, { 1345u, 254u, 4536u, 408u, 54u, 6323u, 428u, 1u, 6u });
+
+    // Get the size of the Periodic Counter Capture Packet
+    periodicCounterCapturePacketSize = 30;
+    totalWrittenSize += periodicCounterCapturePacketSize;
+
+    sendThread.SetReadyToRead();
+
+    // Abruptly terminating the send thread, the amount of data sent may be less that the amount written (the send
+    // thread is not guaranteed to flush the buffer)
+    sendThread.Stop();
+
+    BOOST_CHECK(mockStreamCounterBuffer.GetCommittedSize() == totalWrittenSize);
+    BOOST_CHECK(mockStreamCounterBuffer.GetReadableSize()  <= totalWrittenSize);
+    BOOST_CHECK(mockStreamCounterBuffer.GetReadSize()      <= totalWrittenSize);
+    BOOST_CHECK(mockStreamCounterBuffer.GetReadSize()      <= mockStreamCounterBuffer.GetReadableSize());
+    BOOST_CHECK(mockStreamCounterBuffer.GetReadSize()      <= mockStreamCounterBuffer.GetCommittedSize());
+}
+
+BOOST_AUTO_TEST_CASE(SendCounterPacketTestWithSendThread)
+{
+    ProfilingStateMachine profilingStateMachine;
+    SetWaitingForAckProfilingState(profilingStateMachine);
+
+    MockProfilingConnection mockProfilingConnection;
+    BufferManager bufferManager(1, 1024);
+    SendCounterPacket sendCounterPacket(bufferManager);
+    SendThread sendThread(profilingStateMachine, bufferManager, sendCounterPacket, -1);
+    sendThread.Start(mockProfilingConnection);
+
+    unsigned int streamMetadataPacketsize = GetStreamMetaDataPacketSize();
+
+    sendThread.Stop();
+
+    // check for packet in ProfilingConnection
+    BOOST_CHECK(mockProfilingConnection.CheckForPacket({PacketType::StreamMetaData, streamMetadataPacketsize}) == 1);
+
+    SetActiveProfilingState(profilingStateMachine);
+    sendThread.Start(mockProfilingConnection);
+
+    // SendCounterDirectoryPacket
+    CounterDirectory counterDirectory;
+    sendCounterPacket.SendCounterDirectoryPacket(counterDirectory);
+
+    sendThread.Stop();
+    unsigned int counterDirectoryPacketSize = 32;
+    // check for packet in ProfilingConnection
+    BOOST_CHECK(mockProfilingConnection.CheckForPacket(
+        {PacketType::CounterDirectory, counterDirectoryPacketSize}) == 1);
+
+    sendThread.Start(mockProfilingConnection);
+
+    // SendPeriodicCounterCapturePacket
+    sendCounterPacket.SendPeriodicCounterCapturePacket(123u,
+                                                       {
+                                                           {   1u,      23u },
+                                                           {  33u, 1207623u }
+                                                       });
+
+    sendThread.Stop();
+
+    unsigned int periodicCounterCapturePacketSize = 28;
+    BOOST_CHECK(mockProfilingConnection.CheckForPacket(
+        {PacketType::PeriodicCounterCapture, periodicCounterCapturePacketSize}) == 1);
+}
+
+BOOST_AUTO_TEST_CASE(SendThreadBufferTest)
+{
+    ProfilingStateMachine profilingStateMachine;
+    SetActiveProfilingState(profilingStateMachine);
+
+    MockProfilingConnection mockProfilingConnection;
+    BufferManager bufferManager(3, 1024);
+    SendCounterPacket sendCounterPacket(bufferManager);
+    SendThread sendThread(profilingStateMachine, bufferManager, sendCounterPacket, -1);
+    sendThread.Start(mockProfilingConnection);
+
+    // SendStreamMetaDataPacket
+    sendCounterPacket.SendStreamMetaDataPacket();
+
+    // Read data from the buffer
+    // Buffer should become readable after commit by SendStreamMetaDataPacket
+    auto packetBuffer = bufferManager.GetReadableBuffer();
+    BOOST_TEST(packetBuffer.get());
+
+    unsigned int streamMetadataPacketsize = GetStreamMetaDataPacketSize();
+    BOOST_TEST(packetBuffer->GetSize() == streamMetadataPacketsize);
+
+    // Recommit to be read by sendCounterPacket
+    bufferManager.Commit(packetBuffer, streamMetadataPacketsize);
+
+    // SendCounterDirectoryPacket
+    CounterDirectory counterDirectory;
+    sendCounterPacket.SendCounterDirectoryPacket(counterDirectory);
+
+    // SendPeriodicCounterCapturePacket
+    sendCounterPacket.SendPeriodicCounterCapturePacket(123u,
+                                                       {
+                                                           {   1u,      23u },
+                                                           {  33u, 1207623u }
+                                                       });
+
+    sendThread.Stop();
+
+    // The buffer is read by the send thread so it should not be in the readable buffer.
+    auto readBuffer = bufferManager.GetReadableBuffer();
+    BOOST_TEST(!readBuffer);
+
+    // Successfully reserved the buffer with requested size
+    unsigned int reservedSize = 0;
+    auto reservedBuffer = bufferManager.Reserve(512, reservedSize);
+    BOOST_TEST(reservedSize == 512);
+    BOOST_TEST(reservedBuffer.get());
+
+    const auto writtenDataSize = mockProfilingConnection.GetWrittenDataSize();
+    const auto metaDataPacketCount =
+            mockProfilingConnection.CheckForPacket({PacketType::StreamMetaData, streamMetadataPacketsize});
+
+    BOOST_TEST(metaDataPacketCount >= 1);
+    BOOST_TEST(mockProfilingConnection.CheckForPacket({PacketType::CounterDirectory, 32}) == 1);
+    BOOST_TEST(mockProfilingConnection.CheckForPacket({PacketType::PeriodicCounterCapture, 28}) == 1);
+    // Check that we only received the packets we expected
+    BOOST_TEST(metaDataPacketCount + 2 == writtenDataSize);
+}
+
+BOOST_AUTO_TEST_CASE(SendThreadSendStreamMetadataPacket1)
+{
+    ProfilingStateMachine profilingStateMachine;
+
+    MockProfilingConnection mockProfilingConnection;
+    BufferManager bufferManager(3, 1024);
+    SendCounterPacket sendCounterPacket(bufferManager);
+    SendThread sendThread(profilingStateMachine, bufferManager, sendCounterPacket);
+    sendThread.Start(mockProfilingConnection);
+
+    // The profiling state is set to "Uninitialized", so the send thread should throw an exception
+    BOOST_CHECK_THROW(sendThread.Stop(), armnn::RuntimeException);
+}
+
+BOOST_AUTO_TEST_CASE(SendThreadSendStreamMetadataPacket2)
+{
+    ProfilingStateMachine profilingStateMachine;
+    SetNotConnectedProfilingState(profilingStateMachine);
+
+    MockProfilingConnection mockProfilingConnection;
+    BufferManager bufferManager(3, 1024);
+    SendCounterPacket sendCounterPacket(bufferManager);
+    SendThread sendThread(profilingStateMachine, bufferManager, sendCounterPacket);
+    sendThread.Start(mockProfilingConnection);
+
+    // The profiling state is set to "NotConnected", so the send thread should throw an exception
+    BOOST_CHECK_THROW(sendThread.Stop(), armnn::RuntimeException);
+}
+
+BOOST_AUTO_TEST_CASE(SendThreadSendStreamMetadataPacket3)
+{
+    ProfilingStateMachine profilingStateMachine;
+    SetWaitingForAckProfilingState(profilingStateMachine);
+
+    unsigned int streamMetadataPacketsize = GetStreamMetaDataPacketSize();
+
+    MockProfilingConnection mockProfilingConnection;
+    BufferManager bufferManager(3, 1024);
+    SendCounterPacket sendCounterPacket(bufferManager);
+    SendThread sendThread(profilingStateMachine, bufferManager, sendCounterPacket);
+    sendThread.Start(mockProfilingConnection);
+
+    // The profiling state is set to "WaitingForAck", so the send thread should send a Stream Metadata packet
+    // Wait for sendThread to join
+    BOOST_CHECK_NO_THROW(sendThread.Stop());
+
+    // Check that the buffer contains at least one Stream Metadata packet and no other packets
+    const auto writtenDataSize = mockProfilingConnection.GetWrittenDataSize();
+
+    BOOST_TEST(writtenDataSize >= 1);
+    BOOST_TEST(mockProfilingConnection.CheckForPacket(
+                  {PacketType::StreamMetaData, streamMetadataPacketsize}) == writtenDataSize);
+}
+
+BOOST_AUTO_TEST_CASE(SendThreadSendStreamMetadataPacket4)
+{
+    ProfilingStateMachine profilingStateMachine;
+    SetWaitingForAckProfilingState(profilingStateMachine);
+
+    unsigned int streamMetadataPacketsize = GetStreamMetaDataPacketSize();
+
+    MockProfilingConnection mockProfilingConnection;
+    BufferManager bufferManager(3, 1024);
+    SendCounterPacket sendCounterPacket(bufferManager);
+    SendThread sendThread(profilingStateMachine, bufferManager, sendCounterPacket);
+    sendThread.Start(mockProfilingConnection);
+
+    // The profiling state is set to "WaitingForAck", so the send thread should send a Stream Metadata packet
+    // Wait for sendThread to join
+    sendThread.Stop();
+
+    sendThread.Start(mockProfilingConnection);
+    // Check that the profiling state is still "WaitingForAck"
+    BOOST_TEST((profilingStateMachine.GetCurrentState() == ProfilingState::WaitingForAck));
+
+    // Check that the buffer contains at least one Stream Metadata packet
+    BOOST_TEST(mockProfilingConnection.CheckForPacket({PacketType::StreamMetaData, streamMetadataPacketsize}) >= 1);
+
+    mockProfilingConnection.Clear();
+
+    sendThread.Stop();
+    sendThread.Start(mockProfilingConnection);
+
+    // Try triggering a new buffer read
+    sendThread.SetReadyToRead();
+
+    // Wait for sendThread to join
+    BOOST_CHECK_NO_THROW(sendThread.Stop());
+
+    // Check that the profiling state is still "WaitingForAck"
+    BOOST_TEST((profilingStateMachine.GetCurrentState() == ProfilingState::WaitingForAck));
+
+    // Check that the buffer contains at least one Stream Metadata packet and no other packets
+    const auto writtenDataSize = mockProfilingConnection.GetWrittenDataSize();
 
+    BOOST_TEST(writtenDataSize >= 1);
+    BOOST_TEST(mockProfilingConnection.CheckForPacket(
+                  {PacketType::StreamMetaData, streamMetadataPacketsize}) == writtenDataSize);
 }
 
 BOOST_AUTO_TEST_SUITE_END()