IVGCVSW-3693 Implement SendCounterPacket.SendPeriodicCounterSelectionPacket() function

Signed-off-by: Ferran Balaguer <ferran.balaguer@arm.com>
Change-Id: Ib034a4f5ca589759d925e3dd0ca50e5a3dfa74c5
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8cc1d2c..1a07f69 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -429,6 +429,10 @@
     src/profiling/ProfilingConnectionFactory.hpp
     src/profiling/IBufferWrapper.hpp
     src/profiling/ISendCounterPacket.hpp
+    src/profiling/SendCounterPacket.hpp
+    src/profiling/SendCounterPacket.cpp
+    src/profiling/ProfilingUtils.hpp
+    src/profiling/ProfilingUtils.cpp
     third-party/half/half.hpp
     )
 
diff --git a/src/profiling/ProfilingUtils.cpp b/src/profiling/ProfilingUtils.cpp
new file mode 100644
index 0000000..4dec7be
--- /dev/null
+++ b/src/profiling/ProfilingUtils.cpp
@@ -0,0 +1,58 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "ProfilingUtils.hpp"
+
+#include <boost/assert.hpp>
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+void WriteUint32(unsigned char* buffer, unsigned int offset, uint32_t value)
+{
+    BOOST_ASSERT(buffer);
+
+    buffer[offset] = static_cast<unsigned char>(value & 0xFF);
+    buffer[offset + 1] = static_cast<unsigned char>((value >> 8) & 0xFF);
+    buffer[offset + 2] = static_cast<unsigned char>((value >> 16) & 0xFF);
+    buffer[offset + 3] = static_cast<unsigned char>((value >> 24) & 0xFF);
+}
+
+void WriteUint16(unsigned char* buffer, unsigned int offset, uint16_t value)
+{
+    BOOST_ASSERT(buffer != nullptr);
+
+    buffer[offset] = static_cast<unsigned char>(value & 0xFF);
+    buffer[offset + 1] = static_cast<unsigned char>((value >> 8) & 0xFF);
+}
+
+uint32_t ReadUint32(const unsigned char* buffer, unsigned int offset)
+{
+    BOOST_ASSERT(buffer);
+
+    uint32_t value = 0;
+    value = static_cast<uint32_t>(buffer[offset]);
+    value |= static_cast<uint32_t>(buffer[offset + 1]) << 8;
+    value |= static_cast<uint32_t>(buffer[offset + 2]) << 16;
+    value |= static_cast<uint32_t>(buffer[offset + 3]) << 24;
+    return value;
+}
+
+uint16_t ReadUint16(const unsigned char* buffer, unsigned int offset)
+{
+    BOOST_ASSERT(buffer);
+
+    uint32_t value = 0;
+    value = static_cast<uint32_t>(buffer[offset]);
+    value |= static_cast<uint32_t>(buffer[offset + 1]) << 8;
+    return static_cast<uint16_t>(value);
+}
+
+} // namespace profiling
+
+} // namespace armnn
\ No newline at end of file
diff --git a/src/profiling/ProfilingUtils.hpp b/src/profiling/ProfilingUtils.hpp
new file mode 100644
index 0000000..12770aa
--- /dev/null
+++ b/src/profiling/ProfilingUtils.hpp
@@ -0,0 +1,26 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <stdint.h>
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+void WriteUint32(unsigned char* buffer, unsigned int offset, uint32_t value);
+
+void WriteUint16(unsigned char* buffer, unsigned int offset, uint16_t value);
+
+uint32_t ReadUint32(const unsigned char* buffer, unsigned int offset);
+
+uint16_t ReadUint16(const unsigned char* buffer, unsigned int offset);
+
+} // namespace profiling
+
+} // namespace armnn
\ No newline at end of file
diff --git a/src/profiling/SendCounterPacket.cpp b/src/profiling/SendCounterPacket.cpp
new file mode 100644
index 0000000..b99584c
--- /dev/null
+++ b/src/profiling/SendCounterPacket.cpp
@@ -0,0 +1,96 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "SendCounterPacket.hpp"
+#include "EncodeVersion.hpp"
+#include "ProfilingUtils.hpp"
+
+#include <armnn/Exceptions.hpp>
+
+#include <boost/format.hpp>
+#include <boost/numeric/conversion/cast.hpp>
+
+#include <unistd.h>
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+using boost::numeric_cast;
+
+void SendCounterPacket::SendStreamMetaDataPacket()
+{
+    throw armnn::UnimplementedException();
+}
+
+void SendCounterPacket::SendCounterDirectoryPacket(const Category& category, const std::vector<Counter>& counters)
+{
+    throw armnn::UnimplementedException();
+}
+
+void SendCounterPacket::SendPeriodicCounterCapturePacket(uint64_t timestamp, const std::vector<uint32_t>& counterValues,
+                                                         const std::vector<uint16_t>& counterUids)
+{
+    throw armnn::UnimplementedException();
+}
+
+void SendCounterPacket::SendPeriodicCounterSelectionPacket(uint32_t capturePeriod,
+                                                           const std::vector<uint16_t>& selectedCounterIds)
+{
+    uint32_t packetFamily = 0;
+    uint32_t packetId = 4;
+    uint32_t headerSize = numeric_cast<uint32_t>(2 * sizeof(uint32_t));
+    uint32_t bodySize = numeric_cast<uint32_t>((1 * sizeof(uint32_t)) + (selectedCounterIds.size() * sizeof(uint16_t)));
+    uint32_t totalSize = headerSize + bodySize;
+    uint32_t offset = 0;
+    uint32_t reserved = 0;
+
+    unsigned char* writeBuffer = m_Buffer.Reserve(totalSize, reserved);
+
+    if (reserved < totalSize)
+    {
+        // Cancel the operation.
+        m_Buffer.Commit(0);
+        throw RuntimeException(boost::str(boost::format("No space left in buffer. Unable to reserve (%1%) bytes.")
+                               % totalSize));
+    }
+
+    if (writeBuffer == nullptr)
+    {
+        // Cancel the operation.
+        m_Buffer.Commit(0);
+        throw RuntimeException("Error reserving buffer memory.");
+    }
+
+    // Create header.
+    WriteUint32(writeBuffer, offset, ((packetFamily & 0x3F) << 26) | ((packetId & 0x3FF) << 16));
+    offset += numeric_cast<uint32_t>(sizeof(uint32_t));
+    WriteUint32(writeBuffer, offset, bodySize);
+
+    // Copy capturePeriod.
+    offset += numeric_cast<uint32_t>(sizeof(uint32_t));
+    WriteUint32(writeBuffer, offset, capturePeriod);
+
+    // Copy selectedCounterIds.
+    offset += numeric_cast<uint32_t>(sizeof(uint32_t));
+    for(const uint16_t& id: selectedCounterIds)
+    {
+        WriteUint16(writeBuffer, offset, id);
+        offset += numeric_cast<uint32_t>(sizeof(uint16_t));
+    }
+
+    m_Buffer.Commit(totalSize);
+}
+
+void SendCounterPacket::SetReadyToRead()
+{
+    m_ReadyToRead = true;
+}
+
+} // namespace profiling
+
+} // namespace armnn
\ No newline at end of file
diff --git a/src/profiling/SendCounterPacket.hpp b/src/profiling/SendCounterPacket.hpp
new file mode 100644
index 0000000..c1432b3
--- /dev/null
+++ b/src/profiling/SendCounterPacket.hpp
@@ -0,0 +1,43 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "IBufferWrapper.hpp"
+#include "ISendCounterPacket.hpp"
+#include "CounterDirectory.hpp"
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+class SendCounterPacket : public ISendCounterPacket
+{
+public:
+    SendCounterPacket(IBufferWrapper& buffer) : m_Buffer(buffer), m_ReadyToRead(false) {}
+
+    void SendStreamMetaDataPacket() override;
+
+    void SendCounterDirectoryPacket(const Category& category, const std::vector<Counter>& counters) override;
+
+    void SendPeriodicCounterCapturePacket(uint64_t timestamp, const std::vector<uint32_t>& counterValues,
+                                          const std::vector<uint16_t>& counterUids) override;
+
+    void SendPeriodicCounterSelectionPacket(uint32_t capturePeriod,
+                                            const std::vector<uint16_t>& selectedCounterIds) override;
+
+    void SetReadyToRead() override;
+
+private:
+    IBufferWrapper& m_Buffer;
+    bool m_ReadyToRead;
+};
+
+} // namespace profiling
+
+} // namespace armnn
+
diff --git a/src/profiling/test/SendCounterPacketTests.cpp b/src/profiling/test/SendCounterPacketTests.cpp
index 9a314c1..42a261b 100644
--- a/src/profiling/test/SendCounterPacketTests.cpp
+++ b/src/profiling/test/SendCounterPacketTests.cpp
@@ -3,12 +3,14 @@
 // SPDX-License-Identifier: MIT
 //
 
