aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Flynn <jim.flynn@arm.com>2020-03-29 17:48:26 +0100
committerfinn.williams <finn.williams@arm.com>2020-04-27 16:38:55 +0000
commit4e755a50e35a1f5ac1b011dc4baf89e6d97f116e (patch)
tree9660dba96819145e4f1bfe34e4482dca211ffc26
parent303980c502c721f13d65e7087be6c0758df65044 (diff)
downloadarmnn-4e755a50e35a1f5ac1b011dc4baf89e6d97f116e.tar.gz
IVGCVSW-4595 Add IFileOnlyPacketHandlers to file only profiling connection
Change-Id: Ib49a8cbbf323da4109cdab9750e6c4d276e484b7 Signed-off-by: Jim Flynn <jim.flynn@arm.com>
-rw-r--r--Android.mk14
-rw-r--r--CMakeLists.txt12
-rw-r--r--include/armnn/IRuntime.hpp7
-rw-r--r--include/armnn/profiling/ILocalPacketHandler.hpp48
-rw-r--r--include/armnn/profiling/ITimelineDecoder.hpp12
-rw-r--r--profiling/common/include/ProfilingException.hpp29
-rw-r--r--src/profiling/FileOnlyProfilingConnection.cpp162
-rw-r--r--src/profiling/FileOnlyProfilingConnection.hpp48
-rw-r--r--src/profiling/IProfilingConnection.hpp1
-rw-r--r--src/profiling/NullProfilingConnection.hpp41
-rw-r--r--src/profiling/ProfilingConnectionFactory.cpp7
-rw-r--r--src/profiling/ProfilingService.hpp2
-rw-r--r--src/profiling/ProfilingUtils.cpp29
-rw-r--r--src/profiling/ProfilingUtils.hpp3
-rw-r--r--src/profiling/test/FileOnlyProfilingDecoratorTests.cpp68
-rw-r--r--src/profiling/test/PrintPacketHeaderHandler.cpp31
-rw-r--r--src/profiling/test/PrintPacketHeaderHandler.hpp26
-rw-r--r--src/profiling/test/TestTimelinePacketHandler.cpp132
-rw-r--r--src/profiling/test/TestTimelinePacketHandler.hpp90
-rw-r--r--src/profiling/test/TimelineModel.cpp57
-rw-r--r--src/profiling/test/TimelineModel.hpp67
21 files changed, 867 insertions, 19 deletions
diff --git a/Android.mk b/Android.mk
index 2cd6e50c3b..9ae4a993b9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -10,6 +10,7 @@ OPENCL_HEADER_PATH := $(LOCAL_PATH)/../clframework/include
NN_HEADER_PATH := $(LOCAL_PATH)/../../../../frameworks/ml/nn/runtime/include
ARMNN_HEADER_PATH := $(LOCAL_PATH)/include
ARMNN_PROFILING_INCLUDE_PATH := $(LOCAL_PATH)/profiling
+ARMNN_TIMELINE_DECODER_INCLUDE_PATH := $(LOCAL_PATH)/src/timelineDecoder
ARMNN_MAIN_HEADER_PATH := $(LOCAL_PATH)/src
ARMNN_SOURCE_HEADER_PATH := $(LOCAL_PATH)/src/armnn
ARMNN_SOURCE_UTILS_HEADER_PATH := $(LOCAL_PATH)/src/armnnUtils
@@ -66,6 +67,7 @@ LOCAL_EXPORT_C_INCLUDES := \
$(ARMNN_MAIN_HEADER_PATH) \
$(ARMNN_SOURCE_HEADER_PATH) \
$(ARMNN_PROFILING_INCLUDE_PATH) \
+ $(ARMNN_TIMELINE_DECODER_INCLUDE_PATH) \
$(ARMNN_SOURCE_UTILS_HEADER_PATH) \
$(ARMNN_PROFILING_HEADER_PATH) \
$(ARMNN_BACKENDS_HEADER_PATH)
@@ -75,6 +77,7 @@ LOCAL_C_INCLUDES := \
$(NN_HEADER_PATH) \
$(ARMNN_HEADER_PATH) \
$(ARMNN_PROFILING_INCLUDE_PATH) \
+ $(ARMNN_TIMELINE_DECODER_INCLUDE_PATH) \
$(ARMNN_MAIN_HEADER_PATH) \
$(ARMNN_SOURCE_HEADER_PATH) \
$(ARMNN_SOURCE_UTILS_HEADER_PATH) \
@@ -193,8 +196,8 @@ LOCAL_SRC_FILES := \
src/profiling/ConnectionAcknowledgedCommandHandler.cpp \
src/profiling/CounterDirectory.cpp \
src/profiling/CounterIdMap.cpp \
- src/profiling/DirectoryCaptureCommandHandler.cpp \
src/profiling/DeactivateTimelineReportingCommandHandler.cpp \
+ src/profiling/DirectoryCaptureCommandHandler.cpp \
src/profiling/FileOnlyProfilingConnection.cpp \
src/profiling/Holder.cpp \
src/profiling/LabelsAndEventClasses.cpp \
@@ -216,7 +219,10 @@ LOCAL_SRC_FILES := \
src/profiling/SocketProfilingConnection.cpp \
src/profiling/TimelinePacketWriterFactory.cpp \
src/profiling/TimelineUtilityMethods.cpp \
- src/profiling/backends/BackendProfiling.cpp
+ src/profiling/backends/BackendProfiling.cpp \
+ src/timelineDecoder/TimelineCaptureCommandHandler.cpp \
+ src/timelineDecoder/TimelineDecoder.cpp \
+ src/timelineDecoder/TimelineDirectoryCaptureCommandHandler.cpp
LOCAL_STATIC_LIBRARIES := \
arm_compute_library \
@@ -299,6 +305,7 @@ LOCAL_C_INCLUDES := \
$(NN_HEADER_PATH) \
$(ARMNN_HEADER_PATH) \
$(ARMNN_PROFILING_INCLUDE_PATH) \
+ $(ARMNN_TIMELINE_DECODER_INCLUDE_PATH) \
$(ARMNN_MAIN_HEADER_PATH) \
$(ARMNN_SOURCE_HEADER_PATH) \
$(ARMNN_SOURCE_UTILS_HEADER_PATH) \
@@ -384,12 +391,15 @@ LOCAL_SRC_FILES := \
src/armnnUtils/test/TensorUtilsTest.cpp \
src/profiling/test/BufferTests.cpp \
src/profiling/test/FileOnlyProfilingDecoratorTests.cpp \
+ src/profiling/test/PrintPacketHeaderHandler.cpp \
src/profiling/test/ProfilingConnectionDumpToFileDecoratorTests.cpp \
src/profiling/test/ProfilingGuidTest.cpp \
src/profiling/test/ProfilingTests.cpp \
src/profiling/test/ProfilingTestUtils.cpp \
src/profiling/test/SendCounterPacketTests.cpp \
src/profiling/test/SendTimelinePacketTests.cpp \
+ src/profiling/test/TestTimelinePacketHandler.cpp \
+ src/profiling/test/TimelineModel.cpp \
src/profiling/test/TimelinePacketTests.cpp \
src/profiling/test/TimelineUtilityMethodsTests.cpp
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8f3aa0368a..54376b6109 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -252,6 +252,7 @@ list(APPEND armnn_sources
include/armnn/utility/NumericCast.hpp
include/armnn/utility/PolymorphicDowncast.hpp
include/armnn/utility/StringUtils.hpp
+ profiling/common/include/ProfilingException.hpp
profiling/common/include/SocketConnectionException.hpp
src/armnn/layers/LayerCloneBase.hpp
src/armnn/layers/LayerWithParameters.hpp
@@ -505,6 +506,7 @@ list(APPEND armnn_sources
src/profiling/IProfilingConnectionFactory.hpp
src/profiling/LabelsAndEventClasses.cpp
src/profiling/LabelsAndEventClasses.hpp
+ src/profiling/NullProfilingConnection.hpp
src/profiling/Packet.hpp
src/profiling/PacketBuffer.cpp
src/profiling/PacketBuffer.hpp
@@ -575,7 +577,7 @@ target_include_directories(armnn PRIVATE src/profiling)
target_link_libraries(armnn armnnUtils)
target_link_libraries(armnn ${CMAKE_DL_LIBS})
-if ("${CMAKE_SYSTEM_NAME}" STREQUAL Windows)
+if ("${CMAKE_SYSTEM_NAME}" STREQUAL Windows)
target_link_libraries(armnn Ws2_32.lib)
endif()
@@ -676,6 +678,8 @@ if(BUILD_UNIT_TESTS)
src/armnnUtils/test/TensorUtilsTest.cpp
src/profiling/test/BufferTests.cpp
src/profiling/test/FileOnlyProfilingDecoratorTests.cpp
+ src/profiling/test/PrintPacketHeaderHandler.cpp
+ src/profiling/test/PrintPacketHeaderHandler.hpp
src/profiling/test/ProfilingConnectionDumpToFileDecoratorTests.cpp
src/profiling/test/ProfilingGuidTest.cpp
src/profiling/test/ProfilingMocks.hpp
@@ -685,6 +689,10 @@ if(BUILD_UNIT_TESTS)
src/profiling/test/ProfilingTestUtils.hpp
src/profiling/test/SendCounterPacketTests.cpp
src/profiling/test/SendCounterPacketTests.hpp
+ src/profiling/test/TestTimelinePacketHandler.cpp
+ src/profiling/test/TestTimelinePacketHandler.hpp
+ src/profiling/test/TimelineModel.cpp
+ src/profiling/test/TimelineModel.hpp
src/profiling/test/SendTimelinePacketTests.cpp
src/profiling/test/TimelinePacketTests.cpp
src/profiling/test/TimelineUtilityMethodsTests.cpp
@@ -998,7 +1006,7 @@ if (BUILD_ARMNN_SERIALIZER AND (BUILD_TF_PARSER OR BUILD_TF_LITE_PARSER OR BUILD
${Boost_FILESYSTEM_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY})
addDllCopyCommands(ArmnnConverter)
-endif()
+ endif()
if(BUILD_TIMELINE_DECODER)
add_subdirectory(src/timelineDecoder)
diff --git a/include/armnn/IRuntime.hpp b/include/armnn/IRuntime.hpp
index 06d249ea8c..48ad7c494d 100644
--- a/include/armnn/IRuntime.hpp
+++ b/include/armnn/IRuntime.hpp
@@ -10,6 +10,7 @@
#include "Tensor.hpp"
#include "Types.hpp"
#include "TypesUtils.hpp"
+#include "profiling/ILocalPacketHandler.hpp"
#include <memory>
@@ -61,21 +62,23 @@ public:
{
ExternalProfilingOptions()
: m_EnableProfiling(false)
+ , m_TimelineEnabled(false)
, m_OutgoingCaptureFile("")
, m_IncomingCaptureFile("")
, m_FileOnly(false)
, m_CapturePeriod(LOWEST_CAPTURE_PERIOD)
, m_FileFormat("binary")
- , m_TimelineEnabled(false)
+ , m_LocalPacketHandlers()
{}
bool m_EnableProfiling;
+ bool m_TimelineEnabled;
std::string m_OutgoingCaptureFile;
std::string m_IncomingCaptureFile;
bool m_FileOnly;
uint32_t m_CapturePeriod;
std::string m_FileFormat;
- bool m_TimelineEnabled;
+ std::vector<armnn::profiling::ILocalPacketHandlerSharedPtr> m_LocalPacketHandlers;
};
ExternalProfilingOptions m_ProfilingOptions;
diff --git a/include/armnn/profiling/ILocalPacketHandler.hpp b/include/armnn/profiling/ILocalPacketHandler.hpp
new file mode 100644
index 0000000000..a2b9d5fd56
--- /dev/null
+++ b/include/armnn/profiling/ILocalPacketHandler.hpp
@@ -0,0 +1,48 @@
+//
+// Copyright © 2020 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+
+#include <armnn/utility/IgnoreUnused.hpp>
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+namespace armnn
+{
+
+namespace profiling
+{
+// forward declare to prevent a circular dependency
+class Packet;
+class IProfilingConnection;
+
+class ILocalPacketHandler
+{
+public:
+ virtual ~ILocalPacketHandler() {};
+
+ /// @return lists the headers of the packets that this handler accepts
+ /// only these packets will get sent to this handler.
+ /// If this function returns an empty list then ALL packets
+ /// will be sent to the PacketHandler i.e. a universal handler.
+ virtual std::vector<uint32_t> GetHeadersAccepted() = 0;
+
+ /// process the packet
+ virtual void HandlePacket(const Packet& packet) = 0;
+
+ /// Set a profiling connection on the handler. Only need to implement this
+ /// function if the handler will be writing data back to the profiled application.
+ virtual void SetConnection(IProfilingConnection* profilingConnection) {armnn::IgnoreUnused(profilingConnection);}
+};
+
+using ILocalPacketHandlerPtr = std::unique_ptr<ILocalPacketHandler>;
+using ILocalPacketHandlerSharedPtr = std::shared_ptr<ILocalPacketHandler>;
+
+} // namespace profiling
+
+} // namespace armnn \ No newline at end of file
diff --git a/include/armnn/profiling/ITimelineDecoder.hpp b/include/armnn/profiling/ITimelineDecoder.hpp
index 3a02bb5599..5d22ad8e3d 100644
--- a/include/armnn/profiling/ITimelineDecoder.hpp
+++ b/include/armnn/profiling/ITimelineDecoder.hpp
@@ -27,6 +27,18 @@ public:
LabelLink /// Head uses label Tail (Tail MUST be a guid of a label).
};
+ static char const* GetRelationshipAsCString(RelationshipType rType)
+ {
+ switch (rType)
+ {
+ case RelationshipType::RetentionLink: return "RetentionLink";
+ case RelationshipType::ExecutionLink: return "ExecutionLink";
+ case RelationshipType::DataLink: return "DataLink";
+ case RelationshipType::LabelLink: return "LabelLink";
+ default: return "Unknown";
+ }
+ }
+
struct Entity
{
uint64_t m_Guid;
diff --git a/profiling/common/include/ProfilingException.hpp b/profiling/common/include/ProfilingException.hpp
new file mode 100644
index 0000000000..532c2d49f7
--- /dev/null
+++ b/profiling/common/include/ProfilingException.hpp
@@ -0,0 +1,29 @@
+//
+// Copyright © 2020 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+#pragma once
+
+#include <stdexcept>
+#include <string>
+
+namespace armnnProfiling
+{
+
+/// General Exception class for Profiling code
+class ProfilingException : public std::exception
+{
+public:
+ explicit ProfilingException(const std::string& message) : m_Message(message) {};
+
+ /// @return - Error message of ProfilingException
+ virtual const char* what() const noexcept override
+ {
+ return m_Message.c_str();
+ }
+
+private:
+ std::string m_Message;
+};
+
+} // namespace armnnProfiling
diff --git a/src/profiling/FileOnlyProfilingConnection.cpp b/src/profiling/FileOnlyProfilingConnection.cpp
index f9bdde961f..5947d2c081 100644
--- a/src/profiling/FileOnlyProfilingConnection.cpp
+++ b/src/profiling/FileOnlyProfilingConnection.cpp
@@ -8,6 +8,7 @@
#include <armnn/Exceptions.hpp>
+#include <algorithm>
#include <boost/numeric/conversion/cast.hpp>
#include <iostream>
#include <thread>
@@ -32,10 +33,19 @@ bool FileOnlyProfilingConnection::IsOpen() const
void FileOnlyProfilingConnection::Close()
{
// Dump any unread packets out of the queue.
- for (unsigned int i = 0; i < m_PacketQueue.size(); i++)
+ size_t initialSize = m_PacketQueue.size();
+ for (size_t i = 0; i < initialSize; ++i)
{
m_PacketQueue.pop();
}
+ // dispose of the processing thread
+ m_KeepRunning.store(false);
+ if (m_LocalHandlersThread.joinable())
+ {
+ // make sure the thread wakes up and sees it has to stop
+ m_ConditionPacketReadable.notify_one();
+ m_LocalHandlersThread.join();
+ }
}
bool FileOnlyProfilingConnection::WaitForStreamMeta(const unsigned char* buffer, uint32_t length)
@@ -112,10 +122,11 @@ bool FileOnlyProfilingConnection::SendCounterSelectionPacket()
bool FileOnlyProfilingConnection::WritePacket(const unsigned char* buffer, uint32_t length)
{
ARMNN_ASSERT(buffer);
+ Packet packet = ReceivePacket(buffer, length);
// Read Header and determine case
uint32_t outgoingHeaderAsWords[2];
- PackageActivity packageActivity = GetPackageActivity(buffer, outgoingHeaderAsWords);
+ PackageActivity packageActivity = GetPackageActivity(packet, outgoingHeaderAsWords);
switch (packageActivity)
{
@@ -160,6 +171,7 @@ bool FileOnlyProfilingConnection::WritePacket(const unsigned char* buffer, uint3
break;
}
}
+ ForwardPacketToHandlers(packet);
return true;
}
@@ -181,10 +193,10 @@ Packet FileOnlyProfilingConnection::ReadPacket(uint32_t timeout)
return returnedPacket;
}
-PackageActivity FileOnlyProfilingConnection::GetPackageActivity(const unsigned char* buffer, uint32_t headerAsWords[2])
+PackageActivity FileOnlyProfilingConnection::GetPackageActivity(const Packet& packet, uint32_t headerAsWords[2])
{
- headerAsWords[0] = ToUint32(buffer, m_Endianness);
- headerAsWords[1] = ToUint32(buffer + 4, m_Endianness);
+ headerAsWords[0] = packet.GetHeader();
+ headerAsWords[1] = packet.GetLength();
if (headerAsWords[0] == 0x20000) // Packet family = 0 Packet Id = 2
{
return PackageActivity::CounterDirectory;
@@ -221,6 +233,146 @@ void FileOnlyProfilingConnection::Fail(const std::string& errorMessage)
throw RuntimeException(errorMessage);
}
+/// Adds a local packet handler to the FileOnlyProfilingConnection. Invoking this will start
+/// a processing thread that will ensure that processing of packets will happen on a separate
+/// thread from the profiling services send thread and will therefore protect against the
+/// profiling message buffer becoming exhausted because packet handling slows the dispatch.
+void FileOnlyProfilingConnection::AddLocalPacketHandler(ILocalPacketHandlerSharedPtr localPacketHandler)
+{
+ m_PacketHandlers.push_back(std::move(localPacketHandler));
+ ILocalPacketHandlerSharedPtr localCopy = m_PacketHandlers.back();
+ localCopy->SetConnection(this);
+ if (localCopy->GetHeadersAccepted().empty())
+ {
+ //this is a universal handler
+ m_UniversalHandlers.push_back(localCopy);
+ }
+ else
+ {
+ for (uint32_t header : localCopy->GetHeadersAccepted())
+ {
+ auto iter = m_IndexedHandlers.find(header);
+ if (iter == m_IndexedHandlers.end())
+ {
+ std::vector<ILocalPacketHandlerSharedPtr> handlers;
+ handlers.push_back(localCopy);
+ m_IndexedHandlers.emplace(std::make_pair(header, handlers));
+ }
+ else
+ {
+ iter->second.push_back(localCopy);
+ }
+ }
+ }
+}
+
+void FileOnlyProfilingConnection::StartProcessingThread()
+{
+ // check if the thread has already started
+ if (m_IsRunning.load())
+ {
+ return;
+ }
+ // make sure if there was one running before it is joined
+ if (m_LocalHandlersThread.joinable())
+ {
+ m_LocalHandlersThread.join();
+ }
+ m_IsRunning.store(true);
+ m_KeepRunning.store(true);
+ m_LocalHandlersThread = std::thread(&FileOnlyProfilingConnection::ServiceLocalHandlers, this);
+}
+
+void FileOnlyProfilingConnection::ForwardPacketToHandlers(Packet& packet)
+{
+ if (m_PacketHandlers.empty())
+ {
+ return;
+ }
+ if (m_KeepRunning.load() == false)
+ {
+ return;
+ }
+ {
+ std::unique_lock<std::mutex> readableListLock(m_ReadableMutex);
+ if (m_KeepRunning.load() == false)
+ {
+ return;
+ }
+ m_ReadableList.push(std::move(packet));
+ }
+ m_ConditionPacketReadable.notify_one();
+}
+
+void FileOnlyProfilingConnection::ServiceLocalHandlers()
+{
+ do
+ {
+ Packet returnedPacket;
+ bool readPacket = false;
+ { // only lock while we are taking the packet off the incoming list
+ std::unique_lock<std::mutex> lck(m_ReadableMutex);
+ if (m_Timeout < 0)
+ {
+ m_ConditionPacketReadable.wait(lck,
+ [&] { return !m_ReadableList.empty(); });
+ }
+ else
+ {
+ m_ConditionPacketReadable.wait_for(lck,
+ std::chrono::milliseconds(std::max(m_Timeout, 1000)),
+ [&] { return !m_ReadableList.empty(); });
+ }
+ if (m_KeepRunning.load())
+ {
+ if (!m_ReadableList.empty())
+ {
+ returnedPacket = std::move(m_ReadableList.front());
+ m_ReadableList.pop();
+ readPacket = true;
+ }
+ }
+ else
+ {
+ ClearReadableList();
+ }
+ }
+ if (m_KeepRunning.load() && readPacket)
+ {
+ DispatchPacketToHandlers(returnedPacket);
+ }
+ } while (m_KeepRunning.load());
+ // make sure the readable list is cleared
+ ClearReadableList();
+ m_IsRunning.store(false);
+}
+
+void FileOnlyProfilingConnection::ClearReadableList()
+{
+ // make sure the incoming packet queue gets emptied
+ size_t initialSize = m_ReadableList.size();
+ for (size_t i = 0; i < initialSize; ++i)
+ {
+ m_ReadableList.pop();
+ }
+}
+
+void FileOnlyProfilingConnection::DispatchPacketToHandlers(const Packet& packet)
+{
+ for (auto& delegate : m_UniversalHandlers)
+ {
+ delegate->HandlePacket(packet);
+ }
+ auto iter = m_IndexedHandlers.find(packet.GetHeader());
+ if (iter != m_IndexedHandlers.end())
+ {
+ for (auto &delegate : iter->second)
+ {
+ delegate->HandlePacket(packet);
+ }
+ }
+}
+
} // namespace profiling
} // namespace armnn
diff --git a/src/profiling/FileOnlyProfilingConnection.hpp b/src/profiling/FileOnlyProfilingConnection.hpp
index d4477b6883..12ac27333d 100644
--- a/src/profiling/FileOnlyProfilingConnection.hpp
+++ b/src/profiling/FileOnlyProfilingConnection.hpp
@@ -5,15 +5,19 @@
#pragma once
-#include "CounterDirectory.hpp"
+#include <armnn/profiling/ILocalPacketHandler.hpp>
#include "DirectoryCaptureCommandHandler.hpp"
#include "IProfilingConnection.hpp"
+#include "Packet.hpp"
#include "ProfilingUtils.hpp"
#include "Runtime.hpp"
+#include <atomic>
#include <condition_variable>
#include <fstream>
+#include <mutex>
#include <queue>
+#include <thread>
namespace armnn
{
@@ -42,7 +46,20 @@ public:
: m_Options(options)
, m_QuietOp(quietOp)
, m_Endianness(TargetEndianness::LeWire) // Set a sensible default. WaitForStreamMeta will set a real value.
- {};
+ , m_IsRunning(false)
+ , m_KeepRunning(false)
+ , m_Timeout(1000)
+ {
+ for (ILocalPacketHandlerSharedPtr localPacketHandler : options.m_LocalPacketHandlers)
+ {
+ AddLocalPacketHandler(localPacketHandler);
+ }
+ if (!options.m_LocalPacketHandlers.empty())
+ {
+ StartProcessingThread();
+ }
+ // NOTE: could add timeout to the external profiling options
+ };
~FileOnlyProfilingConnection();
@@ -57,6 +74,11 @@ public:
Packet ReadPacket(uint32_t timeout) override;
private:
+ void AddLocalPacketHandler(ILocalPacketHandlerSharedPtr localPacketHandler);
+ void StartProcessingThread();
+ void ClearReadableList();
+ void DispatchPacketToHandlers(const Packet& packet);
+
bool WaitForStreamMeta(const unsigned char* buffer, uint32_t length);
uint32_t ToUint32(const unsigned char* data, TargetEndianness endianness);
@@ -65,10 +87,13 @@ private:
bool SendCounterSelectionPacket();
- PackageActivity GetPackageActivity(const unsigned char* buffer, uint32_t headerAsWords[2]);
+ PackageActivity GetPackageActivity(const Packet& packet, uint32_t headerAsWords[2]);
void Fail(const std::string& errorMessage);
+ void ForwardPacketToHandlers(Packet& packet);
+ void ServiceLocalHandlers();
+
static const uint32_t PIPE_MAGIC = 0x45495434;
Runtime::CreationOptions::ExternalProfilingOptions m_Options;
@@ -79,6 +104,23 @@ private:
std::mutex m_PacketAvailableMutex;
std::condition_variable m_ConditionPacketAvailable;
+
+ std::vector<ILocalPacketHandlerSharedPtr> m_PacketHandlers;
+ std::map<uint32_t, std::vector<ILocalPacketHandlerSharedPtr>> m_IndexedHandlers;
+ std::vector<ILocalPacketHandlerSharedPtr> m_UniversalHandlers;
+
+ // List of readable packets for the local packet handlers
+ std::queue<Packet> m_ReadableList;
+ // Mutex and condition variable for the readable packet list
+ std::mutex m_ReadableMutex;
+ std::condition_variable m_ConditionPacketReadable;
+ // thread that takes items from the readable list and dispatches them
+ // to the handlers.
+ std::thread m_LocalHandlersThread;
+ // atomic booleans that control the operation of the local handlers thread
+ std::atomic<bool> m_IsRunning;
+ std::atomic<bool> m_KeepRunning;
+ int m_Timeout;
};
} // namespace profiling
diff --git a/src/profiling/IProfilingConnection.hpp b/src/profiling/IProfilingConnection.hpp
index 5d6a352f1d..2a1c35f57e 100644
--- a/src/profiling/IProfilingConnection.hpp
+++ b/src/profiling/IProfilingConnection.hpp
@@ -6,6 +6,7 @@
#pragma once
#include "Packet.hpp"
+#include <armnn/profiling/ILocalPacketHandler.hpp>
#include <cstdint>
diff --git a/src/profiling/NullProfilingConnection.hpp b/src/profiling/NullProfilingConnection.hpp
new file mode 100644
index 0000000000..a72d7bfb82
--- /dev/null
+++ b/src/profiling/NullProfilingConnection.hpp
@@ -0,0 +1,41 @@
+//
+// Copyright © 2020 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "IProfilingConnection.hpp"
+
+#include <armnn/utility/IgnoreUnused.hpp>
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+class NullProfilingConnection : public IProfilingConnection
+{
+ virtual bool IsOpen() const override { return true; };
+
+ virtual void Close() override {};
+
+ virtual bool WritePacket(const unsigned char* buffer, uint32_t length) override
+ {
+ armnn::IgnoreUnused(buffer);
+ armnn::IgnoreUnused(length);
+ return true;
+ };
+
+ virtual Packet ReadPacket(uint32_t timeout) override
+ {
+ armnn::IgnoreUnused(timeout);
+ return Packet(0);
+ }
+
+};
+
+} // namespace profiling
+
+} // namespace armnn \ No newline at end of file
diff --git a/src/profiling/ProfilingConnectionFactory.cpp b/src/profiling/ProfilingConnectionFactory.cpp
index 1d264def57..7849b7e21e 100644
--- a/src/profiling/ProfilingConnectionFactory.cpp
+++ b/src/profiling/ProfilingConnectionFactory.cpp
@@ -30,6 +30,8 @@ std::unique_ptr<IProfilingConnection> ProfilingConnectionFactory::GetProfilingCo
// ProfilingConnectionDumpToFileDecorator is returned.
// 3: If both incoming and outgoing capture files are specified and "file only" then a FileOnlyProfilingConnection
// decorated by a ProfilingConnectionDumpToFileDecorator is returned.
+ // 4. There is now another option if m_FileOnly == true and there are ILocalPacketHandlers specified
+ // we can create a FileOnlyProfilingConnection without a file dump
if ((!options.m_IncomingCaptureFile.empty() || !options.m_OutgoingCaptureFile.empty()) && !options.m_FileOnly)
{
// This is type 2.
@@ -42,6 +44,11 @@ std::unique_ptr<IProfilingConnection> ProfilingConnectionFactory::GetProfilingCo
return std::make_unique<ProfilingConnectionDumpToFileDecorator>(
std::make_unique<FileOnlyProfilingConnection>(options), options);
}
+ else if (options.m_FileOnly && !options.m_LocalPacketHandlers.empty())
+ {
+ // This is the type 4.
+ return std::make_unique<FileOnlyProfilingConnection>(options);
+ }
else
{
// This is type 1.
diff --git a/src/profiling/ProfilingService.hpp b/src/profiling/ProfilingService.hpp
index f3d10e711b..ba5da7d741 100644
--- a/src/profiling/ProfilingService.hpp
+++ b/src/profiling/ProfilingService.hpp
@@ -14,6 +14,7 @@
#include "DeactivateTimelineReportingCommandHandler.hpp"
#include "ICounterRegistry.hpp"
#include "ICounterValues.hpp"
+#include <armnn/profiling/ILocalPacketHandler.hpp>
#include "IProfilingService.hpp"
#include "IReportStructure.hpp"
#include "PeriodicCounterCapture.hpp"
@@ -207,6 +208,7 @@ public:
return m_TimelineReporting;
}
+ void AddLocalPacketHandler(ILocalPacketHandlerSharedPtr localPacketHandler);
private:
//Copy/move constructors/destructors and copy/move assignment operators are deleted
ProfilingService(const ProfilingService&) = delete;
diff --git a/src/profiling/ProfilingUtils.cpp b/src/profiling/ProfilingUtils.cpp
index fc70856e5c..6d2565c610 100644
--- a/src/profiling/ProfilingUtils.cpp
+++ b/src/profiling/ProfilingUtils.cpp
@@ -5,6 +5,8 @@
#include "ProfilingUtils.hpp"
+#include "common/include/ProfilingException.hpp"
+
#include <armnn/Version.hpp>
#include <WallClockTimer.hpp>
@@ -1052,6 +1054,33 @@ uint64_t GetTimestamp()
return static_cast<uint64_t>(timestamp.count());
}
+Packet ReceivePacket(const unsigned char* buffer, uint32_t length)
+{
+ if (buffer == nullptr)
+ {
+ throw armnnProfiling::ProfilingException("data buffer is nullptr");
+ }
+ if (length < 8)
+ {
+ throw armnnProfiling::ProfilingException("length of data buffer is less than 8");
+ }
+
+ uint32_t metadataIdentifier = 0;
+ std::memcpy(&metadataIdentifier, buffer, sizeof(metadataIdentifier));
+
+ uint32_t dataLength = 0;
+ std::memcpy(&dataLength, buffer + 4u, sizeof(dataLength));
+
+ std::unique_ptr<unsigned char[]> packetData;
+ if (dataLength > 0)
+ {
+ packetData = std::make_unique<unsigned char[]>(dataLength);
+ std::memcpy(packetData.get(), buffer + 8u, dataLength);
+ }
+
+ return Packet(metadataIdentifier, dataLength, packetData);
+}
+
} // namespace profiling
} // namespace armnn
diff --git a/src/profiling/ProfilingUtils.hpp b/src/profiling/ProfilingUtils.hpp
index 5888ef0b8c..e2ffb24bf9 100644
--- a/src/profiling/ProfilingUtils.hpp
+++ b/src/profiling/ProfilingUtils.hpp
@@ -10,6 +10,7 @@
#include "ICounterDirectory.hpp"
#include "IPacketBuffer.hpp"
+#include "Packet.hpp"
#include <boost/numeric/conversion/cast.hpp>
@@ -256,6 +257,8 @@ class BufferExhaustion : public armnn::Exception
uint64_t GetTimestamp();
+Packet ReceivePacket(const unsigned char* buffer, uint32_t length);
+
} // namespace profiling
} // namespace armnn
diff --git a/src/profiling/test/FileOnlyProfilingDecoratorTests.cpp b/src/profiling/test/FileOnlyProfilingDecoratorTests.cpp
index baadb8555d..964489478e 100644
--- a/src/profiling/test/FileOnlyProfilingDecoratorTests.cpp
+++ b/src/profiling/test/FileOnlyProfilingDecoratorTests.cpp
@@ -3,12 +3,14 @@
// SPDX-License-Identifier: MIT
//
-#include "../FileOnlyProfilingConnection.hpp"
-
+#include <armnn/utility/IgnoreUnused.hpp>
+#include <FileOnlyProfilingConnection.hpp>
+#include <Filesystem.hpp>
+#include <NullProfilingConnection.hpp>
#include <ProfilingService.hpp>
#include <Runtime.hpp>
-#include <Filesystem.hpp>
-#include <armnn/utility/IgnoreUnused.hpp>
+#include "PrintPacketHeaderHandler.hpp"
+#include "TestTimelinePacketHandler.hpp"
#include <boost/filesystem.hpp>
#include <boost/numeric/conversion/cast.hpp>
@@ -37,11 +39,67 @@ class FileOnlyHelperService : public ProfilingService
BOOST_AUTO_TEST_SUITE(FileOnlyProfilingDecoratorTests)
+std::string UniqueFileName()
+{
+ std::time_t t = std::time(nullptr);
+ char mbstr[100];
+ std::strftime(mbstr, sizeof(mbstr), "%Y_%m_%d_%H_%M_%S_", std::localtime(&t));
+ std::stringstream ss;
+ ss << mbstr;
+ ss << t;
+ ss << ".bin";
+ return ss.str();
+}
+
+BOOST_AUTO_TEST_CASE(TestFileOnlyProfiling)
+{
+ // Create a temporary file name.
+ boost::filesystem::path tempPath = boost::filesystem::temp_directory_path();
+ boost::filesystem::path tempFile = UniqueFileName();
+ tempPath = tempPath / tempFile;
+ armnn::Runtime::CreationOptions creationOptions;
+ creationOptions.m_ProfilingOptions.m_EnableProfiling = true;
+ creationOptions.m_ProfilingOptions.m_FileOnly = true;
+ creationOptions.m_ProfilingOptions.m_CapturePeriod = 100;
+ creationOptions.m_ProfilingOptions.m_TimelineEnabled = true;
+ ILocalPacketHandlerSharedPtr localPacketHandlerPtr = std::make_shared<TestTimelinePacketHandler>();
+ creationOptions.m_ProfilingOptions.m_LocalPacketHandlers.push_back(localPacketHandlerPtr);
+
+ armnn::Runtime runtime(creationOptions);
+
+ // Load a simple network
+ // build up the structure of the network
+ INetworkPtr net(INetwork::Create());
+
+ IConnectableLayer* input = net->AddInputLayer(0, "input");
+
+ NormalizationDescriptor descriptor;
+ IConnectableLayer* normalize = net->AddNormalizationLayer(descriptor, "normalization");
+
+ IConnectableLayer* output = net->AddOutputLayer(0, "output");
+
+ input->GetOutputSlot(0).Connect(normalize->GetInputSlot(0));
+ normalize->GetOutputSlot(0).Connect(output->GetInputSlot(0));
+
+ input->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 4 }, DataType::Float32));
+ normalize->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 4 }, DataType::Float32));
+
+ // optimize the network
+ std::vector<armnn::BackendId> backends = { armnn::Compute::CpuRef };
+ IOptimizedNetworkPtr optNet = Optimize(*net, backends, runtime.GetDeviceSpec());
+
+ // Load it into the runtime. It should succeed.
+ armnn::NetworkId netId;
+ BOOST_TEST(runtime.LoadNetwork(netId, std::move(optNet)) == Status::Success);
+
+ static_cast<TestTimelinePacketHandler*>(localPacketHandlerPtr.get())->WaitOnInferenceCompletion(3000);
+}
+
BOOST_AUTO_TEST_CASE(DumpOutgoingValidFileEndToEnd, * boost::unit_test::disabled())
{
// Create a temporary file name.
boost::filesystem::path tempPath = boost::filesystem::temp_directory_path();
- boost::filesystem::path tempFile = boost::filesystem::unique_path();
+ boost::filesystem::path tempFile = UniqueFileName();
tempPath = tempPath / tempFile;
armnn::Runtime::CreationOptions::ExternalProfilingOptions options;
options.m_EnableProfiling = true;
diff --git a/src/profiling/test/PrintPacketHeaderHandler.cpp b/src/profiling/test/PrintPacketHeaderHandler.cpp
new file mode 100644
index 0000000000..24095d8250
--- /dev/null
+++ b/src/profiling/test/PrintPacketHeaderHandler.cpp
@@ -0,0 +1,31 @@
+//
+// Copyright © 2020 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "PrintPacketHeaderHandler.hpp"
+
+#include <iostream>
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+std::vector<uint32_t> PrintPacketHeaderHandler::GetHeadersAccepted()
+{
+ return std::vector<uint32_t>();
+}
+
+void PrintPacketHeaderHandler::HandlePacket(const Packet& packet)
+{
+ std::stringstream ss;
+ ss << "Handler Received Outgoing Packet [" << packet.GetPacketFamily() << ":" << packet.GetPacketId() << "]";
+ ss << " Length [" << packet.GetLength() << "]" << std::endl;
+ std::cout << ss.str() << std::endl;
+};
+
+} // namespace profiling
+
+} // namespace armnn \ No newline at end of file
diff --git a/src/profiling/test/PrintPacketHeaderHandler.hpp b/src/profiling/test/PrintPacketHeaderHandler.hpp
new file mode 100644
index 0000000000..3cd5921e51
--- /dev/null
+++ b/src/profiling/test/PrintPacketHeaderHandler.hpp
@@ -0,0 +1,26 @@
+//
+// Copyright © 2020 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <armnn/profiling/ILocalPacketHandler.hpp>
+#include "Packet.hpp"
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+class PrintPacketHeaderHandler : public ILocalPacketHandler
+{
+ virtual std::vector<uint32_t> GetHeadersAccepted();
+
+ virtual void HandlePacket(const Packet& packet);
+};
+
+} // namespace profiling
+
+} // namespace armnn
diff --git a/src/profiling/test/TestTimelinePacketHandler.cpp b/src/profiling/test/TestTimelinePacketHandler.cpp
new file mode 100644
index 0000000000..93fb4b433c
--- /dev/null
+++ b/src/profiling/test/TestTimelinePacketHandler.cpp
@@ -0,0 +1,132 @@
+//
+// Copyright © 2020 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "TestTimelinePacketHandler.hpp"
+#include "IProfilingConnection.hpp"
+
+#include <armnn/utility/IgnoreUnused.hpp>
+
+#include <chrono>
+#include <iostream>
+#include <sstream>
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+std::vector<uint32_t> TestTimelinePacketHandler::GetHeadersAccepted()
+{
+ std::vector<uint32_t> headers;
+ headers.push_back(m_DirectoryHeader); // message directory
+ headers.push_back(m_MessageHeader); // message
+ return headers;
+}
+
+void TestTimelinePacketHandler::HandlePacket(const Packet& packet)
+{
+ if (packet.GetHeader() == m_DirectoryHeader)
+ {
+ ProcessDirectoryPacket(packet);
+ }
+ else if (packet.GetHeader() == m_MessageHeader)
+ {
+ ProcessMessagePacket(packet);
+ }
+ else
+ {
+ std::stringstream ss;
+ ss << "Received a packet with unknown header [" << packet.GetHeader() << "]";
+ throw armnn::Exception(ss.str());
+ }
+}
+
+void TestTimelinePacketHandler::Stop()
+{
+ m_Connection->Close();
+}
+
+void TestTimelinePacketHandler::WaitOnInferenceCompletion(unsigned int timeout)
+{
+ std::unique_lock<std::mutex> lck(m_InferenceCompletedMutex);
+
+ auto start = std::chrono::high_resolution_clock::now();
+ // Here we we will go back to sleep after a spurious wake up if
+ // m_InferenceCompleted is not yet true.
+ if (!m_InferenceCompletedConditionVariable.wait_for(lck,
+ std::chrono::milliseconds(timeout),
+ [&]{return m_InferenceCompleted == true;}))
+ {
+ auto finish = std::chrono::high_resolution_clock::now();
+ std::chrono::duration<double, std::milli> elapsed = finish - start;
+ std::stringstream ss;
+ ss << "Timed out waiting on inference completion for " << elapsed.count() << " ms";
+ throw armnn::TimeoutException(ss.str());
+ }
+ return;
+}
+
+void TestTimelinePacketHandler::SetInferenceComplete()
+{
+ { // only lock when we are updating the inference completed variable
+ std::unique_lock<std::mutex> lck(m_InferenceCompletedMutex);
+ m_InferenceCompleted = true;
+ }
+ m_InferenceCompletedConditionVariable.notify_one();
+}
+
+void TestTimelinePacketHandler::ProcessDirectoryPacket(const Packet& packet)
+{
+ m_DirectoryDecoder(packet);
+}
+
+void TestTimelinePacketHandler::ProcessMessagePacket(const Packet& packet)
+{
+ m_Decoder(packet);
+}
+
+// TimelineMessageDecoder functions
+ITimelineDecoder::TimelineStatus TimelineMessageDecoder::CreateEntity(const Entity& entity)
+{
+ m_TimelineModel.AddEntity(entity.m_Guid);
+ return ITimelineDecoder::TimelineStatus::TimelineStatus_Success;
+}
+
+ITimelineDecoder::TimelineStatus TimelineMessageDecoder::CreateEventClass(
+ const ITimelineDecoder::EventClass& eventClass)
+{
+ // for the moment terminate the run here so we can get this code
+ // onto master prior to a major re-organisation
+ if (m_PacketHandler != nullptr)
+ {
+ m_PacketHandler->SetInferenceComplete();
+ }
+ IgnoreUnused(eventClass);
+ return ITimelineDecoder::TimelineStatus::TimelineStatus_Success;
+}
+
+ITimelineDecoder::TimelineStatus TimelineMessageDecoder::CreateEvent(const ITimelineDecoder::Event& event)
+{
+ IgnoreUnused(event);
+ return ITimelineDecoder::TimelineStatus::TimelineStatus_Success;
+}
+
+ITimelineDecoder::TimelineStatus TimelineMessageDecoder::CreateLabel(const ITimelineDecoder::Label& label)
+{
+ m_TimelineModel.AddLabel(label);
+ return ITimelineDecoder::TimelineStatus::TimelineStatus_Success;
+}
+
+ITimelineDecoder::TimelineStatus TimelineMessageDecoder::CreateRelationship(
+ const ITimelineDecoder::Relationship& relationship)
+{
+ m_TimelineModel.AddRelationship(relationship);
+ return ITimelineDecoder::TimelineStatus::TimelineStatus_Success;
+}
+
+} // namespace profiling
+
+} // namespace armnn \ No newline at end of file
diff --git a/src/profiling/test/TestTimelinePacketHandler.hpp b/src/profiling/test/TestTimelinePacketHandler.hpp
new file mode 100644
index 0000000000..2902e5f89c
--- /dev/null
+++ b/src/profiling/test/TestTimelinePacketHandler.hpp
@@ -0,0 +1,90 @@
+//
+// Copyright © 2020 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <armnn/profiling/ILocalPacketHandler.hpp>
+#include <armnn/profiling/ITimelineDecoder.hpp>
+#include "Packet.hpp"
+#include "ProfilingUtils.hpp"
+#include "TimelineCaptureCommandHandler.hpp"
+#include "TimelineDirectoryCaptureCommandHandler.hpp"
+#include "TimelineModel.hpp"
+
+#include <condition_variable>
+#include <map>
+#include <mutex>
+#include <vector>
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+// forward declaration of class
+class TestTimelinePacketHandler;
+class TimelineMessageDecoder : public ITimelineDecoder
+{
+public:
+ TimelineMessageDecoder(TimelineModel& model) : m_PacketHandler(nullptr), m_TimelineModel(model) {}
+ virtual TimelineStatus CreateEntity(const Entity&) override;
+ virtual TimelineStatus CreateEventClass(const EventClass&) override;
+ virtual TimelineStatus CreateEvent(const Event&) override;
+ virtual TimelineStatus CreateLabel(const Label&) override;
+ virtual TimelineStatus CreateRelationship(const Relationship&) override;
+ void SetPacketHandler(TestTimelinePacketHandler* packetHandler) {m_PacketHandler = packetHandler;};
+private:
+ TestTimelinePacketHandler* m_PacketHandler;
+ TimelineModel& m_TimelineModel;
+};
+
+class TestTimelinePacketHandler : public ILocalPacketHandler
+{
+public:
+ TestTimelinePacketHandler() :
+ m_Connection(nullptr),
+ m_InferenceCompleted(false),
+ m_DirectoryHeader(CreateTimelinePacketHeader(1, 0, 0, 0, 0, 0).first),
+ m_MessageHeader(CreateTimelinePacketHeader(1, 0, 1, 0, 0, 0).first),
+ m_MessageDecoder(m_TimelineModel),
+ m_Decoder(1, 1, 0, m_MessageDecoder),
+ m_DirectoryDecoder(1, 0, 0, m_Decoder, true)
+ { m_MessageDecoder.SetPacketHandler(this); }
+
+ virtual std::vector<uint32_t> GetHeadersAccepted() override; // ILocalPacketHandler
+
+ virtual void HandlePacket(const Packet& packet) override; // ILocalPacketHandler
+
+ void Stop();
+
+ void WaitOnInferenceCompletion(unsigned int timeout);
+ void SetInferenceComplete();
+
+ const TimelineModel& GetTimelineModel() const {return m_TimelineModel;}
+
+ virtual void SetConnection(IProfilingConnection* profilingConnection) override // ILocalPacketHandler
+ {
+ m_Connection = profilingConnection;
+ }
+
+private:
+ void ProcessDirectoryPacket(const Packet& packet);
+ void ProcessMessagePacket(const Packet& packet);
+ IProfilingConnection* m_Connection;
+ std::mutex m_InferenceCompletedMutex;
+ std::condition_variable m_InferenceCompletedConditionVariable;
+ bool m_InferenceCompleted;
+ TimelineModel m_TimelineModel;
+ uint32_t m_DirectoryHeader;
+ uint32_t m_MessageHeader;
+ TimelineMessageDecoder m_MessageDecoder;
+ timelinedecoder::TimelineCaptureCommandHandler m_Decoder;
+ timelinedecoder::TimelineDirectoryCaptureCommandHandler m_DirectoryDecoder;
+};
+
+} // namespace profiling
+
+} // namespace armnn \ No newline at end of file
diff --git a/src/profiling/test/TimelineModel.cpp b/src/profiling/test/TimelineModel.cpp
new file mode 100644
index 0000000000..73aa0c5580
--- /dev/null
+++ b/src/profiling/test/TimelineModel.cpp
@@ -0,0 +1,57 @@
+//
+// Copyright © 2020 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "TimelineModel.hpp"
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+void TimelineModel::AddLabel(const ITimelineDecoder::Label& label)
+{
+ m_LabelMap.emplace(label.m_Guid, label);
+}
+
+void TimelineModel::AddEntity(uint64_t guid)
+{
+ m_Entities.emplace(guid, guid);
+}
+
+Entity* TimelineModel::findEntity(uint64_t id)
+{
+ auto iter = m_Entities.find(id);
+ if (iter != m_Entities.end())
+ {
+ return &(iter->second);
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+void TimelineModel::AddRelationship(const ITimelineDecoder::Relationship& relationship)
+{
+ m_Relationships.emplace(relationship.m_Guid, relationship);
+}
+
+ModelRelationship* TimelineModel::findRelationship(uint64_t id)
+{
+ auto iter = m_Relationships.find(id);
+ if (iter != m_Relationships.end())
+ {
+ return &(iter->second);
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+} // namespace profiling
+
+} // namespace armnn \ No newline at end of file
diff --git a/src/profiling/test/TimelineModel.hpp b/src/profiling/test/TimelineModel.hpp
new file mode 100644
index 0000000000..7b88d5fa2c
--- /dev/null
+++ b/src/profiling/test/TimelineModel.hpp
@@ -0,0 +1,67 @@
+//
+// Copyright © 2020 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <armnn/profiling/ITimelineDecoder.hpp>
+
+#include <map>
+#include <vector>
+
+namespace armnn
+{
+
+namespace profiling
+{
+using LabelMap = std::map<uint64_t, ITimelineDecoder::Label>;
+using Attribute = std::pair<std::string, std::string>;
+using Attributes = std::map<std::string, Attribute>;
+class Entity
+{
+public:
+ Entity(uint64_t guid) : m_Guid(guid) {}
+ uint64_t GetGuid() {return m_Guid;}
+ void AddChild(Entity* child)
+ {
+ if (child != nullptr)
+ {
+ m_Children.push_back(child);
+ }
+ }
+ void AddAttribute(const std::string& type, const std::string& value)
+ {
+ Attribute attr(type, value);
+ m_Attributes.emplace(type, attr);
+ }
+private:
+ uint64_t m_Guid;
+ Attributes m_Attributes;
+ std::vector<Entity*> m_Children;
+};
+using Entities = std::map<uint64_t, Entity>;
+struct ModelRelationship
+{
+ ModelRelationship(const ITimelineDecoder::Relationship& relationship) : m_Relationship(relationship) {}
+ ITimelineDecoder::Relationship m_Relationship;
+ std::vector<Entity*> m_RelatedEntities;
+};
+using Relationships = std::map<uint64_t, ModelRelationship>;
+class TimelineModel
+{
+public:
+ void AddLabel(const ITimelineDecoder::Label& label);
+ void AddEntity(uint64_t guid);
+ Entity* findEntity(uint64_t id);
+ void AddRelationship(const ITimelineDecoder::Relationship& relationship);
+ ModelRelationship* findRelationship(uint64_t id);
+private:
+ LabelMap m_LabelMap;
+ Entities m_Entities;
+ Relationships m_Relationships;
+};
+
+} // namespace profiling
+
+} // namespace armnn \ No newline at end of file