IVGCVSW-3926 Create the Timeline Message Directory Package


Signed-off-by: Sadik Armagan <sadik.armagan@arm.com>
Change-Id: I8be40f2e6f1f916b2566ca4d9e87b38d27eb7730
diff --git a/src/profiling/ProfilingUtils.cpp b/src/profiling/ProfilingUtils.cpp
index 0f6432e..e458a3f 100644
--- a/src/profiling/ProfilingUtils.cpp
+++ b/src/profiling/ProfilingUtils.cpp
@@ -405,6 +405,129 @@
     return TimelinePacketStatus::Ok;
 }
 
+TimelinePacketStatus WriteTimelineMessageDirectoryPackage(unsigned char* buffer,
+                                                          unsigned int bufferSize,
+                                                          unsigned int& numberOfBytesWritten)
+{
+    // Initialize the output value
+    numberOfBytesWritten = 0;
+
+    // Check that the given buffer is valid
+    if (buffer == nullptr || bufferSize == 0)
+    {
+        return TimelinePacketStatus::BufferExhaustion;
+    }
+
+    // Utils
+    unsigned int uint32_t_size = sizeof(uint32_t);
+
+    // Packet header word 0:
+    // 26:31 [6] packet_family: timeline Packet Family, value 0b000001
+    // 19:25 [7] packet_class: packet class
+    // 16:18 [3] packet_type: packet type
+    // 8:15  [8] reserved: all zeros
+    // 0:7   [8] stream_id: stream identifier
+    uint32_t packetFamily = 1;
+    uint32_t packetClass  = 0;
+    uint32_t packetType   = 0;
+    uint32_t streamId     = 0;
+    uint32_t packetHeaderWord0 = ((packetFamily & 0x0000003F) << 26) |
+                                 ((packetClass  & 0x0000007F) << 19) |
+                                 ((packetType   & 0x00000007) << 16) |
+                                 ((streamId     & 0x00000007) <<  0);
+
+    // the payload/data of the packet consists of swtrace event definitions encoded according
+    // to the swtrace directory specification. The messages being the five defined below:
+    // |  decl_id  |  decl_name          |    ui_name            |  arg_types  |  arg_names                          |
+    // |-----------|---------------------|-----------------------|-------------|-------------------------------------|
+    // |    0      |   declareLabel      |   declare label       |    ps       |  guid,value                         |
+    // |    1      |   declareEntity     |   declare entity      |    p        |  guid                               |
+    // |    2      | declareEventClass   |  declare event class  |    p        |  guid                               |
+    // |    3      | declareRelationship | declare relationship  |    Ippp     |  relationshipType,relationshipGuid,
+    //                                                                            headGuid,tailGuid                  |
+    // |    4      |   declareEvent      |   declare event       |    @tp      |  timestamp,threadId,eventGuid       |
+
+    std::vector<std::vector<std::string>> timelineDirectoryMessages =
+        { {"declareLabel", "declare label", "ps", "guid,value"},
+          {"declareEntity", "declare entity", "p", "guid"},
+          {"declareEventClass", "declare event class", "p", "guid"},
+          {"declareRelationship", "declare relationship",
+              "Ippp", "relationshipType,relationshipGuid,headGuid,tailGuid"},
+          {"declareEvent", "declare event", "@tp", "timestamp,threadId,eventGuid"} };
+
+    unsigned int messagesDataLength = 0u;
+    std::vector<std::vector<std::vector<uint32_t>>> swTraceTimelineDirectoryMessages;
+
+    for (const auto& timelineDirectoryMessage : timelineDirectoryMessages)
+    {
+        messagesDataLength += uint32_t_size; // decl_id
+
+        std::vector<std::vector<uint32_t>> swTraceStringsVector;
+        for (const auto& label : timelineDirectoryMessage)
+        {
+            std::vector<uint32_t> swTraceString;
+            bool result = StringToSwTraceString<SwTraceCharPolicy>(label, swTraceString);
+            if (!result)
+            {
+                return TimelinePacketStatus::Error;
+            }
+
+            messagesDataLength += boost::numeric_cast<unsigned int>(swTraceString.size()) * uint32_t_size;
+            swTraceStringsVector.push_back(swTraceString);
+        }
+        swTraceTimelineDirectoryMessages.push_back(swTraceStringsVector);
+    }
+
+    // Calculate the timeline directory binary packet size (in bytes)
+    unsigned int timelineDirectoryPacketSize = 2 * uint32_t_size + // Header (2 words)
+                                               messagesDataLength; // 5 messages length
+
+    // Check whether the timeline directory binary packet fits in the given buffer
+    if (timelineDirectoryPacketSize > bufferSize)
+    {
+        return TimelinePacketStatus::BufferExhaustion;
+    }
+
+    // Packet header word 1:
+    // 25:31 [7]  reserved: all zeros
+    // 24    [1]  sequence_numbered: when non-zero the 4 bytes following the header is a u32 sequence number
+    // 0:23  [24] data_length: unsigned 24-bit integer. Length of data, in bytes. Zero is permitted
+    uint32_t sequenceNumbered  = 0;
+    uint32_t dataLength        = boost::numeric_cast<uint32_t>(messagesDataLength);
+    uint32_t packetHeaderWord1 = ((sequenceNumbered & 0x00000001) << 24) |
+                                 ((dataLength       & 0x00FFFFFF) <<  0);
+
+    // Initialize the offset for writing in the buffer
+    unsigned int offset = 0;
+
+    // Write the timeline binary packet header to the buffer
+    WriteUint32(buffer, offset, packetHeaderWord0);
+    offset += uint32_t_size;
+    WriteUint32(buffer, offset, packetHeaderWord1);
+    offset += uint32_t_size;
+
+    for (unsigned int i = 0u; i < swTraceTimelineDirectoryMessages.size(); ++i)
+    {
+        // Write the timeline binary packet payload to the buffer
+        WriteUint32(buffer, offset, i); // decl_id
+        offset += uint32_t_size;
+
+        for (std::vector<uint32_t> swTraceString : swTraceTimelineDirectoryMessages[i])
+        {
+            for (uint32_t swTraceDeclStringWord : swTraceString)
+            {
+                WriteUint32(buffer, offset, swTraceDeclStringWord);
+                offset += uint32_t_size;
+            }
+        }
+    }
+
+    // Update the number of bytes written
+    numberOfBytesWritten = timelineDirectoryPacketSize;
+
+    return TimelinePacketStatus::Ok;
+}
+
 } // namespace profiling
 
 } // namespace armnn