-#include "../IBufferWrapper.hpp"
-#include "../ISendCounterPacket.hpp"
+#include "../SendCounterPacket.hpp"
+#include "../ProfilingUtils.hpp"
 
-#include <iostream>
+#include <armnn/Exceptions.hpp>
+
 #include <boost/test/unit_test.hpp>
 
+#include <iostream>
 
 BOOST_AUTO_TEST_SUITE(SendCounterPacketTests)
 
@@ -17,7 +19,9 @@
 class MockBuffer : public IBufferWrapper
 {
 public:
-    MockBuffer() : m_Buffer() {}
+    MockBuffer(unsigned int size)
+    : m_BufferSize(size),
+      m_Buffer(std::make_unique<unsigned char[]>(size)) {}
 
     unsigned char* Reserve(unsigned int requestedSize, unsigned int& reservedSize) override
     {
@@ -30,22 +34,22 @@
             reservedSize = requestedSize;
         }
 
-        return m_Buffer;
+        return m_Buffer.get();
     }
 
     void Commit(unsigned int size) override {}
 
     const unsigned char* GetReadBuffer(unsigned int& size) override
     {
-        size = static_cast<unsigned int>(strlen(reinterpret_cast<const char*>(m_Buffer)) + 1);
-        return m_Buffer;
+        size = static_cast<unsigned int>(strlen(reinterpret_cast<const char*>(m_Buffer.get())) + 1);
+        return m_Buffer.get();
     }
 
     void Release( unsigned int size) override {}
 
 private:
