IVGCVSW-3690 Implement SendCounterPacket.SendStreamMetaDataPacket() function

Signed-off-by: Ferran Balaguer <ferran.balaguer@arm.com>
Change-Id: I9e034b0caaff5371fadfdba20fdbe3171d4cd2e6
diff --git a/src/profiling/ProfilingUtils.cpp b/src/profiling/ProfilingUtils.cpp
index b948026..86abef1 100644
--- a/src/profiling/ProfilingUtils.cpp
+++ b/src/profiling/ProfilingUtils.cpp
@@ -5,8 +5,12 @@
 
 #include "ProfilingUtils.hpp"
 
+#include <armnn/Version.hpp>
+
 #include <boost/assert.hpp>
 
+#include <fstream>
+
 namespace armnn
 {
 
@@ -84,6 +88,31 @@
     return static_cast<uint16_t>(value);
 }
 
+std::string GetSoftwareInfo()
+{
+    return std::string("ArmNN");
+}
+
+std::string GetHardwareVersion()
+{
+    return std::string();
+}
+
+std::string GetSoftwareVersion()
+{
+    std::string armnnVersion(ARMNN_VERSION);
+    std::string result = "Armnn " + armnnVersion.substr(2,2) + "." + armnnVersion.substr(4,2);
+    return result;
+}
+
+std::string GetProcessName()
+{
+    std::ifstream comm("/proc/self/comm");
+    std::string name;
+    getline(comm, name);
+    return name;
+}
+
 } // namespace profiling
 
 } // namespace armnn
\ No newline at end of file
diff --git a/src/profiling/ProfilingUtils.hpp b/src/profiling/ProfilingUtils.hpp
index fe58ee1..410198a 100644
--- a/src/profiling/ProfilingUtils.hpp
+++ b/src/profiling/ProfilingUtils.hpp
@@ -7,6 +7,7 @@
 
 #include <armnn/Exceptions.hpp>
 
+#include <string>
 #include <stdint.h>
 
 namespace armnn
@@ -27,6 +28,14 @@
 
 uint16_t ReadUint16(const unsigned char* buffer, unsigned int offset);
 
+std::string GetSoftwareInfo();
+
+std::string GetSoftwareVersion();
+
+std::string GetHardwareVersion();
+
+std::string GetProcessName();
+
 class BufferExhaustion : public armnn::Exception
 {
     using Exception::Exception;
@@ -34,4 +43,4 @@
 
 } // namespace profiling
 
-} // namespace armnn
\ No newline at end of file
+} // namespace armnn
diff --git a/src/profiling/SendCounterPacket.cpp b/src/profiling/SendCounterPacket.cpp
index 645b791..61d32da 100644
--- a/src/profiling/SendCounterPacket.cpp
+++ b/src/profiling/SendCounterPacket.cpp
@@ -11,8 +11,11 @@
 
 #include <boost/format.hpp>
 #include <boost/numeric/conversion/cast.hpp>
+#include <boost/core/ignore_unused.hpp>
 
+#include <iostream>
 #include <unistd.h>