diff --git a/src/profiling/ProfilingUtils.hpp b/src/profiling/ProfilingUtils.hpp
index b6a4de9..47a6d3f 100644
--- a/src/profiling/ProfilingUtils.hpp
+++ b/src/profiling/ProfilingUtils.hpp
@@ -137,6 +137,10 @@
                                                     unsigned int bufferSize,
                                                     unsigned int& numberOfBytesWritten);
 
+TimelinePacketStatus WriteTimelineMessageDirectoryPackage(unsigned char* buffer,
+                                                          unsigned int bufferSize,
+                                                          unsigned int& numberOfBytesWritten);
+
 class BufferExhaustion : public armnn::Exception
 {
     using Exception::Exception;
diff --git a/src/profiling/test/TimelinePacketTests.cpp b/src/profiling/test/TimelinePacketTests.cpp
index 24d665d..b039c37 100644
--- a/src/profiling/test/TimelinePacketTests.cpp
+++ b/src/profiling/test/TimelinePacketTests.cpp
@@ -130,6 +130,134 @@
     BOOST_CHECK(buffer[offset] == '\0'); // The null-terminator at the end of the SWTrace label
 }
 
+BOOST_AUTO_TEST_CASE(TimelineMessageDirectoryPacketTest1)
+{
+    unsigned int numberOfBytesWritten = 789u;
+    TimelinePacketStatus result = WriteTimelineMessageDirectoryPackage(nullptr,
+                                                                       512u,
+                                                                       numberOfBytesWritten);
+    BOOST_CHECK(result == TimelinePacketStatus::BufferExhaustion);
+    BOOST_CHECK(numberOfBytesWritten == 0);
+}
+
+BOOST_AUTO_TEST_CASE(TimelineMessageDirectoryPacketTest2)
+{
+    std::vector<unsigned char> buffer(512, 0);
+
+    unsigned int numberOfBytesWritten = 789u;
+    TimelinePacketStatus result = WriteTimelineMessageDirectoryPackage(buffer.data(),
+                                                                       0,
+                                                                       numberOfBytesWritten);
+    BOOST_CHECK(result == TimelinePacketStatus::BufferExhaustion);
+    BOOST_CHECK(numberOfBytesWritten == 0);
+}
+
+BOOST_AUTO_TEST_CASE(TimelineMessageDirectoryPacketTest3)
+{
+    std::vector<unsigned char> buffer(512, 0);
+    unsigned int numberOfBytesWritten = 789u;
+    TimelinePacketStatus result = WriteTimelineMessageDirectoryPackage(buffer.data(),
+                                                                       boost::numeric_cast<unsigned int>(buffer.size()),
+                                                                       numberOfBytesWritten);
+    BOOST_CHECK(result == TimelinePacketStatus::Ok);
+
+    BOOST_CHECK(numberOfBytesWritten == 424);
+
+    unsigned int uint32_t_size = sizeof(uint32_t);
+
+    // Check the packet header
+    unsigned int offset = 0;
+    uint32_t packetHeaderWord0 = ReadUint32(buffer.data(), offset);
+    uint32_t packetFamily = (packetHeaderWord0 >> 26) & 0x0000003F;
+    uint32_t packetClass  = (packetHeaderWord0 >> 19) & 0x0000007F;
+    uint32_t packetType   = (packetHeaderWord0 >> 16) & 0x00000007;
+    uint32_t streamId     = (packetHeaderWord0 >>  0) & 0x00000007;
+    BOOST_CHECK(packetFamily == 1);
+    BOOST_CHECK(packetClass  == 0);
+    BOOST_CHECK(packetType   == 0);
+    BOOST_CHECK(streamId     == 0);
+
+    offset += uint32_t_size;
+    uint32_t packetHeaderWord1 = ReadUint32(buffer.data(), offset);
+    uint32_t sequenceNumbered = (packetHeaderWord1 >> 24) & 0x00000001;
+    uint32_t dataLength       = (packetHeaderWord1 >>  0) & 0x00FFFFFF;
+    BOOST_CHECK(sequenceNumbered ==  0);
+    BOOST_CHECK(dataLength       == 416);
+
+    // Check the decl_id
+    offset += uint32_t_size;
+    uint32_t readDeclId = ReadUint32(buffer.data(), offset);
+    BOOST_CHECK(readDeclId == 0);
+
+    // SWTrace "namestring" format
+    // length of the string (first 4 bytes) + string + null terminator
+
+    // Check the decl_name
+    offset += uint32_t_size;
+    uint32_t swTraceDeclNameLength = ReadUint32(buffer.data(), offset);
+    BOOST_CHECK(swTraceDeclNameLength == 13); // decl_name length including the null-terminator
+
+    std::string label = "declareLabel";
+    offset += uint32_t_size;
+    BOOST_CHECK(std::memcmp(buffer.data() + offset,           // Offset to the label in the buffer
+                            label.data(),                      // The original label
+                            swTraceDeclNameLength - 1) == 0); // The length of the label
+
+    // Check the ui_name
+    std::vector<uint32_t> swTraceString;
+    StringToSwTraceString<SwTraceCharPolicy>(label, swTraceString);
+    offset += (boost::numeric_cast<unsigned int>(swTraceString.size()) - 1) * uint32_t_size;
+    uint32_t swTraceUINameLength = ReadUint32(buffer.data(), offset);
+    BOOST_CHECK(swTraceUINameLength == 14); // ui_name length including the null-terminator
+
+    label = "declare label";
+    offset += uint32_t_size;
+    BOOST_CHECK(std::memcmp(buffer.data() + offset,           // Offset to the label in the buffer
+                            label.data(),                     // The original label
+                            swTraceUINameLength - 1) == 0);   // The length of the label
+
+    // Check arg_types
+    StringToSwTraceString<SwTraceCharPolicy>(label, swTraceString);
+    offset += (boost::numeric_cast<unsigned int>(swTraceString.size()) - 1) * uint32_t_size;
+    uint32_t swTraceArgTypesLength = ReadUint32(buffer.data(), offset);
+    BOOST_CHECK(swTraceArgTypesLength == 3); // arg_types length including the null-terminator
+
+    label = "ps";
+    offset += uint32_t_size;
+    BOOST_CHECK(std::memcmp(buffer.data() + offset,           // Offset to the label in the buffer
+                            label.data(),                     // The original label
+                            swTraceArgTypesLength - 1) == 0); // The length of the label
+
+    // Check arg_names
+    StringToSwTraceString<SwTraceCharPolicy>(label, swTraceString);
+    offset += (boost::numeric_cast<unsigned int>(swTraceString.size()) - 1) * uint32_t_size;
+    uint32_t swTraceArgNamesLength = ReadUint32(buffer.data(), offset);
+    BOOST_CHECK(swTraceArgNamesLength == 11); // arg_names length including the null-terminator
+
+    label = "guid,value";
+    offset += uint32_t_size;
+    BOOST_CHECK(std::memcmp(buffer.data() + offset,        // Offset to the label in the buffer
+                            label.data(),                     // The original label
+                            swTraceArgNamesLength - 1) == 0); // The length of the label
+
+    // Check second message decl_id
+    StringToSwTraceString<SwTraceCharPolicy>(label, swTraceString);
+    offset += (boost::numeric_cast<unsigned int>(swTraceString.size()) - 1) * uint32_t_size;
+    readDeclId = ReadUint32(buffer.data(), offset);
+    BOOST_CHECK(readDeclId == 1);
+
+    // Check second decl_name
+    offset += uint32_t_size;
+    swTraceDeclNameLength = ReadUint32(buffer.data(), offset);
+    BOOST_CHECK(swTraceDeclNameLength == 14); // decl_name length including the null-terminator
+
+    label = "declareEntity";
+    offset += uint32_t_size;
+    BOOST_CHECK(std::memcmp(buffer.data() + offset,           // Offset to the label in the buffer
+                            label.data(),                     // The original label
+                            swTraceDeclNameLength - 1) == 0); // The length of the label
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 BOOST_AUTO_TEST_SUITE(TimelineEntityTests)