blob: 3651696fd3853add1b106fce195a4a45e22b1d19 [file] [log] [blame]
//
// Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
// SPDX-License-Identifier: MIT
//
#include "CounterDirectory.hpp"
#include "CounterIdMap.hpp"
#include "Holder.hpp"
#include "MockBackend.hpp"
#include "MockBackendId.hpp"
#include "PeriodicCounterCapture.hpp"
#include "PeriodicCounterSelectionCommandHandler.hpp"
#include "ProfilingOptionsConverter.hpp"
#include "ProfilingStateMachine.hpp"
#include "ProfilingUtils.hpp"
#include "RequestCounterDirectoryCommandHandler.hpp"
#include <TestUtils.hpp>
#include <armnn/utility/IgnoreUnused.hpp>
#include <armnn/BackendId.hpp>
#include <armnn/Logging.hpp>
#include <armnn/profiling/ISendTimelinePacket.hpp>
#include <armnn/profiling/ProfilingOptions.hpp>
#include <doctest/doctest.h>
#include <vector>
#include <cstdint>
#include <limits>
#include <backends/BackendProfiling.hpp>
using namespace arm::pipe;
class ReadCounterVals : public IReadCounterValues
{
virtual bool IsCounterRegistered(uint16_t counterUid) const override
{
return (counterUid > 4 && counterUid < 11);
}
virtual uint16_t GetCounterCount() const override
{
return 1;
}
virtual uint32_t GetAbsoluteCounterValue(uint16_t counterUid) const override
{
return counterUid;
}
virtual uint32_t GetDeltaCounterValue(uint16_t counterUid) override
{
return counterUid;
}
};
class MockBackendSendCounterPacket : public ISendCounterPacket
{
public:
using IndexValuePairsVector = std::vector<CounterValue>;
/// Create and write a StreamMetaDataPacket in the buffer
virtual void SendStreamMetaDataPacket() {}
/// Create and write a CounterDirectoryPacket from the parameters to the buffer.
virtual void SendCounterDirectoryPacket(const ICounterDirectory& counterDirectory)
{
armnn::IgnoreUnused(counterDirectory);
}
/// Create and write a PeriodicCounterCapturePacket from the parameters to the buffer.
virtual void SendPeriodicCounterCapturePacket(uint64_t timestamp, const IndexValuePairsVector& values)
{
m_timestamps.emplace_back(Timestamp{timestamp, values});
}
/// Create and write a PeriodicCounterSelectionPacket from the parameters to the buffer.
virtual void SendPeriodicCounterSelectionPacket(uint32_t capturePeriod,
const std::vector<uint16_t>& selectedCounterIds)
{
armnn::IgnoreUnused(capturePeriod);
armnn::IgnoreUnused(selectedCounterIds);
}
std::vector<Timestamp> GetTimestamps()
{
return m_timestamps;
}
void ClearTimestamps()
{
m_timestamps.clear();
}
private:
std::vector<Timestamp> m_timestamps;
};
arm::pipe::Packet PacketWriter(uint32_t period, std::vector<uint16_t> countervalues)
{
const uint32_t packetId = 0x40000;
uint32_t offset = 0;
uint32_t dataLength = static_cast<uint32_t>(4 + countervalues.size() * 2);
std::unique_ptr<unsigned char[]> uniqueData = std::make_unique<unsigned char[]>(dataLength);
unsigned char* data1 = reinterpret_cast<unsigned char*>(uniqueData.get());
WriteUint32(data1, offset, period);
offset += 4;
for (auto countervalue : countervalues)
{
WriteUint16(data1, offset, countervalue);
offset += 2;
}
return {packetId, dataLength, uniqueData};
}
TEST_SUITE("BackendProfilingTestSuite")
{
TEST_CASE("BackendProfilingCounterRegisterMockBackendTest")
{
// Reset the profiling service to the uninitialized state
armnn::IRuntime::CreationOptions options;
options.m_ProfilingOptions.m_EnableProfiling = true;
armnn::MockBackendInitialiser initialiser;
// Create a runtime
armnn::RuntimeImpl runtime(options);
unsigned int shiftedId = 0;
if (armnn::BackendRegistry().IsBackendRegistered("EthosNAcc"))
{
shiftedId = 4;
}
// Check if the MockBackends 3 dummy counters {0, 1, 2-5 (four cores)} are registered
armnn::BackendId mockId = armnn::MockBackendId();
const ICounterMappings& counterMap = GetProfilingService(&runtime).GetCounterMappings();
CHECK(counterMap.GetGlobalId(0, mockId) == 5 + shiftedId);
CHECK(counterMap.GetGlobalId(1, mockId) == 6 + shiftedId);
CHECK(counterMap.GetGlobalId(2, mockId) == 7 + shiftedId);
CHECK(counterMap.GetGlobalId(3, mockId) == 8 + shiftedId);
CHECK(counterMap.GetGlobalId(4, mockId) == 9 + shiftedId);
CHECK(counterMap.GetGlobalId(5, mockId) == 10 + shiftedId);
options.m_ProfilingOptions.m_EnableProfiling = false;
GetProfilingService(&runtime).ResetExternalProfilingOptions(
ConvertExternalProfilingOptions(options.m_ProfilingOptions), true);
}
TEST_CASE("TestBackendCounters")
{
Holder holder;
arm::pipe::PacketVersionResolver packetVersionResolver;
ProfilingStateMachine stateMachine;
ReadCounterVals readCounterVals;
CounterIdMap counterIdMap;
MockBackendSendCounterPacket sendCounterPacket;
const armnn::BackendId cpuAccId(armnn::Compute::CpuAcc);
const armnn::BackendId gpuAccId(armnn::Compute::GpuAcc);
ProfilingOptions options;
options.m_EnableProfiling = true;
ProfilingService profilingService;
std::unique_ptr<IBackendProfiling> cpuBackendProfilingPtr =
std::make_unique<BackendProfiling>(options, profilingService, cpuAccId);
std::unique_ptr<IBackendProfiling> gpuBackendProfilingPtr =
std::make_unique<BackendProfiling>(options, profilingService, gpuAccId);
std::shared_ptr<IBackendProfilingContext> cpuProfilingContextPtr =
std::make_shared<armnn::MockBackendProfilingContext>(cpuBackendProfilingPtr);
std::shared_ptr<IBackendProfilingContext> gpuProfilingContextPtr =
std::make_shared<armnn::MockBackendProfilingContext>(gpuBackendProfilingPtr);
std::unordered_map<armnn::BackendId,
std::shared_ptr<IBackendProfilingContext>> backendProfilingContexts;
backendProfilingContexts[cpuAccId] = cpuProfilingContextPtr;
backendProfilingContexts[gpuAccId] = gpuProfilingContextPtr;
uint16_t globalId = 5;
counterIdMap.RegisterMapping(globalId++, 0, cpuAccId);
counterIdMap.RegisterMapping(globalId++, 1, cpuAccId);
counterIdMap.RegisterMapping(globalId++, 2, cpuAccId);
counterIdMap.RegisterMapping(globalId++, 0, gpuAccId);
counterIdMap.RegisterMapping(globalId++, 1, gpuAccId);
counterIdMap.RegisterMapping(globalId++, 2, gpuAccId);
backendProfilingContexts[cpuAccId] = cpuProfilingContextPtr;
backendProfilingContexts[gpuAccId] = gpuProfilingContextPtr;
PeriodicCounterCapture periodicCounterCapture(holder, sendCounterPacket, readCounterVals,
counterIdMap, backendProfilingContexts);
uint16_t maxArmnnCounterId = 4;
PeriodicCounterSelectionCommandHandler periodicCounterSelectionCommandHandler(0,
4,
packetVersionResolver.ResolvePacketVersion(0, 4).GetEncodedValue(),
backendProfilingContexts,
counterIdMap,
holder,
maxArmnnCounterId,
periodicCounterCapture,
readCounterVals,
sendCounterPacket,
stateMachine);
stateMachine.TransitionToState(ProfilingState::NotConnected);
stateMachine.TransitionToState(ProfilingState::WaitingForAck);
stateMachine.TransitionToState(ProfilingState::Active);
uint32_t period = 12345u;
std::vector<uint16_t> cpuCounters{5, 6, 7};
std::vector<uint16_t> gpuCounters{8, 9, 10};
// Request only gpu counters
periodicCounterSelectionCommandHandler(PacketWriter(period, gpuCounters));
periodicCounterCapture.Stop();
std::set<armnn::BackendId> activeIds = holder.GetCaptureData().GetActiveBackends();
CHECK(activeIds.size() == 1);
CHECK((activeIds.find(gpuAccId) != activeIds.end()));
std::vector<Timestamp> recievedTimestamp = sendCounterPacket.GetTimestamps();
CHECK(recievedTimestamp[0].timestamp == period);
CHECK(recievedTimestamp.size() == 1);
CHECK(recievedTimestamp[0].counterValues.size() == gpuCounters.size());
for (unsigned long i=0; i< gpuCounters.size(); ++i)
{
CHECK(recievedTimestamp[0].counterValues[i].counterId == gpuCounters[i]);
CHECK(recievedTimestamp[0].counterValues[i].counterValue == i + 1u);
}
sendCounterPacket.ClearTimestamps();
// Request only cpu counters
periodicCounterSelectionCommandHandler(PacketWriter(period, cpuCounters));
periodicCounterCapture.Stop();
activeIds = holder.GetCaptureData().GetActiveBackends();
CHECK(activeIds.size() == 1);
CHECK((activeIds.find(cpuAccId) != activeIds.end()));
recievedTimestamp = sendCounterPacket.GetTimestamps();
CHECK(recievedTimestamp[0].timestamp == period);
CHECK(recievedTimestamp.size() == 1);
CHECK(recievedTimestamp[0].counterValues.size() == cpuCounters.size());
for (unsigned long i=0; i< cpuCounters.size(); ++i)
{
CHECK(recievedTimestamp[0].counterValues[i].counterId == cpuCounters[i]);
CHECK(recievedTimestamp[0].counterValues[i].counterValue == i + 1u);
}
sendCounterPacket.ClearTimestamps();
// Request combination of cpu & gpu counters with new period
period = 12222u;
periodicCounterSelectionCommandHandler(PacketWriter(period, {cpuCounters[0], gpuCounters[2],
gpuCounters[1], cpuCounters[1], gpuCounters[0]}));
periodicCounterCapture.Stop();
activeIds = holder.GetCaptureData().GetActiveBackends();
CHECK(activeIds.size() == 2);
CHECK((activeIds.find(cpuAccId) != activeIds.end()));
CHECK((activeIds.find(gpuAccId) != activeIds.end()));
recievedTimestamp = sendCounterPacket.GetTimestamps();
//
CHECK(recievedTimestamp[0].timestamp == period);
CHECK(recievedTimestamp[1].timestamp == period);
CHECK(recievedTimestamp.size() == 2);
CHECK(recievedTimestamp[0].counterValues.size() == 2);
CHECK(recievedTimestamp[1].counterValues.size() == gpuCounters.size());
CHECK(recievedTimestamp[0].counterValues[0].counterId == cpuCounters[0]);
CHECK(recievedTimestamp[0].counterValues[0].counterValue == 1u);
CHECK(recievedTimestamp[0].counterValues[1].counterId == cpuCounters[1]);
CHECK(recievedTimestamp[0].counterValues[1].counterValue == 2u);
for (unsigned long i=0; i< gpuCounters.size(); ++i)
{
CHECK(recievedTimestamp[1].counterValues[i].counterId == gpuCounters[i]);
CHECK(recievedTimestamp[1].counterValues[i].counterValue == i + 1u);
}
sendCounterPacket.ClearTimestamps();
// Request all counters
std::vector<uint16_t> counterValues;
counterValues.insert(counterValues.begin(), cpuCounters.begin(), cpuCounters.end());
counterValues.insert(counterValues.begin(), gpuCounters.begin(), gpuCounters.end());
periodicCounterSelectionCommandHandler(PacketWriter(period, counterValues));
periodicCounterCapture.Stop();
activeIds = holder.GetCaptureData().GetActiveBackends();
CHECK(activeIds.size() == 2);
CHECK((activeIds.find(cpuAccId) != activeIds.end()));
CHECK((activeIds.find(gpuAccId) != activeIds.end()));
recievedTimestamp = sendCounterPacket.GetTimestamps();
CHECK(recievedTimestamp[0].counterValues.size() == cpuCounters.size());
for (unsigned long i=0; i< cpuCounters.size(); ++i)
{
CHECK(recievedTimestamp[0].counterValues[i].counterId == cpuCounters[i]);
CHECK(recievedTimestamp[0].counterValues[i].counterValue == i + 1u);
}
CHECK(recievedTimestamp[1].counterValues.size() == gpuCounters.size());
for (unsigned long i=0; i< gpuCounters.size(); ++i)
{
CHECK(recievedTimestamp[1].counterValues[i].counterId == gpuCounters[i]);
CHECK(recievedTimestamp[1].counterValues[i].counterValue == i + 1u);
}
sendCounterPacket.ClearTimestamps();
// Request random counters with duplicates and invalid counters
counterValues = {0, 0, 200, cpuCounters[2], gpuCounters[0],3 ,30, cpuCounters[0],cpuCounters[2], gpuCounters[1], 3,
90, 0, 30, gpuCounters[0], gpuCounters[0]};
periodicCounterSelectionCommandHandler(PacketWriter(period, counterValues));
periodicCounterCapture.Stop();
activeIds = holder.GetCaptureData().GetActiveBackends();
CHECK(activeIds.size() == 2);
CHECK((activeIds.find(cpuAccId) != activeIds.end()));
CHECK((activeIds.find(gpuAccId) != activeIds.end()));
recievedTimestamp = sendCounterPacket.GetTimestamps();
CHECK(recievedTimestamp.size() == 2);
CHECK(recievedTimestamp[0].counterValues.size() == 2);
CHECK(recievedTimestamp[0].counterValues[0].counterId == cpuCounters[0]);
CHECK(recievedTimestamp[0].counterValues[0].counterValue == 1u);
CHECK(recievedTimestamp[0].counterValues[1].counterId == cpuCounters[2]);
CHECK(recievedTimestamp[0].counterValues[1].counterValue == 3u);
CHECK(recievedTimestamp[1].counterValues.size() == 2);
CHECK(recievedTimestamp[1].counterValues[0].counterId == gpuCounters[0]);
CHECK(recievedTimestamp[1].counterValues[0].counterValue == 1u);
CHECK(recievedTimestamp[1].counterValues[1].counterId == gpuCounters[1]);
CHECK(recievedTimestamp[1].counterValues[1].counterValue == 2u);
sendCounterPacket.ClearTimestamps();
// Request no counters
periodicCounterSelectionCommandHandler(PacketWriter(period, {}));
periodicCounterCapture.Stop();
activeIds = holder.GetCaptureData().GetActiveBackends();
CHECK(activeIds.size() == 0);
recievedTimestamp = sendCounterPacket.GetTimestamps();
CHECK(recievedTimestamp.size() == 0);
sendCounterPacket.ClearTimestamps();
// Request period of zero
periodicCounterSelectionCommandHandler(PacketWriter(0, counterValues));
periodicCounterCapture.Stop();
activeIds = holder.GetCaptureData().GetActiveBackends();
CHECK(activeIds.size() == 0);
recievedTimestamp = sendCounterPacket.GetTimestamps();
CHECK(recievedTimestamp.size() == 0);
}
TEST_CASE("TestBackendCounterLogging")
{
std::stringstream ss;
struct StreamRedirector
{
public:
StreamRedirector(std::ostream &stream, std::streambuf *newStreamBuffer)
: m_Stream(stream), m_BackupBuffer(m_Stream.rdbuf(newStreamBuffer))
{}
~StreamRedirector()
{ m_Stream.rdbuf(m_BackupBuffer); }
private:
std::ostream &m_Stream;
std::streambuf *m_BackupBuffer;
};
Holder holder;
arm::pipe::PacketVersionResolver packetVersionResolver;
ProfilingStateMachine stateMachine;
ReadCounterVals readCounterVals;
StreamRedirector redirect(std::cout, ss.rdbuf());
CounterIdMap counterIdMap;
MockBackendSendCounterPacket sendCounterPacket;
const armnn::BackendId cpuAccId(armnn::Compute::CpuAcc);
const armnn::BackendId gpuAccId(armnn::Compute::GpuAcc);
ProfilingOptions options;
options.m_EnableProfiling = true;
ProfilingService profilingService;
std::unique_ptr<IBackendProfiling> cpuBackendProfilingPtr =
std::make_unique<BackendProfiling>(options, profilingService, cpuAccId);
std::shared_ptr<IBackendProfilingContext> cpuProfilingContextPtr =
std::make_shared<armnn::MockBackendProfilingContext>(cpuBackendProfilingPtr);
std::unordered_map<armnn::BackendId,
std::shared_ptr<IBackendProfilingContext>> backendProfilingContexts;
uint16_t globalId = 5;
counterIdMap.RegisterMapping(globalId, 0, cpuAccId);
backendProfilingContexts[cpuAccId] = cpuProfilingContextPtr;
PeriodicCounterCapture periodicCounterCapture(holder, sendCounterPacket, readCounterVals,
counterIdMap, backendProfilingContexts);
uint16_t maxArmnnCounterId = 4;
PeriodicCounterSelectionCommandHandler periodicCounterSelectionCommandHandler(0,
4,
packetVersionResolver.ResolvePacketVersion(0, 4).GetEncodedValue(),
backendProfilingContexts,
counterIdMap,
holder,
maxArmnnCounterId,
periodicCounterCapture,
readCounterVals,
sendCounterPacket,
stateMachine);
stateMachine.TransitionToState(ProfilingState::NotConnected);
stateMachine.TransitionToState(ProfilingState::WaitingForAck);
stateMachine.TransitionToState(ProfilingState::Active);
uint32_t period = 15939u;
armnn::SetAllLoggingSinks(true, false, false);
SetLogFilter(armnn::LogSeverity::Warning);
periodicCounterSelectionCommandHandler(PacketWriter(period, {5}));
periodicCounterCapture.Stop();
SetLogFilter(armnn::LogSeverity::Fatal);
CHECK(ss.str().find("ActivateCounters example test error") != std::string::npos);
}
TEST_CASE("BackendProfilingContextGetSendTimelinePacket")
{
// Reset the profiling service to the uninitialized state
armnn::IRuntime::CreationOptions options;
options.m_ProfilingOptions.m_EnableProfiling = true;
ProfilingService profilingService;
profilingService.ConfigureProfilingService(
ConvertExternalProfilingOptions(options.m_ProfilingOptions), true);
armnn::MockBackendInitialiser initialiser;
// Create a runtime. During this the mock backend will be registered and context returned.
armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
armnn::MockBackendProfilingService mockProfilingService = armnn::MockBackendProfilingService::Instance();
armnn::MockBackendProfilingContext *mockBackEndProfilingContext = mockProfilingService.GetContext();
// Check that there is a valid context set.
CHECK(mockBackEndProfilingContext);
armnn::IBackendInternal::IBackendProfilingPtr& backendProfilingIface =
mockBackEndProfilingContext->GetBackendProfiling();
CHECK(backendProfilingIface);
// Now for the meat of the test. We're just going to send a random packet and make sure there
// are no exceptions or errors. The sending of packets is already tested in SendTimelinePacketTests.
std::unique_ptr<ISendTimelinePacket> timelinePacket =
backendProfilingIface->GetSendTimelinePacket();
// Send TimelineEntityClassBinaryPacket
const uint64_t entityBinaryPacketProfilingGuid = 123456u;
timelinePacket->SendTimelineEntityBinaryPacket(entityBinaryPacketProfilingGuid);
timelinePacket->Commit();
// Reset the profiling servie after the test.
options.m_ProfilingOptions.m_EnableProfiling = false;
profilingService.ResetExternalProfilingOptions(
ConvertExternalProfilingOptions(options.m_ProfilingOptions), true);
}
TEST_CASE("GetProfilingGuidGenerator")
{
// Reset the profiling service to the uninitialized state
armnn::IRuntime::CreationOptions options;
options.m_ProfilingOptions.m_EnableProfiling = true;
armnn::MockBackendInitialiser initialiser;
// Create a runtime. During this the mock backend will be registered and context returned.
armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
armnn::MockBackendProfilingService mockProfilingService = armnn::MockBackendProfilingService::Instance();
armnn::MockBackendProfilingContext *mockBackEndProfilingContext = mockProfilingService.GetContext();
// Check that there is a valid context set.
CHECK(mockBackEndProfilingContext);
armnn::IBackendInternal::IBackendProfilingPtr& backendProfilingIface =
mockBackEndProfilingContext->GetBackendProfiling();
CHECK(backendProfilingIface);
// Get the Guid generator and check the getting two Guid's results in the second being greater than the first.
IProfilingGuidGenerator& guidGenerator = backendProfilingIface->GetProfilingGuidGenerator();
const ProfilingDynamicGuid& firstGuid = guidGenerator.NextGuid();
const ProfilingDynamicGuid& secondGuid = guidGenerator.NextGuid();
CHECK(secondGuid > firstGuid);
// Reset the profiling servie after the test.
options.m_ProfilingOptions.m_EnableProfiling = false;
}
}