IVGCVSW-4542 Refactor TimelineDecoder parsing of TimelinePackets

 * Added test to add multiple SwTraceMessages to the buffer
 * Updated TimelineCaptureCommandHandler to iterate until there
   are no more read functions in the packet
 * Further commenting

Change-Id: I41d5acf4f7288ce5a51ffd10a5eea335ac3026ec
Signed-off-by: Keith Davis <keith.davis@arm.com>
diff --git a/src/profiling/ProfilingUtils.hpp b/src/profiling/ProfilingUtils.hpp
index 2604361..37ab88c 100644
--- a/src/profiling/ProfilingUtils.hpp
+++ b/src/profiling/ProfilingUtils.hpp
@@ -108,7 +108,10 @@
     // Prepare the output buffer
     size_t s_size        = s.size() + 1;    // The size of the string (in chars) plus the null-terminator
     size_t uint32_t_size = sizeof(uint32_t);
-    size_t outBufferSize = 1 + s_size / uint32_t_size + (s_size % uint32_t_size != 0 ? 1 : 0);
+    // Output buffer size = StringLength (32 bit) + amount of complete 32bit words that fit into the string
+    //                      + an additional 32bit word if there are remaining chars to complete the string
+    //                      (The rest of the 32bit word is then filled with the NULL terminator)
+    size_t outBufferSize = 1 + (s_size / uint32_t_size) + (s_size % uint32_t_size != 0 ? 1 : 0);
     outputBuffer.resize(outBufferSize, '\0');
 
     // Write the SWTrace string to the output buffer
diff --git a/src/profiling/SendTimelinePacket.hpp b/src/profiling/SendTimelinePacket.hpp
index b3b4aa1..3e52c97 100644
--- a/src/profiling/SendTimelinePacket.hpp
+++ b/src/profiling/SendTimelinePacket.hpp
@@ -98,7 +98,8 @@
                     throw RuntimeException("Error processing while sending TimelineBinaryPacket",
                                            CHECK_LOCATION());
 
-                default:m_Offset += numberOfBytesWritten;
+                default:
+                    m_Offset += numberOfBytesWritten;
                     m_RemainingBufferSize -= numberOfBytesWritten;
                     return;
             }
diff --git a/src/timelineDecoder/TimelineCaptureCommandHandler.cpp b/src/timelineDecoder/TimelineCaptureCommandHandler.cpp
index 01f55bd..fb6935e 100644
--- a/src/timelineDecoder/TimelineCaptureCommandHandler.cpp
+++ b/src/timelineDecoder/TimelineCaptureCommandHandler.cpp
@@ -26,8 +26,9 @@
 void TimelineCaptureCommandHandler::ParseData(const armnn::profiling::Packet& packet)
 {
     uint32_t offset = 0;
+    m_PacketLength = packet.GetLength();
 
-    if (packet.GetLength() < 8)
+    if ( m_PacketLength < 8 )
     {
         return;
     }
@@ -36,13 +37,16 @@
 
     uint32_t declId = 0;
 
-    declId = profiling::ReadUint32(data, offset);
-    offset += uint32_t_size;
+    while ( offset < m_PacketLength )
+    {
+        declId = profiling::ReadUint32(data, offset);
+        offset += uint32_t_size;
 
-    (this->*m_ReadFunctions[declId])(data, offset);
+        (this->*m_ReadFunctions[declId])(data, offset);
+    }
 }
 
-void TimelineCaptureCommandHandler::ReadLabel(const unsigned char* data, uint32_t offset)
+void TimelineCaptureCommandHandler::ReadLabel(const unsigned char* data, uint32_t& offset)
 {
     ITimelineDecoder::Label label;
     label.m_Guid = profiling::ReadUint64(data, offset);
@@ -51,30 +55,36 @@
     uint32_t nameLength = profiling::ReadUint32(data, offset);
     offset += uint32_t_size;
 
-    for (uint32_t i = 0; i < nameLength-1; ++i)
+    uint32_t i = 0;
+    // nameLength - 1 to account for null operator \0
+    for ( i = 0; i < nameLength - 1; ++i )
     {
         label.m_Name += static_cast<char>(profiling::ReadUint8(data, offset + i));
     }
+    // Shift offset past nameLength
+    uint32_t uint32WordAmount = (nameLength / uint32_t_size) + (nameLength % uint32_t_size != 0 ? 1 : 0);
+    offset += uint32WordAmount * uint32_t_size;
+
     m_TimelineDecoder.CreateLabel(label);
 }
 
