IVGCVSW-4595 Change FileOnlyProfilingConnection to all packet processor model

Change-Id: Ieccb26190d80e570ddef8d7c22e824eda1b92d7f
Signed-off-by: Jim Flynn <jim.flynn@arm.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c2c4cc6..9b9b19f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -689,6 +689,8 @@
         src/profiling/test/ProfilingTests.hpp
         src/profiling/test/ProfilingTestUtils.cpp
         src/profiling/test/ProfilingTestUtils.hpp
+        src/profiling/test/RequestCountersPacketHandler.cpp
+        src/profiling/test/RequestCountersPacketHandler.hpp
         src/profiling/test/SendCounterPacketTests.cpp
         src/profiling/test/SendCounterPacketTests.hpp
         src/profiling/test/TestTimelinePacketHandler.cpp
diff --git a/include/armnn/profiling/ILocalPacketHandler.hpp b/include/armnn/profiling/ILocalPacketHandler.hpp
index a2b9d5f..158c0eb 100644
--- a/include/armnn/profiling/ILocalPacketHandler.hpp
+++ b/include/armnn/profiling/ILocalPacketHandler.hpp
@@ -17,9 +17,32 @@
 
 namespace profiling
 {
+
+enum class TargetEndianness
+{
+    BeWire,
+    LeWire
+};
+
 // forward declare to prevent a circular dependency
 class Packet;
-class IProfilingConnection;
+
+// the handlers need to be able to do two
+// things to service the FileOnlyProfilingConnection
+// and any other implementation of IProfilingConnection
+// set the endianness and write a packet back i.e.
+// return a packet and close the connection
+class IInternalProfilingConnection
+{
+public:
+    virtual ~IInternalProfilingConnection() {};
+
+    virtual void SetEndianess(const TargetEndianness& endianness) = 0;
+
+    virtual void ReturnPacket(Packet& packet) = 0;
+
+    virtual void Close() = 0;
+};
 
 class ILocalPacketHandler
 {
@@ -37,7 +60,8 @@
 
     /// Set a profiling connection on the handler. Only need to implement this
     /// function if the handler will be writing data back to the profiled application.
-    virtual void SetConnection(IProfilingConnection* profilingConnection) {armnn::IgnoreUnused(profilingConnection);}
+    virtual void SetConnection(IInternalProfilingConnection* profilingConnection)
+    {armnn::IgnoreUnused(profilingConnection);}
 };
 
 using ILocalPacketHandlerPtr = std::unique_ptr<ILocalPacketHandler>;
diff --git a/profiling/common/include/Packet.hpp b/profiling/common/include/Packet.hpp
index c1f2796..23c3124 100644
--- a/profiling/common/include/Packet.hpp
+++ b/profiling/common/include/Packet.hpp
@@ -20,6 +20,8 @@
 public:
     Packet()
         : m_Header(0)
+        , m_PacketFamily(0)
+        , m_PacketId(0)
         , m_Length(0)
         , m_Data(nullptr)
     {}
diff --git a/src/profiling/FileOnlyProfilingConnection.cpp b/src/profiling/FileOnlyProfilingConnection.cpp
index 1d4e23b..1e26aaa 100644
--- a/src/profiling/FileOnlyProfilingConnection.cpp
+++ b/src/profiling/FileOnlyProfilingConnection.cpp
@@ -8,6 +8,7 @@
 
 #include <armnn/Exceptions.hpp>
 #include <common/include/Constants.hpp>
+#include <common/include/ProfilingException.hpp>
 
 #include <algorithm>
 #include <boost/numeric/conversion/cast.hpp>
@@ -20,9 +21,66 @@
 namespace profiling
 {
 
+std::vector<uint32_t> StreamMetaDataProcessor::GetHeadersAccepted()
+{
+    std::vector<uint32_t> headers;
+    headers.push_back(m_MetaDataPacketHeader);
+    return headers;
+}
+
+void StreamMetaDataProcessor::HandlePacket(const Packet& packet)
+{
+    if (packet.GetHeader() != m_MetaDataPacketHeader)
+    {
+        throw armnnProfiling::ProfilingException("StreamMetaDataProcessor can only handle Stream Meta Data Packets");
+    }
+    // determine the endianness of the protocol
+    TargetEndianness endianness;
+    if (ToUint32(packet.GetData(),TargetEndianness::BeWire) == armnnProfiling::PIPE_MAGIC)
+    {
+        endianness = TargetEndianness::BeWire;
+    }
+    else if (ToUint32(packet.GetData(), TargetEndianness::LeWire) == armnnProfiling::PIPE_MAGIC)
+    {
+        endianness = TargetEndianness::LeWire;
+    }
+    else
+    {
+        throw armnnProfiling::ProfilingException("Protocol read error. Unable to read PIPE_MAGIC value.");
+    }
+    m_FileOnlyProfilingConnection->SetEndianess(endianness);
+    // send back the acknowledgement
+    std::unique_ptr<unsigned char[]> uniqueNullPtr = nullptr;
+    Packet returnPacket(0x10000, 0, uniqueNullPtr);
+    m_FileOnlyProfilingConnection->ReturnPacket(returnPacket);
+}
+
+uint32_t StreamMetaDataProcessor::ToUint32(const unsigned char* data, TargetEndianness endianness)
+{
+    // Extract the first 4 bytes starting at data and push them into a 32bit integer based on the
+    // specified endianness.
+    if (endianness == TargetEndianness::BeWire)
+    {
+        return static_cast<uint32_t>(data[0]) << 24 | static_cast<uint32_t>(data[1]) << 16 |
+               static_cast<uint32_t>(data[2]) << 8 | static_cast<uint32_t>(data[3]);
+    }
+    else
+    {
+        return static_cast<uint32_t>(data[3]) << 24 | static_cast<uint32_t>(data[2]) << 16 |
+               static_cast<uint32_t>(data[1]) << 8 | static_cast<uint32_t>(data[0]);
+    }
+}
+
 FileOnlyProfilingConnection::~FileOnlyProfilingConnection()
 {
-    Close();
+    try
+    {
+        Close();
+    }
+    catch (...)
+    {
+        // do nothing
+    }
 }
 
 bool FileOnlyProfilingConnection::IsOpen() const
@@ -49,144 +107,35 @@
     }
 }
 
