// // Copyright © 2019 Arm Ltd. All rights reserved. // SPDX-License-Identifier: MIT // #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace armnn { namespace profiling { class MockProfilingConnection : public IProfilingConnection { public: MockProfilingConnection() : m_IsOpen(true) , m_WrittenData() , m_Packet() {} enum class PacketType { StreamMetaData, ConnectionAcknowledge, CounterDirectory, ReqCounterDirectory, PeriodicCounterSelection, PerJobCounterSelection, TimelineMessageDirectory, PeriodicCounterCapture, Unknown }; bool IsOpen() const override { std::lock_guard lock(m_Mutex); return m_IsOpen; } void Close() override { std::lock_guard lock(m_Mutex); m_IsOpen = false; } bool WritePacket(const unsigned char* buffer, uint32_t length) override { if (buffer == nullptr || length == 0) { return false; } uint32_t header = ReadUint32(buffer, 0); uint32_t packetFamily = (header >> 26); uint32_t packetId = ((header >> 16) & 1023); PacketType packetType; switch (packetFamily) { case 0: packetType = packetId < 6 ? PacketType(packetId) : PacketType::Unknown; break; case 1: packetType = packetId == 0 ? PacketType::TimelineMessageDirectory : PacketType::Unknown; break; case 3: packetType = packetId == 0 ? PacketType::PeriodicCounterCapture : PacketType::Unknown; break; default: packetType = PacketType::Unknown; } std::lock_guard lock(m_Mutex); m_WrittenData.push_back({ packetType, length }); return true; } long CheckForPacket(const std::pair packetInfo) { std::lock_guard lock(m_Mutex); if(packetInfo.second != 0) { return std::count(m_WrittenData.begin(), m_WrittenData.end(), packetInfo); } else { return std::count_if(m_WrittenData.begin(), m_WrittenData.end(), [&packetInfo](const std::pair pair) { return packetInfo.first == pair.first; }); } } bool WritePacket(Packet&& packet) { std::lock_guard lock(m_Mutex); m_Packet = std::move(packet); return true; } Packet ReadPacket(uint32_t timeout) override { boost::ignore_unused(timeout); // Simulate a delay in the reading process. The default timeout is way too long. std::this_thread::sleep_for(std::chrono::milliseconds(5)); std::lock_guard lock(m_Mutex); return std::move(m_Packet); } unsigned long GetWrittenDataSize() { std::lock_guard lock(m_Mutex); return m_WrittenData.size(); } void Clear() { std::lock_guard lock(m_Mutex); m_WrittenData.clear(); } private: bool m_IsOpen; std::vector> m_WrittenData; Packet m_Packet; mutable std::mutex m_Mutex; }; class MockProfilingConnectionFactory : public IProfilingConnectionFactory { public: IProfilingConnectionPtr GetProfilingConnection(const ExternalProfilingOptions& options) const override { boost::ignore_unused(options); return std::make_unique(); } }; class MockPacketBuffer : public IPacketBuffer { public: MockPacketBuffer(unsigned int maxSize) : m_MaxSize(maxSize) , m_Size(0) , m_Data(std::make_unique(m_MaxSize)) {} ~MockPacketBuffer() {} const unsigned char* GetReadableData() const override { return m_Data.get(); } unsigned int GetSize() const override { return m_Size; } void MarkRead() override { m_Size = 0; } void Commit(unsigned int size) override { m_Size = size; } void Release() override { m_Size = 0; } unsigned char* GetWritableData() override { return m_Data.get(); } private: unsigned int m_MaxSize; unsigned int m_Size; std::unique_ptr m_Data; }; class MockBufferManager : public IBufferManager { public: MockBufferManager(unsigned int size) : m_BufferSize(size), m_Buffer(std::make_unique(size)) {} ~MockBufferManager() {} IPacketBufferPtr Reserve(unsigned int requestedSize, unsigned int& reservedSize) override { if (requestedSize > m_BufferSize) { reservedSize = m_BufferSize; } else { reservedSize = requestedSize; } return std::move(m_Buffer); } void Commit(IPacketBufferPtr& packetBuffer, unsigned int size, bool notifyConsumer = true) override { packetBuffer->Commit(size); m_Buffer = std::move(packetBuffer); if (notifyConsumer) { FlushReadList(); } } IPacketBufferPtr GetReadableBuffer() override { return std::move(m_Buffer); } void Release(IPacketBufferPtr& packetBuffer) override { packetBuffer->Release(); m_Buffer = std::move(packetBuffer); } void MarkRead(IPacketBufferPtr& packetBuffer) override { packetBuffer->MarkRead(); m_Buffer = std::move(packetBuffer); } void SetConsumer(IConsumer* consumer) override { if (consumer != nullptr) { m_Consumer = consumer; } } void FlushReadList() override { // notify consumer that packet is ready to read if (m_Consumer != nullptr) { m_Consumer->SetReadyToRead(); } } private: unsigned int m_BufferSize; IPacketBufferPtr m_Buffer; IConsumer* m_Consumer = nullptr; }; class MockStreamCounterBuffer : public IBufferManager { public: MockStreamCounterBuffer(unsigned int maxBufferSize = 4096) : m_MaxBufferSize(maxBufferSize) , m_BufferList() , m_CommittedSize(0) , m_ReadableSize(0) , m_ReadSize(0) {} ~MockStreamCounterBuffer() {} IPacketBufferPtr Reserve(unsigned int requestedSize, unsigned int& reservedSize) override { std::lock_guard lock(m_Mutex); reservedSize = 0; if (requestedSize > m_MaxBufferSize) { throw armnn::InvalidArgumentException("The maximum buffer size that can be requested is [" + std::to_string(m_MaxBufferSize) + "] bytes"); } reservedSize = requestedSize; return std::make_unique(requestedSize); } void Commit(IPacketBufferPtr& packetBuffer, unsigned int size, bool notifyConsumer = true) override { std::lock_guard lock(m_Mutex); packetBuffer->Commit(size); m_BufferList.push_back(std::move(packetBuffer)); m_CommittedSize += size; if (notifyConsumer) { FlushReadList(); } } void Release(IPacketBufferPtr& packetBuffer) override { std::lock_guard lock(m_Mutex); packetBuffer->Release(); } IPacketBufferPtr GetReadableBuffer() override { std::lock_guard lock(m_Mutex); if (m_BufferList.empty()) { return nullptr; } IPacketBufferPtr buffer = std::move(m_BufferList.back()); m_BufferList.pop_back(); m_ReadableSize += buffer->GetSize(); return buffer; } void MarkRead(IPacketBufferPtr& packetBuffer) override { std::lock_guard lock(m_Mutex); m_ReadSize += packetBuffer->GetSize(); packetBuffer->MarkRead(); } void SetConsumer(IConsumer* consumer) override { if (consumer != nullptr) { m_Consumer = consumer; } } void FlushReadList() override { // notify consumer that packet is ready to read if (m_Consumer != nullptr) { m_Consumer->SetReadyToRead(); } } unsigned int GetCommittedSize() const { return m_CommittedSize; } unsigned int GetReadableSize() const { return m_ReadableSize; } unsigned int GetReadSize() const { return m_ReadSize; } private: // The maximum buffer size when creating a new buffer unsigned int m_MaxBufferSize; // A list of buffers std::vector m_BufferList; // The mutex to synchronize this mock's methods std::mutex m_Mutex; // The total size of the buffers that has been committed for reading unsigned int m_CommittedSize; // The total size of the buffers that can be read unsigned int m_ReadableSize; // The total size of the buffers that has already been read unsigned int m_ReadSize; // Consumer thread to notify packet is ready to read IConsumer* m_Consumer = nullptr; }; class MockSendCounterPacket : public ISendCounterPacket { public: MockSendCounterPacket(IBufferManager& sendBuffer) : m_BufferManager(sendBuffer) {} void SendStreamMetaDataPacket() override { std::string message("SendStreamMetaDataPacket"); unsigned int reserved = 0; IPacketBufferPtr buffer = m_BufferManager.Reserve(1024, reserved); memcpy(buffer->GetWritableData(), message.c_str(), static_cast(message.size()) + 1); m_BufferManager.Commit(buffer, reserved, false); } void SendCounterDirectoryPacket(const ICounterDirectory& counterDirectory) override { boost::ignore_unused(counterDirectory); std::string message("SendCounterDirectoryPacket"); unsigned int reserved = 0; IPacketBufferPtr buffer = m_BufferManager.Reserve(1024, reserved); memcpy(buffer->GetWritableData(), message.c_str(), static_cast(message.size()) + 1); m_BufferManager.Commit(buffer, reserved); } void SendPeriodicCounterCapturePacket(uint64_t timestamp, const std::vector& values) override { boost::ignore_unused(timestamp, values); std::string message("SendPeriodicCounterCapturePacket"); unsigned int reserved = 0; IPacketBufferPtr buffer = m_BufferManager.Reserve(1024, reserved); memcpy(buffer->GetWritableData(), message.c_str(), static_cast(message.size()) + 1); m_BufferManager.Commit(buffer, reserved); } void SendPeriodicCounterSelectionPacket(uint32_t capturePeriod, const std::vector& selectedCounterIds) override { boost::ignore_unused(capturePeriod, selectedCounterIds); std::string message("SendPeriodicCounterSelectionPacket"); unsigned int reserved = 0; IPacketBufferPtr buffer = m_BufferManager.Reserve(1024, reserved); memcpy(buffer->GetWritableData(), message.c_str(), static_cast(message.size()) + 1); m_BufferManager.Commit(buffer, reserved); } private: IBufferManager& m_BufferManager; }; class MockCounterDirectory : public ICounterDirectory { public: MockCounterDirectory() = default; ~MockCounterDirectory() = default; // Register profiling objects const Category* RegisterCategory(const std::string& categoryName, const armnn::Optional& deviceUid = armnn::EmptyOptional(), const armnn::Optional& counterSetUid = armnn::EmptyOptional()) { // Get the device UID uint16_t deviceUidValue = deviceUid.has_value() ? deviceUid.value() : 0; // Get the counter set UID uint16_t counterSetUidValue = counterSetUid.has_value() ? counterSetUid.value() : 0; // Create the category CategoryPtr category = std::make_unique(categoryName, deviceUidValue, counterSetUidValue); BOOST_ASSERT(category); // Get the raw category pointer const Category* categoryPtr = category.get(); BOOST_ASSERT(categoryPtr); // Register the category m_Categories.insert(std::move(category)); return categoryPtr; } const Device* RegisterDevice(const std::string& deviceName, uint16_t cores = 0, const armnn::Optional& parentCategoryName = armnn::EmptyOptional()) { // Get the device UID uint16_t deviceUid = GetNextUid(); // Create the device DevicePtr device = std::make_unique(deviceUid, deviceName, cores); BOOST_ASSERT(device); // Get the raw device pointer const Device* devicePtr = device.get(); BOOST_ASSERT(devicePtr); // Register the device m_Devices.insert(std::make_pair(deviceUid, std::move(device))); // Connect the counter set to the parent category, if required if (parentCategoryName.has_value()) { // Set the counter set UID in the parent category Category* parentCategory = const_cast(GetCategory(parentCategoryName.value())); BOOST_ASSERT(parentCategory); parentCategory->m_DeviceUid = deviceUid; } return devicePtr; } const CounterSet* RegisterCounterSet( const std::string& counterSetName, uint16_t count = 0, const armnn::Optional& parentCategoryName = armnn::EmptyOptional()) { // Get the counter set UID uint16_t counterSetUid = GetNextUid(); // Create the counter set CounterSetPtr counterSet = std::make_unique(counterSetUid, counterSetName, count); BOOST_ASSERT(counterSet); // Get the raw counter set pointer const CounterSet* counterSetPtr = counterSet.get(); BOOST_ASSERT(counterSetPtr); // Register the counter set m_CounterSets.insert(std::make_pair(counterSetUid, std::move(counterSet))); // Connect the counter set to the parent category, if required if (parentCategoryName.has_value()) { // Set the counter set UID in the parent category Category* parentCategory = const_cast(GetCategory(parentCategoryName.value())); BOOST_ASSERT(parentCategory); parentCategory->m_CounterSetUid = counterSetUid; } return counterSetPtr; } const Counter* RegisterCounter(const BackendId& backendId, const uint16_t uid, const std::string& parentCategoryName, uint16_t counterClass, uint16_t interpolation, double multiplier, const std::string& name, const std::string& description, const armnn::Optional& units = armnn::EmptyOptional(), const armnn::Optional& numberOfCores = armnn::EmptyOptional(), const armnn::Optional& deviceUid = armnn::EmptyOptional(), const armnn::Optional& counterSetUid = armnn::EmptyOptional()) { boost::ignore_unused(backendId); // Get the number of cores from the argument only uint16_t deviceCores = numberOfCores.has_value() ? numberOfCores.value() : 0; // Get the device UID uint16_t deviceUidValue = deviceUid.has_value() ? deviceUid.value() : 0; // Get the counter set UID uint16_t counterSetUidValue = counterSetUid.has_value() ? counterSetUid.value() : 0; // Get the counter UIDs and calculate the max counter UID std::vector counterUids = GetNextCounterUids(uid, deviceCores); BOOST_ASSERT(!counterUids.empty()); uint16_t maxCounterUid = deviceCores <= 1 ? counterUids.front() : counterUids.back(); // Get the counter units const std::string unitsValue = units.has_value() ? units.value() : ""; // Create the counter CounterPtr counter = std::make_shared(armnn::profiling::BACKEND_ID, counterUids.front(), maxCounterUid, counterClass, interpolation, multiplier, name, description, unitsValue, deviceUidValue, counterSetUidValue); BOOST_ASSERT(counter); // Get the raw counter pointer const Counter* counterPtr = counter.get(); BOOST_ASSERT(counterPtr); // Process multiple counters if necessary for (uint16_t counterUid : counterUids) { // Connect the counter to the parent category Category* parentCategory = const_cast(GetCategory(parentCategoryName)); BOOST_ASSERT(parentCategory); parentCategory->m_Counters.push_back(counterUid); // Register the counter m_Counters.insert(std::make_pair(counterUid, counter)); } return counterPtr; } // Getters for counts uint16_t GetCategoryCount() const override { return boost::numeric_cast(m_Categories.size()); } uint16_t GetDeviceCount() const override { return boost::numeric_cast(m_Devices.size()); } uint16_t GetCounterSetCount() const override { return boost::numeric_cast(m_CounterSets.size()); } uint16_t GetCounterCount() const override { return boost::numeric_cast(m_Counters.size()); } // Getters for collections const Categories& GetCategories() const override { return m_Categories; } const Devices& GetDevices() const override { return m_Devices; } const CounterSets& GetCounterSets() const override { return m_CounterSets; } const Counters& GetCounters() const override { return m_Counters; } // Getters for profiling objects const Category* GetCategory(const std::string& name) const override { auto it = std::find_if(m_Categories.begin(), m_Categories.end(), [&name](const CategoryPtr& category) { BOOST_ASSERT(category); return category->m_Name == name; }); if (it == m_Categories.end()) { return nullptr; } return it->get(); } const Device* GetDevice(uint16_t uid) const override { boost::ignore_unused(uid); return nullptr; // Not used by the unit tests } const CounterSet* GetCounterSet(uint16_t uid) const override { boost::ignore_unused(uid); return nullptr; // Not used by the unit tests } const Counter* GetCounter(uint16_t uid) const override { boost::ignore_unused(uid); return nullptr; // Not used by the unit tests } private: Categories m_Categories; Devices m_Devices; CounterSets m_CounterSets; Counters m_Counters; }; class SendCounterPacketTest : public SendCounterPacket { public: SendCounterPacketTest(IBufferManager& buffer) : SendCounterPacket(buffer) {} bool CreateDeviceRecordTest(const DevicePtr& device, DeviceRecord& deviceRecord, std::string& errorMessage) { return CreateDeviceRecord(device, deviceRecord, errorMessage); } bool CreateCounterSetRecordTest(const CounterSetPtr& counterSet, CounterSetRecord& counterSetRecord, std::string& errorMessage) { return CreateCounterSetRecord(counterSet, counterSetRecord, errorMessage); } bool CreateEventRecordTest(const CounterPtr& counter, EventRecord& eventRecord, std::string& errorMessage) { return CreateEventRecord(counter, eventRecord, errorMessage); } bool CreateCategoryRecordTest(const CategoryPtr& category, const Counters& counters, CategoryRecord& categoryRecord, std::string& errorMessage) { return CreateCategoryRecord(category, counters, categoryRecord, errorMessage); } }; } // namespace profiling } // namespace armnn