-void TimelineCaptureCommandHandler::ReadEntity(const unsigned char* data, uint32_t offset)
+void TimelineCaptureCommandHandler::ReadEntity(const unsigned char* data, uint32_t& offset)
 {
     ITimelineDecoder::Entity entity;
     entity.m_Guid = profiling::ReadUint64(data, offset);
-
+    offset += uint64_t_size;
     m_TimelineDecoder.CreateEntity(entity);
 }
 
-void TimelineCaptureCommandHandler::ReadEventClass(const unsigned char* data, uint32_t offset)
+void TimelineCaptureCommandHandler::ReadEventClass(const unsigned char* data, uint32_t& offset)
 {
     ITimelineDecoder::EventClass eventClass;
     eventClass.m_Guid = profiling::ReadUint64(data, offset);
-
+    offset += uint64_t_size;
     m_TimelineDecoder.CreateEventClass(eventClass);
 }
 
-void TimelineCaptureCommandHandler::ReadRelationship(const unsigned char* data, uint32_t offset)
+void TimelineCaptureCommandHandler::ReadRelationship(const unsigned char* data, uint32_t& offset)
 {
     ITimelineDecoder::Relationship relationship;
     relationship.m_RelationshipType =
@@ -84,25 +94,25 @@
     relationship.m_Guid = profiling::ReadUint64(data, offset);
     offset += uint64_t_size;
 
-    relationship.m_HeadGuid  = profiling::ReadUint64(data, offset);
+    relationship.m_HeadGuid = profiling::ReadUint64(data, offset);
     offset += uint64_t_size;
 
     relationship.m_TailGuid = profiling::ReadUint64(data, offset);
-
+    offset += uint64_t_size;
     m_TimelineDecoder.CreateRelationship(relationship);
 }
 
-void TimelineCaptureCommandHandler::ReadEvent(const unsigned char* data, uint32_t offset)
+void TimelineCaptureCommandHandler::ReadEvent(const unsigned char* data, uint32_t& offset)
 {
     ITimelineDecoder::Event event;
     event.m_TimeStamp = profiling::ReadUint64(data, offset);
     offset += uint64_t_size;
 
-    if (m_ThreadIdSize == 4)
+    if ( m_ThreadIdSize == 4 )
     {
         event.m_ThreadId = profiling::ReadUint32(data, offset);
     }
-    else if (m_ThreadIdSize == 8)
+    else if ( m_ThreadIdSize == 8 )
     {
         event.m_ThreadId = profiling::ReadUint64(data, offset);
     }
@@ -110,6 +120,7 @@
     offset += m_ThreadIdSize;
 
     event.m_Guid = profiling::ReadUint64(data, offset);
+    offset += uint64_t_size;
 
     m_TimelineDecoder.CreateEvent(event);
 }
