diff options
Diffstat (limited to 'src/profiling')
-rw-r--r-- | src/profiling/FileOnlyProfilingConnection.cpp | 162 | ||||
-rw-r--r-- | src/profiling/FileOnlyProfilingConnection.hpp | 48 | ||||
-rw-r--r-- | src/profiling/IProfilingConnection.hpp | 1 | ||||
-rw-r--r-- | src/profiling/NullProfilingConnection.hpp | 41 | ||||
-rw-r--r-- | src/profiling/ProfilingConnectionFactory.cpp | 7 | ||||
-rw-r--r-- | src/profiling/ProfilingService.hpp | 2 | ||||
-rw-r--r-- | src/profiling/ProfilingUtils.cpp | 29 | ||||
-rw-r--r-- | src/profiling/ProfilingUtils.hpp | 3 | ||||
-rw-r--r-- | src/profiling/test/FileOnlyProfilingDecoratorTests.cpp | 68 | ||||
-rw-r--r-- | src/profiling/test/PrintPacketHeaderHandler.cpp | 31 | ||||
-rw-r--r-- | src/profiling/test/PrintPacketHeaderHandler.hpp | 26 | ||||
-rw-r--r-- | src/profiling/test/TestTimelinePacketHandler.cpp | 132 | ||||
-rw-r--r-- | src/profiling/test/TestTimelinePacketHandler.hpp | 90 | ||||
-rw-r--r-- | src/profiling/test/TimelineModel.cpp | 57 | ||||
-rw-r--r-- | src/profiling/test/TimelineModel.hpp | 67 |
15 files changed, 751 insertions, 13 deletions
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 |