IVGCVSW-3971 Implement the Per-Job Counter Selection command handler

 * Added new PerJobCounterSelectionCommandHandler class
 * The new handler drops the incoming packet without altering the
   state of the profiling service
 * Added unit test

Signed-off-by: Matteo Martincigh <matteo.martincigh@arm.com>
Change-Id: I2b1bb803318a9e6c438391a0985893eb412e7787
diff --git a/src/profiling/PerJobCounterSelectionCommandHandler.cpp b/src/profiling/PerJobCounterSelectionCommandHandler.cpp
new file mode 100644
index 0000000..8892e14
--- /dev/null
+++ b/src/profiling/PerJobCounterSelectionCommandHandler.cpp
@@ -0,0 +1,48 @@
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "PerJobCounterSelectionCommandHandler.hpp"
+
+#include <boost/format.hpp>
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+void PerJobCounterSelectionCommandHandler::operator()(const Packet& packet)
+{
+    ProfilingState currentState = m_StateMachine.GetCurrentState();
+    switch (currentState)
+    {
+    case ProfilingState::Uninitialised:
+    case ProfilingState::NotConnected:
+    case ProfilingState::WaitingForAck:
+        throw RuntimeException(boost::str(boost::format("Per-Job Counter Selection Command Handler invoked while in "
+                                                        "an wrong state: %1%")
+                                          % GetProfilingStateName(currentState)));
+    case ProfilingState::Active:
+        // Process the packet
+        if (!(packet.GetPacketFamily() == 0u && packet.GetPacketId() == 5u))
+        {
+            throw armnn::InvalidArgumentException(boost::str(boost::format("Expected Packet family = 0, id = 5 but "
+                                                                           "received family = %1%, id = %2%")
+                                                  % packet.GetPacketFamily()
+                                                  % packet.GetPacketId()));
+        }
+
+        // Silently drop the packet
+
+        break;
+    default:
+        throw RuntimeException(boost::str(boost::format("Unknown profiling service state: %1%")
+                                          % static_cast<int>(currentState)));
+    }
+}
+
+} // namespace profiling
+
+} // namespace armnn
diff --git a/src/profiling/PerJobCounterSelectionCommandHandler.hpp b/src/profiling/PerJobCounterSelectionCommandHandler.hpp
new file mode 100644
index 0000000..6caa08d
--- /dev/null
+++ b/src/profiling/PerJobCounterSelectionCommandHandler.hpp
@@ -0,0 +1,38 @@
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "Packet.hpp"
+#include "CommandHandlerFunctor.hpp"
+#include "ProfilingStateMachine.hpp"
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+class PerJobCounterSelectionCommandHandler : public CommandHandlerFunctor
+{
+
+public:
+    PerJobCounterSelectionCommandHandler(uint32_t packetId,
+                                           uint32_t version,
+                                           const ProfilingStateMachine& profilingStateMachine)
+        : CommandHandlerFunctor(packetId, version)
+        , m_StateMachine(profilingStateMachine)
+    {}
+
+    void operator()(const Packet& packet) override;
+
+private:
+    const ProfilingStateMachine& m_StateMachine;
+};
+
+} // namespace profiling
+
+} // namespace armnn
+
diff --git a/src/profiling/ProfilingService.hpp b/src/profiling/ProfilingService.hpp
index d4fa856..1afcb1c 100644
--- a/src/profiling/ProfilingService.hpp
+++ b/src/profiling/ProfilingService.hpp
@@ -16,6 +16,7 @@
 #include "ConnectionAcknowledgedCommandHandler.hpp"
 #include "RequestCounterDirectoryCommandHandler.hpp"
 #include "PeriodicCounterSelectionCommandHandler.hpp"
+#include "PerJobCounterSelectionCommandHandler.hpp"
 
 namespace armnn
 {
@@ -96,6 +97,7 @@
     ConnectionAcknowledgedCommandHandler m_ConnectionAcknowledgedCommandHandler;
     RequestCounterDirectoryCommandHandler m_RequestCounterDirectoryCommandHandler;
     PeriodicCounterSelectionCommandHandler m_PeriodicCounterSelectionCommandHandler;
+    PerJobCounterSelectionCommandHandler m_PerJobCounterSelectionCommandHandler;
 
 protected:
     // Default constructor/destructor kept protected for testing
@@ -131,6 +133,9 @@
                                                    *this,
                                                    m_SendCounterPacket,
                                                    m_StateMachine)