diff --git a/src/timelineDecoder/TimelineCaptureCommandHandler.hpp b/src/timelineDecoder/TimelineCaptureCommandHandler.hpp
index d95adc0..b69e615 100644
--- a/src/timelineDecoder/TimelineCaptureCommandHandler.hpp
+++ b/src/timelineDecoder/TimelineCaptureCommandHandler.hpp
@@ -17,13 +17,14 @@
 namespace timelinedecoder
 {
 
-class TimelineCaptureCommandHandler : public profiling::CommandHandlerFunctor
+class TimelineCaptureCommandHandler :
+    public profiling::CommandHandlerFunctor
 {
     // Utils
     uint32_t uint32_t_size = sizeof(uint32_t);
     uint32_t uint64_t_size = sizeof(uint64_t);
 
-    using ReadFunction = void (TimelineCaptureCommandHandler::*)(const unsigned char*, uint32_t);
+    using ReadFunction = void (TimelineCaptureCommandHandler::*)(const unsigned char*, uint32_t&);
 
 public:
     TimelineCaptureCommandHandler(uint32_t familyId,
@@ -31,25 +32,29 @@
                                   uint32_t version,
                                   ITimelineDecoder& timelineDecoder,
                                   uint32_t threadId_size)
-            : CommandHandlerFunctor(familyId, packetId, version)
-            , m_TimelineDecoder(timelineDecoder)
-            , m_ThreadIdSize(threadId_size)
+        : CommandHandlerFunctor(familyId, packetId, version),
+          m_TimelineDecoder(timelineDecoder),
+          m_ThreadIdSize(threadId_size),
+          m_PacketLength(0)
     {}
 
     void operator()(const armnn::profiling::Packet& packet) override;
 
-    void ReadLabel(const unsigned char* data, uint32_t offset);
-    void ReadEntity(const unsigned char* data, uint32_t offset);
-    void ReadEventClass(const unsigned char* data, uint32_t offset);
-    void ReadRelationship(const unsigned char* data, uint32_t offset);
-    void ReadEvent(const unsigned char* data, uint32_t offset);
+    void ReadLabel(const unsigned char* data, uint32_t& offset);
+    void ReadEntity(const unsigned char* data, uint32_t& offset);
+    void ReadEventClass(const unsigned char* data, uint32_t& offset);
+    void ReadRelationship(const unsigned char* data, uint32_t& offset);
+    void ReadEvent(const unsigned char* data, uint32_t& offset);
 
 private:
     void ParseData(const armnn::profiling::Packet& packet);
 
     ITimelineDecoder& m_TimelineDecoder;
-    const uint32_t m_ThreadIdSize;
+
+    const uint32_t            m_ThreadIdSize;
+    unsigned int              m_PacketLength;
     static const ReadFunction m_ReadFunctions[];
+
 };
 
 } //namespace gatordmock
diff --git a/src/timelineDecoder/tests/TimelineTests.cpp b/src/timelineDecoder/tests/TimelineTests.cpp
index 02d93c3..62b4330 100644
--- a/src/timelineDecoder/tests/TimelineTests.cpp
+++ b/src/timelineDecoder/tests/TimelineTests.cpp
@@ -248,4 +248,109 @@
     }
 }
 