-    static const unsigned int m_BufferSize = 512;
-    unsigned char m_Buffer[m_BufferSize];
+    unsigned int m_BufferSize;
+    std::unique_ptr<unsigned char[]> m_Buffer;
 };
 
 class MockSendCounterPacket : public ISendCounterPacket
@@ -99,7 +103,7 @@
 {
     unsigned int size = 0;
 
-    MockBuffer mockBuffer;
+    MockBuffer mockBuffer(512);
     MockSendCounterPacket sendCounterPacket(mockBuffer);
 
     sendCounterPacket.SendStreamMetaDataPacket();
@@ -128,4 +132,67 @@
 
 }
 
+BOOST_AUTO_TEST_CASE(SendPeriodicCounterSelectionPacketTest)
+{
+    // Error no space left in buffer
+    MockBuffer mockBuffer1(10);
+    SendCounterPacket sendPacket1(mockBuffer1);
+
+    uint32_t capturePeriod = 1000;
+    std::vector<uint16_t> selectedCounterIds;
+    BOOST_CHECK_THROW(sendPacket1.SendPeriodicCounterSelectionPacket(capturePeriod, selectedCounterIds),
+                      armnn::Exception);
+
+    // Packet without any counters
+    MockBuffer mockBuffer2(512);
+    SendCounterPacket sendPacket2(mockBuffer2);
+
+    sendPacket2.SendPeriodicCounterSelectionPacket(capturePeriod, selectedCounterIds);
+    unsigned int sizeRead = 0;
+    const unsigned char* readBuffer2 = mockBuffer2.GetReadBuffer(sizeRead);
+
+    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
+
+    // Full packet message
+    MockBuffer 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);
+    sizeRead = 0;
+    const unsigned char* readBuffer3 = mockBuffer3.GetReadBuffer(sizeRead);
+
+    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_SUITE_END()