+#include <string.h>
 
 namespace armnn
 {
@@ -22,9 +25,147 @@
 
 using boost::numeric_cast;
 
+const unsigned int SendCounterPacket::PIPE_MAGIC;
+const unsigned int SendCounterPacket::MAX_METADATA_PACKET_LENGTH;
+
 void SendCounterPacket::SendStreamMetaDataPacket()
 {
-    throw armnn::UnimplementedException();
+    std::string info(GetSoftwareInfo());
+    std::string hardwareVersion(GetHardwareVersion());
+    std::string softwareVersion(GetSoftwareVersion());
+    std::string processName = GetProcessName().substr(0, 60);
+
+    uint32_t infoSize = numeric_cast<uint32_t>(info.size()) > 0 ? numeric_cast<uint32_t>(info.size()) + 1 : 0;
+    uint32_t hardwareVersionSize = numeric_cast<uint32_t>(hardwareVersion.size()) > 0 ?
+                                   numeric_cast<uint32_t>(hardwareVersion.size()) + 1 : 0;
+    uint32_t softwareVersionSize = numeric_cast<uint32_t>(softwareVersion.size()) > 0 ?
+                                   numeric_cast<uint32_t>(softwareVersion.size()) + 1 : 0;
+    uint32_t processNameSize = numeric_cast<uint32_t>(processName.size()) > 0 ?
+                               numeric_cast<uint32_t>(processName.size()) + 1 : 0;
+
+    uint32_t sizeUint32 = numeric_cast<uint32_t>(sizeof(uint32_t));
+
+    uint32_t headerSize = 2 * sizeUint32;
+    uint32_t bodySize = 10 * sizeUint32;
+    uint32_t packetVersionCountSize = sizeUint32;
+
+    // Supported Packets
+    // Stream metadata packet            (packet family=0; packet id=0)
+    // Connection Acknowledged packet    (packet family=0, packet id=1)
+    // Counter Directory packet          (packet family=0; packet id=2)
+    // Request Counter Directory packet  (packet family=0, packet id=3)
+    // Periodic Counter Selection packet (packet family=0, packet id=4)
+    uint32_t packetVersionEntries = 5;
+
+    uint32_t payloadSize = numeric_cast<uint32_t>(infoSize + hardwareVersionSize + softwareVersionSize +
+                                                  processNameSize + packetVersionCountSize +
+                                                  (packetVersionEntries * 2 * sizeUint32));
+
+    uint32_t totalSize = headerSize + bodySize + payloadSize;
+    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.");
+    }
+
+    try
+    {
+        // Create header
+
+        WriteUint32(writeBuffer, offset, 0);
+        offset += sizeUint32;
+        WriteUint32(writeBuffer, offset, totalSize - headerSize);
+
+        // Packet body
+
+        offset += sizeUint32;
+        WriteUint32(writeBuffer, offset, PIPE_MAGIC); // pipe_magic
+        offset += sizeUint32;
+        WriteUint32(writeBuffer, offset, EncodeVersion(1, 0, 0)); // stream_metadata_version
+        offset += sizeUint32;
+        WriteUint32(writeBuffer, offset, MAX_METADATA_PACKET_LENGTH); // max_data_length
+        offset += sizeUint32;
+        WriteUint32(writeBuffer, offset, numeric_cast<uint32_t>(getpid())); // pid
+        offset += sizeUint32;
+        uint32_t poolOffset = bodySize;
+        WriteUint32(writeBuffer, offset, infoSize ? poolOffset : 0); // offset_info
+        offset += sizeUint32;
+        poolOffset += infoSize;
+        WriteUint32(writeBuffer, offset, hardwareVersionSize ? poolOffset : 0); // offset_hw_version
+        offset += sizeUint32;
+        poolOffset += hardwareVersionSize;
+        WriteUint32(writeBuffer, offset, softwareVersionSize ? poolOffset : 0); // offset_sw_version
+        offset += sizeUint32;
+        poolOffset += softwareVersionSize;
+        WriteUint32(writeBuffer, offset, processNameSize ? poolOffset : 0); // offset_process_name
+        offset += sizeUint32;
+        poolOffset += processNameSize;
+        WriteUint32(writeBuffer, offset, packetVersionEntries ? poolOffset : 0); // offset_packet_version_table
+        offset += sizeUint32;
+        WriteUint32(writeBuffer, offset, 0); // reserved
+        offset += sizeUint32;
+
+        // Pool
+
+        if (infoSize) {
+            memcpy(&writeBuffer[offset], info.c_str(), infoSize);
+            offset += infoSize;
+        }
+
+        if (hardwareVersionSize) {
+            memcpy(&writeBuffer[offset], hardwareVersion.c_str(), hardwareVersionSize);
+            offset += hardwareVersionSize;
+        }
+
+        if (softwareVersionSize) {
+            memcpy(&writeBuffer[offset], softwareVersion.c_str(), softwareVersionSize);
+            offset += softwareVersionSize;
+        }
+
+        if (processNameSize) {
+            memcpy(&writeBuffer[offset], processName.c_str(), processNameSize);
+            offset += processNameSize;
+        }
+
+        if (packetVersionEntries) {
+            // Packet Version Count
+            WriteUint32(writeBuffer, offset, packetVersionEntries << 16);
+
+            // Packet Version Entries
+            uint32_t packetFamily = 0;
+            uint32_t packetId = 0;
+
+            offset += sizeUint32;
+            for (uint32_t i = 0; i < packetVersionEntries; ++i) {
+                WriteUint32(writeBuffer, offset, ((packetFamily & 0x3F) << 26) | ((packetId++ & 0x3FF) << 16));
+                offset += sizeUint32;
+                WriteUint32(writeBuffer, offset, EncodeVersion(1, 0, 0));
+                offset += sizeUint32;
+            }
+        }
+    }
+    catch(...)
+    {
+        // Cancel the operation.
+        m_Buffer.Commit(0);
+        throw RuntimeException("Error processing packet.");
+    }
+
+    m_Buffer.Commit(totalSize);
 }
 
 void SendCounterPacket::SendCounterDirectoryPacket(const Category& category, const std::vector<Counter>& counters)
diff --git a/src/profiling/SendCounterPacket.hpp b/src/profiling/SendCounterPacket.hpp
index 453c001..0fc3055 100644
--- a/src/profiling/SendCounterPacket.hpp
+++ b/src/profiling/SendCounterPacket.hpp
@@ -33,6 +33,9 @@
 
     void SetReadyToRead() override;
 
+    static const unsigned int PIPE_MAGIC = 0x45495434;
+    static const unsigned int MAX_METADATA_PACKET_LENGTH = 4096;
+
 private:
     IBufferWrapper& m_Buffer;
     bool m_ReadyToRead;