-bool FileOnlyProfilingConnection::WaitForStreamMeta(const unsigned char* buffer, uint32_t length)
-{
-    IgnoreUnused(length);
-
-    // The first word, stream_metadata_identifer, should always be 0.
-    if (ToUint32(buffer, TargetEndianness::BeWire) != 0)
-    {
-        Fail("Protocol error. The stream_metadata_identifer was not 0.");
-    }
-
-    // Before we interpret the length we need to read the pipe_magic word to determine endianness.
-    if (ToUint32(buffer + 8, TargetEndianness::BeWire) == armnnProfiling::PIPE_MAGIC)
-    {
-        m_Endianness = TargetEndianness::BeWire;
-    }
-    else if (ToUint32(buffer + 8, TargetEndianness::LeWire) == armnnProfiling::PIPE_MAGIC)
-    {
-        m_Endianness = TargetEndianness::LeWire;
-    }
-    else
-    {
-        Fail("Protocol read error. Unable to read PIPE_MAGIC value.");
-    }
-    return true;
-}
-
-void FileOnlyProfilingConnection::SendConnectionAck()
-{
-    if (!m_QuietOp)
-    {
-        std::cout << "Sending connection acknowledgement." << std::endl;
-    }
-    std::unique_ptr<unsigned char[]> uniqueNullPtr = nullptr;
-    {
-        std::lock_guard<std::mutex> lck(m_PacketAvailableMutex);
-        m_PacketQueue.push(Packet(0x10000, 0, uniqueNullPtr));
-    }
-    m_ConditionPacketAvailable.notify_one();
-}
-
-bool FileOnlyProfilingConnection::SendCounterSelectionPacket()
-{
-    uint32_t uint16_t_size = sizeof(uint16_t);
-    uint32_t uint32_t_size = sizeof(uint32_t);
-
-    uint32_t offset   = 0;
-    uint32_t bodySize = uint32_t_size + boost::numeric_cast<uint32_t>(m_IdList.size()) * uint16_t_size;
-
-    auto uniqueData     = std::make_unique<unsigned char[]>(bodySize);
-    unsigned char* data = reinterpret_cast<unsigned char*>(uniqueData.get());
-
-    // Copy capturePeriod
-    WriteUint32(data, offset, m_Options.m_CapturePeriod);
-
-    // Copy m_IdList
-    offset += uint32_t_size;
-    for (const uint16_t& id : m_IdList)
-    {
-        WriteUint16(data, offset, id);
-        offset += uint16_t_size;
-    }
-
-    {
-        std::lock_guard<std::mutex> lck(m_PacketAvailableMutex);
-        m_PacketQueue.push(Packet(0x40000, bodySize, uniqueData));
-    }
-    m_ConditionPacketAvailable.notify_one();
-
-    return true;
-}
-
 bool FileOnlyProfilingConnection::WritePacket(const unsigned char* buffer, uint32_t length)
 {
     ARMNN_ASSERT(buffer);
     Packet packet = ReceivePacket(buffer, length);
-
-    // Read Header and determine case
-    uint32_t outgoingHeaderAsWords[2];
-    PackageActivity packageActivity = GetPackageActivity(packet, outgoingHeaderAsWords);
-
-    switch (packageActivity)
-    {
-        case PackageActivity::StreamMetaData:
-        {
-            if (!WaitForStreamMeta(buffer, length))
-            {
-                return EXIT_FAILURE;
-            }
-
-            SendConnectionAck();
-            break;
-        }
-        case PackageActivity::CounterDirectory:
-        {
-            std::unique_ptr<unsigned char[]> uniqueCounterData = std::make_unique<unsigned char[]>(length - 8);
-
-            std::memcpy(uniqueCounterData.get(), buffer + 8, length - 8);
-
-            Packet directoryPacket(outgoingHeaderAsWords[0], length - 8, uniqueCounterData);
-
-            armnn::profiling::PacketVersionResolver packetVersionResolver;
-            DirectoryCaptureCommandHandler directoryCaptureCommandHandler(
-                0, 2, packetVersionResolver.ResolvePacketVersion(0, 2).GetEncodedValue());
-            directoryCaptureCommandHandler.operator()(directoryPacket);
-            const ICounterDirectory& counterDirectory = directoryCaptureCommandHandler.GetCounterDirectory();
-            for (auto& category : counterDirectory.GetCategories())
-            {
-                // Remember we need to translate the Uid's from our CounterDirectory instance to the parent one.
-                std::vector<uint16_t> translatedCounters;
-                for (auto const& copyUid : category->m_Counters)
-                {
-                    translatedCounters.emplace_back(directoryCaptureCommandHandler.TranslateUIDCopyToOriginal(copyUid));
-                }
-                m_IdList.insert(std::end(m_IdList), std::begin(translatedCounters), std::end(translatedCounters));
-            }
-            SendCounterSelectionPacket();
-            break;
-        }
-        default:
-        {
-            break;
-        }
-    }
     ForwardPacketToHandlers(packet);
     return true;
 }
 