+BOOST_AUTO_TEST_CASE(TimelineCaptureTestMultipleStringsInBuffer)
+{
+    unsigned int                           threadIdSize = sizeof(std::thread::id);
+    profiling::BufferManager               bufferManager(50);
+    profiling::TimelinePacketWriterFactory timelinePacketWriterFactory(bufferManager);
+
+    std::unique_ptr<profiling::ISendTimelinePacket> sendTimelinePacket =
+                                                        timelinePacketWriterFactory.GetSendTimelinePacket();
+
+    profiling::PacketVersionResolver packetVersionResolver;
+
+    TimelineDecoder timelineDecoder;
+    const TimelineDecoder::Model& model = timelineDecoder.GetModel();
+
+    TimelineCaptureCommandHandler timelineCaptureCommandHandler(
+        1, 1, packetVersionResolver.ResolvePacketVersion(1, 1).GetEncodedValue(), timelineDecoder, threadIdSize);
+
+    using Status = ITimelineDecoder::TimelineStatus;
+    BOOST_CHECK(timelineDecoder.SetEntityCallback(PushEntity) == Status::TimelineStatus_Success);
+    BOOST_CHECK(timelineDecoder.SetEventClassCallback(PushEventClass) == Status::TimelineStatus_Success);
+    BOOST_CHECK(timelineDecoder.SetEventCallback(PushEvent) == Status::TimelineStatus_Success);
+    BOOST_CHECK(timelineDecoder.SetLabelCallback(PushLabel) == Status::TimelineStatus_Success);
+    BOOST_CHECK(timelineDecoder.SetRelationshipCallback(PushRelationship) == Status::TimelineStatus_Success);
+
+    const uint64_t entityGuid     = 111111u;
+    const uint64_t eventClassGuid = 22222u;
+    const uint64_t timestamp      = 33333u;
+    const uint64_t eventGuid      = 44444u;
+
+    const std::thread::id threadId = std::this_thread::get_id();
+
+    // need to do a bit of work here to extract the value from threadId
+    unsigned char* uCharThreadId = new unsigned char[threadIdSize]();;
+    uint64_t uint64ThreadId;
+
+    profiling::WriteBytes(uCharThreadId, 0, &threadId, threadIdSize);
+
+    if ( threadIdSize == 4 )
+    {
+        uint64ThreadId = profiling::ReadUint32(uCharThreadId, 0);
+    } else if ( threadIdSize == 8 )
+    {
+        uint64ThreadId = profiling::ReadUint64(uCharThreadId, 0);
+    }
+    delete[] uCharThreadId;
+
+    const uint64_t labelGuid  = 66666u;
+    std::string    labelName  = "test_label";
+    std::string    labelName2 = "test_label2";
+    std::string    labelName3 = "test_label32";
+
+    const uint64_t relationshipGuid = 77777u;
+    const uint64_t headGuid         = 888888u;
+    const uint64_t tailGuid         = 999999u;
+
+    // Check with multiple messages in the same buffer
+    for ( int i = 0; i < 9; ++i )
+    {
+        // Send entity
+        sendTimelinePacket->SendTimelineEntityBinaryPacket(entityGuid);
+        // Send event class
+        sendTimelinePacket->SendTimelineEventClassBinaryPacket(eventClassGuid);
+        // Send event
+        sendTimelinePacket->SendTimelineEventBinaryPacket(timestamp, threadId, eventGuid);
+        // Send label
+        sendTimelinePacket->SendTimelineLabelBinaryPacket(labelGuid, labelName);
+        sendTimelinePacket->SendTimelineLabelBinaryPacket(labelGuid, labelName2);
+        sendTimelinePacket->SendTimelineLabelBinaryPacket(labelGuid, labelName3);
+        // Send relationship
+        profiling::ProfilingRelationshipType relationshipType = profiling::ProfilingRelationshipType::DataLink;
+        sendTimelinePacket->SendTimelineRelationshipBinaryPacket(relationshipType,
+                                                                 relationshipGuid,
+                                                                 headGuid,
+                                                                 tailGuid);
+    }
+
+    sendTimelinePacket->Commit();
+    SendTimelinePacketToCommandHandler(bufferManager.GetReadableBuffer()->GetReadableData(),
+                                       timelineCaptureCommandHandler);
+
+    for ( unsigned long i = 0; i < 9; ++i )
+    {
+        BOOST_CHECK(model.m_Entities[i].m_Guid == entityGuid);
+
+        BOOST_CHECK(model.m_EventClasses[i].m_Guid == eventClassGuid);
+
+        BOOST_CHECK(model.m_Labels[i].m_Guid == labelGuid);
+
+        BOOST_CHECK(model.m_Events[i].m_TimeStamp == timestamp);
+        BOOST_CHECK(model.m_Events[i].m_ThreadId == uint64ThreadId);
+        BOOST_CHECK(model.m_Events[i].m_Guid == eventGuid);
+
+        BOOST_CHECK(model.m_Relationships[i].m_RelationshipType == ITimelineDecoder::RelationshipType::DataLink);
+        BOOST_CHECK(model.m_Relationships[i].m_Guid == relationshipGuid);
+        BOOST_CHECK(model.m_Relationships[i].m_HeadGuid == headGuid);
+        BOOST_CHECK(model.m_Relationships[i].m_TailGuid == tailGuid);
+    }
+    for ( unsigned long i = 0; i < 9; i += 3 )
+    {
+        BOOST_CHECK(model.m_Labels[i].m_Name == labelName);
+        BOOST_CHECK(model.m_Labels[i+1].m_Name == labelName2);
+        BOOST_CHECK(model.m_Labels[i+2].m_Name == labelName3);
+    }
+}
+
 BOOST_AUTO_TEST_SUITE_END()