diff --git a/src/profiling/test/SendCounterPacketTests.cpp b/src/profiling/test/SendCounterPacketTests.cpp
index 89c05d1..5309560 100644
--- a/src/profiling/test/SendCounterPacketTests.cpp
+++ b/src/profiling/test/SendCounterPacketTests.cpp
@@ -5,10 +5,12 @@
 
 #include "../SendCounterPacket.hpp"
 #include "../ProfilingUtils.hpp"
+#include "../EncodeVersion.hpp"
 
 #include <armnn/Exceptions.hpp>
 
 #include <boost/test/unit_test.hpp>
+#include <boost/numeric/conversion/cast.hpp>
 
 #include <chrono>
 #include <iostream>
@@ -142,7 +144,7 @@
     uint32_t capturePeriod = 1000;
     std::vector<uint16_t> selectedCounterIds;
     BOOST_CHECK_THROW(sendPacket1.SendPeriodicCounterSelectionPacket(capturePeriod, selectedCounterIds),
-                      armnn::Exception);
+                      armnn::RuntimeException);
 
     // Packet without any counters
     MockBuffer mockBuffer2(512);
@@ -273,4 +275,117 @@
 
 }
 
+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
+    MockBuffer mockBuffer1(10);
+    SendCounterPacket sendPacket1(mockBuffer1);
+    BOOST_CHECK_THROW(sendPacket1.SendStreamMetaDataPacket(), armnn::RuntimeException);
+
+    // Full metadata packet
+
+    std::string processName = GetProcessName().substr(0, 60);
+
+    uint32_t infoSize = numeric_cast<uint32_t>(GetSoftwareInfo().size()) > 0 ?
+                        numeric_cast<uint32_t>(GetSoftwareInfo().size()) + 1 : 0;
+    uint32_t hardwareVersionSize = numeric_cast<uint32_t>(GetHardwareVersion().size()) > 0 ?
+                                   numeric_cast<uint32_t>(GetHardwareVersion().size()) + 1 : 0;
+    uint32_t softwareVersionSize = numeric_cast<uint32_t>(GetSoftwareVersion().size()) > 0 ?
+                                   numeric_cast<uint32_t>(GetSoftwareVersion().size()) + 1 : 0;
+    uint32_t processNameSize = numeric_cast<uint32_t>(processName.size()) > 0 ?
+                               numeric_cast<uint32_t>(processName.size()) + 1 : 0;
+
+    uint32_t packetEntries = 5;
+
+    MockBuffer mockBuffer2(512);
+    SendCounterPacket sendPacket2(mockBuffer2);
+    sendPacket2.SendStreamMetaDataPacket();
+    unsigned int sizeRead = 0;
+    const unsigned char* readBuffer2 = mockBuffer2.GetReadBuffer(sizeRead);
+
+    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) == SendCounterPacket::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) == SendCounterPacket::MAX_METADATA_PACKET_LENGTH); // max_data_len
+    offset += sizeUint32;
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == numeric_cast<uint32_t>(getpid())); // pid
+    offset += sizeUint32;
+    uint32_t poolOffset = 10 * sizeUint32;
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == (infoSize ? poolOffset : 0)); // offset_info
+    offset += sizeUint32;
+    poolOffset += infoSize;
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == (hardwareVersionSize ? poolOffset : 0)); // offset_hw_version
+    offset += sizeUint32;
+    poolOffset += hardwareVersionSize;
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == (softwareVersionSize ? poolOffset : 0)); // offset_sw_version
+    offset += sizeUint32;
+    poolOffset += softwareVersionSize;
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == (processNameSize ? poolOffset : 0)); // offset_process_name
+    offset += sizeUint32;
+    poolOffset += processNameSize;
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == (packetEntries ? poolOffset : 0)); // offset_packet_version_table
+    offset += sizeUint32;
+    BOOST_TEST(ReadUint32(readBuffer2, offset) == 0); // reserved
+
+    offset += sizeUint32;
+    if (infoSize)
+    {
+        BOOST_TEST(strcmp(reinterpret_cast<const char *>(&readBuffer2[offset]), GetSoftwareInfo().c_str()) == 0);
+        offset += infoSize;
+    }
+
+    if (hardwareVersionSize)
+    {
+        BOOST_TEST(strcmp(reinterpret_cast<const char *>(&readBuffer2[offset]), GetHardwareVersion().c_str()) == 0);
+        offset += hardwareVersionSize;
+    }
+
+    if (softwareVersionSize)
+    {
+        BOOST_TEST(strcmp(reinterpret_cast<const char *>(&readBuffer2[offset]), GetSoftwareVersion().c_str()) == 0);
+        offset += softwareVersionSize;
+    }
+
+    if (processNameSize)
+    {
+        BOOST_TEST(strcmp(reinterpret_cast<const char *>(&readBuffer2[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; ++i)
+        {
+            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;
+        }
+    }
+
+    BOOST_TEST(offset == totalLength);
+}
+
+
 BOOST_AUTO_TEST_SUITE_END()