+void FileOnlyProfilingConnection::ReturnPacket(Packet& packet)
+{
+    {
+        std::lock_guard<std::mutex> lck(m_PacketAvailableMutex);
+        m_PacketQueue.push(std::move(packet));
+    }
+    m_ConditionPacketAvailable.notify_one();
+}
+
 Packet FileOnlyProfilingConnection::ReadPacket(uint32_t timeout)
 {
     std::unique_lock<std::mutex> lck(m_PacketAvailableMutex);
 
     // Here we are using m_PacketQueue.empty() as a predicate variable
     // The conditional variable will wait until packetQueue is not empty or until a timeout
-    if(!m_ConditionPacketAvailable.wait_for(lck,
-                                            std::chrono::milliseconds(timeout),
-                                            [&]{return !m_PacketQueue.empty();}))
+    if (!m_ConditionPacketAvailable.wait_for(lck,
+                                             std::chrono::milliseconds(timeout),
+                                             [&]{return !m_PacketQueue.empty();}))
     {
-        throw armnn::TimeoutException("Thread has timed out as per requested time limit");
+        Packet empty;
+        return empty;
     }
 
     Packet returnedPacket = std::move(m_PacketQueue.front());
@@ -194,40 +143,6 @@
     return returnedPacket;
 }
 