+        , m_PerJobCounterSelectionCommandHandler(5,
+                                                 m_PacketVersionResolver.ResolvePacketVersion(4).GetEncodedValue(),
+                                                 m_StateMachine)
     {
         // Register the "Connection Acknowledged" command handler
         m_CommandHandlerRegistry.RegisterFunctor(&m_ConnectionAcknowledgedCommandHandler);
@@ -140,6 +145,9 @@
 
         // Register the "Periodic Counter Selection" command handler
         m_CommandHandlerRegistry.RegisterFunctor(&m_PeriodicCounterSelectionCommandHandler);
+
+        // Register the "Per-Job Counter Selection" command handler
+        m_CommandHandlerRegistry.RegisterFunctor(&m_PerJobCounterSelectionCommandHandler);
     }
     ~ProfilingService() = default;
 
diff --git a/src/profiling/test/ProfilingTests.cpp b/src/profiling/test/ProfilingTests.cpp
index 446c609..6437521 100644
--- a/src/profiling/test/ProfilingTests.cpp
+++ b/src/profiling/test/ProfilingTests.cpp
@@ -3072,4 +3072,74 @@
     profilingService.ResetExternalProfilingOptions(options, true);
 }
 
+BOOST_AUTO_TEST_CASE(CheckProfilingServiceGoodPerJobCounterSelectionPacket)
+{
+    // Swap the profiling connection factory in the profiling service instance with our mock one
+    SwapProfilingConnectionFactoryHelper helper;
+
+    // Reset the profiling service to the uninitialized state
+    armnn::Runtime::CreationOptions::ExternalProfilingOptions options;
+    options.m_EnableProfiling = true;
+    ProfilingService& profilingService = ProfilingService::Instance();
+    profilingService.ResetExternalProfilingOptions(options, true);
+
+    // Bring the profiling service to the "Active" state
+    BOOST_CHECK(profilingService.GetCurrentState() == ProfilingState::Uninitialised);
+    profilingService.Update(); // Initialize the counter directory
+    BOOST_CHECK(profilingService.GetCurrentState() == ProfilingState::NotConnected);
+    profilingService.Update(); // Create the profiling connection
+    BOOST_CHECK(profilingService.GetCurrentState() == ProfilingState::WaitingForAck);
+    profilingService.Update(); // Start the command handler and the send thread
+
+    // Wait for the Stream Metadata packet the be sent
+    // (we are not testing the connection acknowledgement here so it will be ignored by this test)
+    helper.WaitForProfilingPacketsSent();
+
+    // Force the profiling service to the "Active" state
+    helper.ForceTransitionToState(ProfilingState::Active);
+    BOOST_CHECK(profilingService.GetCurrentState() == ProfilingState::Active);
+
+    // Get the mock profiling connection
+    MockProfilingConnection* mockProfilingConnection = helper.GetMockProfilingConnection();
+    BOOST_CHECK(mockProfilingConnection);
+
+    // Remove the packets received so far
+    mockProfilingConnection->Clear();
+
+    // Write a "Per-Job Counter Selection" packet into the mock profiling connection, to simulate an input from an
+    // external profiling service
+
+    // Per-Job Counter Selection packet header:
+    // 26:31 [6]  packet_family: Control Packet Family, value 0b000000
+    // 16:25 [10] packet_id: Packet identifier, value 0b0000000100
+    // 8:15  [8]  reserved: Reserved, value 0b00000000
+    // 0:7   [8]  reserved: Reserved, value 0b00000000
+    uint32_t packetFamily = 0;
+    uint32_t packetId     = 5;
+    uint32_t header = ((packetFamily & 0x0000003F) << 26) |
+                      ((packetId     & 0x000003FF) << 16);
+
+    // Create the Per-Job Counter Selection packet
+    Packet periodicCounterSelectionPacket(header); // Length == 0, this will disable the collection of counters
+
+    // Write the packet to the mock profiling connection
+    mockProfilingConnection->WritePacket(std::move(periodicCounterSelectionPacket));
+
+    // Wait for a bit (must at least be the delay value of the mock profiling connection) to make sure that
+    // the Per-Job Counter Selection packet gets processed by the profiling service
+    std::this_thread::sleep_for(std::chrono::seconds(2));
+
+    // The Per-Job Counter Selection packets are dropped silently, so there should be no reply coming
+    // from the profiling service
+    const std::vector<uint32_t> writtenData = mockProfilingConnection->GetWrittenData();
+    BOOST_TEST(writtenData.empty());
+
+    // The Per-Job Counter Selection Command Handler should not have updated the profiling state
+    BOOST_CHECK(profilingService.GetCurrentState() == ProfilingState::Active);
+
+    // Reset the profiling service to stop any running thread
+    options.m_EnableProfiling = false;
+    profilingService.ResetExternalProfilingOptions(options, true);
+}
+
 BOOST_AUTO_TEST_SUITE_END()