-PackageActivity FileOnlyProfilingConnection::GetPackageActivity(const Packet& packet, uint32_t headerAsWords[2])
-{
-    headerAsWords[0] = packet.GetHeader();
-    headerAsWords[1] = packet.GetLength();
-    if (headerAsWords[0] == 0x20000)    // Packet family = 0 Packet Id = 2
-    {
-        return PackageActivity::CounterDirectory;
-    }
-    else if (headerAsWords[0] == 0)    // Packet family = 0 Packet Id = 0
-    {
-        return PackageActivity::StreamMetaData;
-    }
-    else
-    {
-        return PackageActivity::Unknown;
-    }
-}
-
-uint32_t FileOnlyProfilingConnection::ToUint32(const unsigned char* data, TargetEndianness endianness)
-{
-    // Extract the first 4 bytes starting at data and push them into a 32bit integer based on the
-    // specified endianness.
-    if (endianness == TargetEndianness::BeWire)
-    {
-        return static_cast<uint32_t>(data[0]) << 24 | static_cast<uint32_t>(data[1]) << 16 |
-               static_cast<uint32_t>(data[2]) << 8 | static_cast<uint32_t>(data[3]);
-    }
-    else
-    {
-        return static_cast<uint32_t>(data[3]) << 24 | static_cast<uint32_t>(data[2]) << 16 |
-               static_cast<uint32_t>(data[1]) << 8 | static_cast<uint32_t>(data[0]);
-    }
-}
-
 void FileOnlyProfilingConnection::Fail(const std::string& errorMessage)
 {
     Close();
@@ -290,13 +205,13 @@
     {
         return;
     }
-    if (m_KeepRunning.load() == false)
+    if (!m_KeepRunning.load())
     {
         return;
     }
     {
         std::unique_lock<std::mutex> readableListLock(m_ReadableMutex);
-        if (m_KeepRunning.load() == false)
+        if (!m_KeepRunning.load())
         {
             return;
         }
@@ -367,9 +282,24 @@
     auto iter = m_IndexedHandlers.find(packet.GetHeader());
     if (iter != m_IndexedHandlers.end())
     {
-        for (auto &delegate : iter->second)
+        for (auto& delegate : iter->second)
         {
-            delegate->HandlePacket(packet);
+            try
+            {
+                delegate->HandlePacket(packet);
+            }
+            catch (const armnnProfiling::ProfilingException& ex)
+            {
+                Fail(ex.what());
+            }
+            catch (const std::exception& ex)
+            {
+                Fail(ex.what());
+            }
+            catch (...)
+            {
+                Fail("handler failed");
+            }
         }
     }
 }
diff --git a/src/profiling/FileOnlyProfilingConnection.hpp b/src/profiling/FileOnlyProfilingConnection.hpp
index 3776dbc..b19b983 100644
--- a/src/profiling/FileOnlyProfilingConnection.hpp
+++ b/src/profiling/FileOnlyProfilingConnection.hpp
@@ -25,43 +25,54 @@
 namespace profiling
 {
 
-enum class TargetEndianness
-{
-    BeWire,
-    LeWire
-};
+// forward declaration
+class FileOnlyProfilingConnection;
 
-enum class PackageActivity
-{
-    StreamMetaData,
-    CounterDirectory,
-    Unknown
-};
-
-class FileOnlyProfilingConnection : public IProfilingConnection
+class StreamMetaDataProcessor : public ILocalPacketHandler
 {
 public:
-    FileOnlyProfilingConnection(const Runtime::CreationOptions::ExternalProfilingOptions& options,
-                                const bool quietOp = true)
+    explicit StreamMetaDataProcessor(FileOnlyProfilingConnection* fileOnlyProfilingConnection) :
+            m_FileOnlyProfilingConnection(fileOnlyProfilingConnection),
+            m_MetaDataPacketHeader(ConstructHeader(0, 0)) {};
+
+    std::vector<uint32_t> GetHeadersAccepted() override;
+
+    void HandlePacket(const Packet& packet) override;
+
+private:
+    FileOnlyProfilingConnection* m_FileOnlyProfilingConnection;
+    uint32_t m_MetaDataPacketHeader;
+
+    static uint32_t ToUint32(const unsigned char* data, TargetEndianness endianness);
+};
+
+class FileOnlyProfilingConnection : public IProfilingConnection, public IInternalProfilingConnection
+{
+public:
+    explicit FileOnlyProfilingConnection(const Runtime::CreationOptions::ExternalProfilingOptions& options)
         : m_Options(options)
-        , m_QuietOp(quietOp)
-        , m_Endianness(TargetEndianness::LeWire)    // Set a sensible default. WaitForStreamMeta will set a real value.
+        , m_Endianness(TargetEndianness::LeWire)    // Set a sensible default.
+                                                    // StreamMetaDataProcessor will set a real value.
         , m_IsRunning(false)
         , m_KeepRunning(false)
         , m_Timeout(1000)
     {
-        for (ILocalPacketHandlerSharedPtr localPacketHandler : options.m_LocalPacketHandlers)
+        // add the StreamMetaDataProcessor
+        auto streamMetaDataProcessor = std::make_shared<StreamMetaDataProcessor>(this);
+        AddLocalPacketHandler(streamMetaDataProcessor);
+        // and any additional ones added by the users
+        for (const ILocalPacketHandlerSharedPtr& localPacketHandler : options.m_LocalPacketHandlers)
         {
             AddLocalPacketHandler(localPacketHandler);
         }
-        if (!options.m_LocalPacketHandlers.empty())
+        if (!m_PacketHandlers.empty())
         {
             StartProcessingThread();
         }
         // NOTE: could add timeout to the external profiling options
     };
 
-    ~FileOnlyProfilingConnection();
+    ~FileOnlyProfilingConnection() override;
 
     bool IsOpen() const override;
 
@@ -73,30 +84,25 @@
     // Sending a packet back to ArmNN.
     Packet ReadPacket(uint32_t timeout) override;
 
+    void SetEndianess(const TargetEndianness& endianness) override //IInternalProfilingConnection
+    {
+        m_Endianness = endianness;
+    }
+
+    void ReturnPacket(Packet& packet) override; //IInternalProfilingConnection
+
 private:
     void AddLocalPacketHandler(ILocalPacketHandlerSharedPtr localPacketHandler);
     void StartProcessingThread();
     void ClearReadableList();
     void DispatchPacketToHandlers(const Packet& packet);
 
-    bool WaitForStreamMeta(const unsigned char* buffer, uint32_t length);
-
-    uint32_t ToUint32(const unsigned char* data, TargetEndianness endianness);
-
-    void SendConnectionAck();
-
-    bool SendCounterSelectionPacket();
-
-    PackageActivity GetPackageActivity(const Packet& packet, uint32_t headerAsWords[2]);
-
     void Fail(const std::string& errorMessage);
 
     void ForwardPacketToHandlers(Packet& packet);
     void ServiceLocalHandlers();
 
     Runtime::CreationOptions::ExternalProfilingOptions m_Options;
-    bool m_QuietOp;
-    std::vector<uint16_t> m_IdList;
     std::queue<Packet> m_PacketQueue;
     TargetEndianness m_Endianness;
 
diff --git a/src/profiling/test/FileOnlyProfilingDecoratorTests.cpp b/src/profiling/test/FileOnlyProfilingDecoratorTests.cpp
index 80236ae..aa877a1 100644
--- a/src/profiling/test/FileOnlyProfilingDecoratorTests.cpp
+++ b/src/profiling/test/FileOnlyProfilingDecoratorTests.cpp
@@ -49,8 +49,11 @@
 
 BOOST_AUTO_TEST_CASE(TestFileOnlyProfiling)
 {
-    // This test requires the CpuRef backend to be enabled
-    if(!BackendRegistryInstance().IsBackendRegistered("CpuRef"))
+    // This test requires at least one backend registry to be enabled
+    // which can execute a NormalizationLayer
+    if (BackendRegistryInstance().IsBackendRegistered(GetComputeDeviceAsCString(armnn::Compute::CpuRef)) ||
+        BackendRegistryInstance().IsBackendRegistered(GetComputeDeviceAsCString(armnn::Compute::CpuAcc)) ||
+        BackendRegistryInstance().IsBackendRegistered(GetComputeDeviceAsCString(armnn::Compute::GpuAcc)))
     {
         return;
     }
@@ -87,13 +90,30 @@
     normalize->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 4 }, DataType::Float32));
 
     // optimize the network
-    std::vector<armnn::BackendId> backends = { armnn::Compute::CpuRef };
+    std::vector<armnn::BackendId> backends =
+            { armnn::Compute::CpuRef, armnn::Compute::CpuAcc, armnn::Compute::GpuAcc };
     IOptimizedNetworkPtr optNet = Optimize(*net, backends, runtime.GetDeviceSpec());
 
     // Load it into the runtime. It should succeed.
     armnn::NetworkId netId;
     BOOST_TEST(runtime.LoadNetwork(netId, std::move(optNet)) == Status::Success);
 
+    // Creates structures for input & output.
+    std::vector<float> inputData(16);
+    std::vector<float> outputData(16);
+
+    InputTensors  inputTensors
+    {
+        {0, ConstTensor(runtime.GetInputTensorInfo(netId, 0), inputData.data())}
+    };
+    OutputTensors outputTensors
+    {
+        {0, Tensor(runtime.GetOutputTensorInfo(netId, 0), outputData.data())}
+    };
+
+    // Does the inference.
+    runtime.EnqueueWorkload(netId, inputTensors, outputTensors);
+
     static_cast<TestTimelinePacketHandler*>(localPacketHandlerPtr.get())->WaitOnInferenceCompletion(3000);
 }
 
diff --git a/src/profiling/test/RequestCountersPacketHandler.cpp b/src/profiling/test/RequestCountersPacketHandler.cpp
new file mode 100644
index 0000000..76c4b0c
--- /dev/null
+++ b/src/profiling/test/RequestCountersPacketHandler.cpp
@@ -0,0 +1,78 @@
+//
+// Copyright © 2020 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "RequestCountersPacketHandler.hpp"
+
+#include "DirectoryCaptureCommandHandler.hpp"
+#include "PacketVersionResolver.hpp"
+
+#include <common/include/ProfilingException.hpp>
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+std::vector<uint32_t> RequestCountersPacketHandler::GetHeadersAccepted()
+{
+    std::vector<uint32_t> headers;
+    headers.push_back(m_CounterDirectoryMessageHeader); // counter directory
+    return headers;
+}
+
+void RequestCountersPacketHandler::HandlePacket(const Packet& packet)
+{
+    if (packet.GetHeader() != m_CounterDirectoryMessageHeader)
+    {
+        return;
+    }
+    armnn::profiling::PacketVersionResolver packetVersionResolver;
+    DirectoryCaptureCommandHandler directoryCaptureCommandHandler(
+            0, 2, packetVersionResolver.ResolvePacketVersion(0, 2).GetEncodedValue());
+    directoryCaptureCommandHandler.operator()(packet);
+    const ICounterDirectory& counterDirectory = directoryCaptureCommandHandler.GetCounterDirectory();
+    for (auto& category : counterDirectory.GetCategories())
+    {
+        // Remember we need to translate the Uid's from our CounterDirectory instance to the parent one.
+        std::vector<uint16_t> translatedCounters;
+        for (auto const& copyUid : category->m_Counters)
+        {
+            translatedCounters.emplace_back(directoryCaptureCommandHandler.TranslateUIDCopyToOriginal(copyUid));
+        }
+        m_IdList.insert(std::end(m_IdList), std::begin(translatedCounters), std::end(translatedCounters));
+    }
+    SendCounterSelectionPacket();
+}
+
+void RequestCountersPacketHandler::SendCounterSelectionPacket()
+{
+    uint32_t uint16_t_size = sizeof(uint16_t);
+    uint32_t uint32_t_size = sizeof(uint32_t);
+
+    uint32_t offset   = 0;
+    uint32_t bodySize = uint32_t_size + boost::numeric_cast<uint32_t>(m_IdList.size()) * uint16_t_size;
+
+    auto uniqueData     = std::make_unique<unsigned char[]>(bodySize);
+    auto data = reinterpret_cast<unsigned char*>(uniqueData.get());
+
+    // Copy capturePeriod
+    WriteUint32(data, offset, m_CapturePeriod);
+
+    // Copy m_IdList
+    offset += uint32_t_size;
+    for (const uint16_t& id : m_IdList)
+    {
+        WriteUint16(data, offset, id);
+        offset += uint16_t_size;
+    }
+
+    Packet packet(0x40000, bodySize, uniqueData);
+    m_Connection->ReturnPacket(packet);
+}
+
+} // namespace profiling
+
+} // namespace armnn
\ No newline at end of file
diff --git a/src/profiling/test/RequestCountersPacketHandler.hpp b/src/profiling/test/RequestCountersPacketHandler.hpp
new file mode 100644
index 0000000..203edcc
--- /dev/null
+++ b/src/profiling/test/RequestCountersPacketHandler.hpp
@@ -0,0 +1,47 @@
+//
+// Copyright © 2020 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <armnn/Types.hpp>
+#include <armnn/profiling/ILocalPacketHandler.hpp>
+#include "Packet.hpp"
+#include "ProfilingUtils.hpp"
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+class RequestCountersPacketHandler : public ILocalPacketHandler
+{
+public:
+    explicit RequestCountersPacketHandler(uint32_t capturePeriod = LOWEST_CAPTURE_PERIOD) :
+        m_CapturePeriod(capturePeriod),
+        m_Connection(nullptr),
+        m_CounterDirectoryMessageHeader(ConstructHeader(0, 2)) {}
+
+    std::vector<uint32_t> GetHeadersAccepted() override; // ILocalPacketHandler
+
+    void HandlePacket(const Packet& packet) override; // ILocalPacketHandler
+
+    void SetConnection(IInternalProfilingConnection* profilingConnection) override // ILocalPacketHandler
+    {
+        m_Connection = profilingConnection;
+    }
+
+private:
+    uint32_t m_CapturePeriod;
+    IInternalProfilingConnection* m_Connection;
+    uint32_t m_CounterDirectoryMessageHeader;
+    std::vector<uint16_t> m_IdList;
+
+    void SendCounterSelectionPacket();
+};
+
+} // namespace profiling
+
+} // namespace armnn
\ No newline at end of file
diff --git a/src/profiling/test/TestTimelinePacketHandler.hpp b/src/profiling/test/TestTimelinePacketHandler.hpp
index 6739525..6cc6a0c 100644
--- a/src/profiling/test/TestTimelinePacketHandler.hpp
+++ b/src/profiling/test/TestTimelinePacketHandler.hpp
@@ -66,7 +66,7 @@
 
     const TimelineModel& GetTimelineModel() const {return m_TimelineModel;}
 
-    virtual void SetConnection(IProfilingConnection* profilingConnection) override // ILocalPacketHandler
+    virtual void SetConnection(IInternalProfilingConnection* profilingConnection) override // ILocalPacketHandler
     {
         m_Connection = profilingConnection;
     }
@@ -74,7 +74,7 @@
 private:
     void ProcessDirectoryPacket(const Packet& packet);
     void ProcessMessagePacket(const Packet& packet);
-    IProfilingConnection* m_Connection;
+    IInternalProfilingConnection* m_Connection;
     std::mutex m_InferenceCompletedMutex;
     std::condition_variable m_InferenceCompletedConditionVariable;
     bool m_InferenceCompleted;