From 15db745d59a796fd05e3bb5a8b735f25710bdf22 Mon Sep 17 00:00:00 2001 From: Finn Williams Date: Tue, 15 Oct 2019 14:22:13 +0100 Subject: IVGCVSW-3989 Create the Counter Directory Decoder Signed-off-by: Finn Williams Change-Id: If388e60434eae39d82b639d2275680679963624c --- CMakeLists.txt | 4 + tests/profiling/gatordmock/CommandFileParser.cpp | 9 + tests/profiling/gatordmock/CounterDirectory.hpp | 263 ++++++++++++++++ .../gatordmock/DirectoryCaptureCommandHandler.cpp | 341 +++++++++++++++++++++ .../gatordmock/DirectoryCaptureCommandHandler.hpp | 75 +++++ tests/profiling/gatordmock/GatordMockMain.cpp | 5 + tests/profiling/gatordmock/GatordMockService.cpp | 10 + tests/profiling/gatordmock/GatordMockService.hpp | 3 + tests/profiling/gatordmock/MockUtils.cpp | 57 ++++ tests/profiling/gatordmock/MockUtils.hpp | 25 ++ .../PeriodicCounterCaptureCommandHandler.cpp | 21 +- .../profiling/gatordmock/tests/GatordMockTests.cpp | 167 ++++++++-- 12 files changed, 927 insertions(+), 53 deletions(-) create mode 100644 tests/profiling/gatordmock/CounterDirectory.hpp create mode 100644 tests/profiling/gatordmock/DirectoryCaptureCommandHandler.cpp create mode 100644 tests/profiling/gatordmock/DirectoryCaptureCommandHandler.hpp create mode 100644 tests/profiling/gatordmock/MockUtils.cpp create mode 100644 tests/profiling/gatordmock/MockUtils.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index fac3f16dc3..8fdfc95a76 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -905,8 +905,12 @@ if(BUILD_GATORD_MOCK) tests/profiling/gatordmock/CommandFileParser.cpp tests/profiling/gatordmock/CommandLineProcessor.hpp tests/profiling/gatordmock/CommandLineProcessor.cpp + tests/profiling/gatordmock/CounterDirectory.hpp + tests/profiling/gatordmock/DirectoryCaptureCommandHandler.cpp + tests/profiling/gatordmock/DirectoryCaptureCommandHandler.hpp tests/profiling/gatordmock/GatordMockService.hpp tests/profiling/gatordmock/GatordMockService.cpp + tests/profiling/gatordmock/MockUtils.cpp tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.cpp tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.hpp tests/profiling/gatordmock/PeriodicCounterSelectionResponseHandler.cpp diff --git a/tests/profiling/gatordmock/CommandFileParser.cpp b/tests/profiling/gatordmock/CommandFileParser.cpp index e86763b55f..d08e72cadd 100644 --- a/tests/profiling/gatordmock/CommandFileParser.cpp +++ b/tests/profiling/gatordmock/CommandFileParser.cpp @@ -34,6 +34,15 @@ void CommandFileParser::ParseFile(std::string CommandFile, GatordMockService& mo std::string command = tokens[0]; + if (command == "LIST") + { + // Expected format for the SET command + // + // LIST + // + + mockService.SendRequestCounterDir(); + } if (command == "SET") { // Expected format for the SET command diff --git a/tests/profiling/gatordmock/CounterDirectory.hpp b/tests/profiling/gatordmock/CounterDirectory.hpp new file mode 100644 index 0000000000..7b45e661e0 --- /dev/null +++ b/tests/profiling/gatordmock/CounterDirectory.hpp @@ -0,0 +1,263 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + + +#include "GatordMockService.hpp" +#include "MockUtils.hpp" + +#include "Packet.hpp" +#include "CommandHandlerFunctor.hpp" +#include "SendCounterPacket.hpp" +#include "IPeriodicCounterCapture.hpp" + +#include +#include +#include +#include +#include + +namespace armnn +{ + +namespace gatordmock +{ + +struct EventRecord +{ + uint16_t m_CounterUid; + uint16_t m_MaxCounterUid; + uint16_t m_DeviceUid; + uint16_t m_CounterSetUid; + uint16_t m_CounterClass; + uint16_t m_CounterInterpolation; + double m_CounterMultiplier; + std::string m_CounterName; + std::string m_CounterDescription; + std::string m_CounterUnits; + + static void printHeader(std::string categoryName) + { + std::string header; + + header.append(gatordmock::CentreAlignFormatting("Counter Name", 20)); + header.append(" | "); + header.append(gatordmock::CentreAlignFormatting("Description", 50)); + header.append(" | "); + header.append(gatordmock::CentreAlignFormatting("Units", 14)); + header.append(" | "); + header.append(gatordmock::CentreAlignFormatting("UID", 6)); + header.append(" | "); + header.append(gatordmock::CentreAlignFormatting("Max UID",10)); + header.append(" | "); + header.append(gatordmock::CentreAlignFormatting("Class", 8)); + header.append(" | "); + header.append(gatordmock::CentreAlignFormatting("Interpolation", 14)); + header.append(" | "); + header.append(gatordmock::CentreAlignFormatting("Multiplier", 20)); + header.append(" | "); + header.append(gatordmock::CentreAlignFormatting("Counter set UID", 16)); + header.append(" | "); + header.append(gatordmock::CentreAlignFormatting("Device UID", 14)); + header.append("\n"); + + std::cout << "\n" << "\n"; + std::cout << gatordmock::CentreAlignFormatting("EVENTS IN CATEGORY: " + categoryName, + static_cast(header.size())); + std::cout << "\n"; + std::cout << std::string(header.size(), '=') << "\n"; + std::cout << header; + } + + void printContents() const + { + std::string body; + + body.append(gatordmock::CentreAlignFormatting(m_CounterName, 20)); + body.append(" | "); + body.append(gatordmock::CentreAlignFormatting(m_CounterDescription, 50)); + body.append(" | "); + body.append(gatordmock::CentreAlignFormatting(m_CounterUnits, 14)); + body.append(" | "); + body.append(gatordmock::CentreAlignFormatting(std::to_string(m_CounterUid), 6)); + body.append(" | "); + body.append(gatordmock::CentreAlignFormatting(std::to_string(m_MaxCounterUid), 10)); + body.append(" | "); + body.append(gatordmock::CentreAlignFormatting(std::to_string(m_CounterClass), 8)); + body.append(" | "); + body.append(gatordmock::CentreAlignFormatting(std::to_string(m_CounterInterpolation), 14)); + body.append(" | "); + body.append(gatordmock::CentreAlignFormatting(std::to_string(m_CounterMultiplier), 20)); + body.append(" | "); + body.append(gatordmock::CentreAlignFormatting(std::to_string(m_CounterSetUid), 16)); + body.append(" | "); + body.append(gatordmock::CentreAlignFormatting(std::to_string(m_DeviceUid), 14)); + + body.append("\n"); + + std::cout << std::string(body.size(), '-') << "\n"; + + std::cout << body; + } +}; + +struct CategoryRecord +{ + uint16_t m_DeviceUid; + uint16_t m_CounterSet; + uint16_t m_EventCount; + std::string m_CategoryName; + std::vector m_EventRecords; + + void print() const + { + std::string body; + std::string header; + + header.append(gatordmock::CentreAlignFormatting("Name", 20)); + header.append(" | "); + header.append(gatordmock::CentreAlignFormatting("Device", 12)); + header.append(" | "); + header.append(gatordmock::CentreAlignFormatting("Counter set UID:", 16)); + header.append(" | "); + header.append(gatordmock::CentreAlignFormatting("Event Count", 14)); + header.append("\n"); + + body.append(gatordmock::CentreAlignFormatting(m_CategoryName, 20)); + body.append(" | "); + body.append(gatordmock::CentreAlignFormatting(std::to_string(m_DeviceUid), 12)); + body.append(" | "); + body.append(gatordmock::CentreAlignFormatting(std::to_string(m_CounterSet), 16)); + body.append(" | "); + body.append(gatordmock::CentreAlignFormatting(std::to_string(m_EventCount), 14)); + + std::cout << "\n" << "\n"; + std::cout << gatordmock::CentreAlignFormatting("CATEGORY", static_cast(header.size())); + std::cout << "\n"; + std::cout << std::string(header.size(), '=') << "\n"; + + std::cout<< header; + + std::cout << std::string(body.size(), '-') << "\n"; + + std::cout<< body; + + if(m_EventRecords.size() > 0) + { + EventRecord::printHeader(m_CategoryName); + + std::for_each(m_EventRecords.begin(), m_EventRecords.end(), std::mem_fun_ref(&EventRecord::printContents)); + } + } +}; + +struct CounterSetRecord +{ + uint16_t m_CounterSetUid; + uint16_t m_CounterSetCount; + std::string m_CounterSetName; + + static void printHeader() + { + std::string header; + + header.append(gatordmock::CentreAlignFormatting("Counter set name", 20)); + header.append(" | "); + header.append(gatordmock::CentreAlignFormatting("UID",13)); + header.append(" | "); + header.append(gatordmock::CentreAlignFormatting("Count",10)); + header.append("\n"); + + std::cout << "\n" << "\n"; + std::cout << gatordmock::CentreAlignFormatting("COUNTER SETS", static_cast(header.size())); + std::cout << "\n"; + std::cout << std::string(header.size(), '=') << "\n"; + + std::cout<< header; + } + + void printContents() const + { + std::string body; + + body.append(gatordmock::CentreAlignFormatting(m_CounterSetName, 20)); + body.append(" | "); + body.append(gatordmock::CentreAlignFormatting(std::to_string(m_CounterSetUid), 13)); + body.append(" | "); + body.append(gatordmock::CentreAlignFormatting(std::to_string(m_CounterSetCount), 10)); + body.append("\n"); + + std::cout << std::string(body.size(), '-') << "\n"; + + std::cout<< body; + } +}; + +struct DeviceRecord +{ + uint16_t m_DeviceUid; + uint16_t m_DeviceCores; + std::string m_DeviceName; + + static void printHeader() + { + std::string header; + + header.append(gatordmock::CentreAlignFormatting("Device name", 20)); + header.append(" | "); + header.append(gatordmock::CentreAlignFormatting("UID",13)); + header.append(" | "); + header.append(gatordmock::CentreAlignFormatting("Cores",10)); + header.append("\n"); + + std::cout << "\n" << "\n"; + std::cout << gatordmock::CentreAlignFormatting("DEVICES", static_cast(header.size())); + std::cout << "\n"; + std::cout << std::string(header.size(), '=') << "\n"; + std::cout<< header; + } + + void printContents() const + { + std::string body; + + body.append(gatordmock::CentreAlignFormatting(m_DeviceName, 20)); + body.append(" | "); + body.append(gatordmock::CentreAlignFormatting(std::to_string(m_DeviceUid), 13)); + body.append(" | "); + body.append(gatordmock::CentreAlignFormatting(std::to_string(m_DeviceCores), 10)); + body.append("\n"); + + std::cout << std::string(body.size(), '-') << "\n"; + std::cout<< body; + } +}; + +struct CounterDirectory +{ + std::vector m_Categories; + std::vector m_CounterSets; + std::vector m_DeviceRecords; + + void print() const + { + DeviceRecord::printHeader(); + std::for_each(m_DeviceRecords.begin(), m_DeviceRecords.end(), + std::mem_fun_ref(&DeviceRecord::printContents)); + + CounterSetRecord::printHeader(); + std::for_each(m_CounterSets.begin(), m_CounterSets.end(), + std::mem_fun_ref(&CounterSetRecord::printContents)); + + std::for_each(m_Categories.begin(), m_Categories.end(), + std::mem_fun_ref(&CategoryRecord::print)); + std::cout << "\n"; + } +}; + +} // namespace gatordmock + +} // namespace armnn \ No newline at end of file diff --git a/tests/profiling/gatordmock/DirectoryCaptureCommandHandler.cpp b/tests/profiling/gatordmock/DirectoryCaptureCommandHandler.cpp new file mode 100644 index 0000000000..eafef0b53c --- /dev/null +++ b/tests/profiling/gatordmock/DirectoryCaptureCommandHandler.cpp @@ -0,0 +1,341 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include +#include "DirectoryCaptureCommandHandler.hpp" + +namespace armnn +{ + +namespace gatordmock +{ + +// Utils +uint32_t uint16_t_size = sizeof(uint16_t); +uint32_t uint32_t_size = sizeof(uint32_t); + +void DirectoryCaptureCommandHandler::ParseData(const armnn::profiling::Packet& packet) +{ + uint16_t categoryRecordCount; + uint16_t counterSetRecordCount; + uint16_t deviceRecordCount; + + uint32_t offset = 0; + + if (packet.GetLength() < 8) + { + std::cout << "Counter directory packet received." << std::endl; + return; + } + + const unsigned char* data = reinterpret_cast(packet.GetData()); + // Body header word 0: + // 0:15 [16] reserved: all zeros + offset += uint16_t_size; + // 16:31 [16] device_records_count: number of entries in the device_records_pointer_table + deviceRecordCount = profiling::ReadUint16(data, offset); + offset += uint16_t_size; + + // Body header word 1: + // 0:31 [32] device_records_pointer_table_offset: offset to the device_records_pointer_table + // The offset is always zero here, as the device record pointer table field is always the first item in the pool + offset += uint32_t_size; + + // Body header word 2: + // 0:15 [16] reserved: all zeros + offset += uint16_t_size; + // 16:31 [16] counter_set_count: number of entries in the counter_set_pointer_table + counterSetRecordCount = profiling::ReadUint16(data, offset); + offset += uint16_t_size; + + // Body header word 3: + // 0:31 [32] counter_set_pointer_table_offset: offset to the counter_set_pointer_table + // counterPointerTableSetOffset = profiling::ReadUint32(data, offset); + offset += uint32_t_size; + + // Body header word 4: + // 0:15 [16] reserved: all zeros + offset += uint16_t_size; + // 16:31 [16] categories_count: number of entries in the categories_pointer_table + categoryRecordCount = profiling::ReadUint16(data, offset); + offset += uint16_t_size; + + // Body header word 5: + // 0:31 [32] categories_pointer_table_offset: offset to the categories_pointer_table + // categoriesPointerTableOffset = profiling::ReadUint32(data, offset); + offset += uint32_t_size; + + std::vector deviceRecordOffsets(deviceRecordCount); + std::vector counterSetOffsets(counterSetRecordCount); + std::vector categoryOffsets(categoryRecordCount); + + for (uint32_t i = 0; i < deviceRecordCount; ++i) + { + deviceRecordOffsets[i] = profiling::ReadUint32(data, offset); + offset += uint32_t_size; + } + + for (uint32_t i = 0; i < counterSetRecordCount; ++i) + { + counterSetOffsets[i] = profiling::ReadUint32(data, offset); + offset += uint32_t_size; + } + + for (uint32_t i = 0; i < categoryRecordCount; ++i) + { + categoryOffsets[i] = profiling::ReadUint32(data, offset); + offset += uint32_t_size; + } + + m_CounterDirectory.m_DeviceRecords = ReadDeviceRecords(data, offset, deviceRecordOffsets); + m_CounterDirectory.m_CounterSets = ReadCounterSetRecords(data, offset, counterSetOffsets); + m_CounterDirectory.m_Categories = ReadCategoryRecords(data, offset, categoryOffsets); + + m_CounterDirectoryCount.operator++(std::memory_order_release); +} + +std::vector DirectoryCaptureCommandHandler::ReadDeviceRecords(const unsigned char* const data, + uint32_t offset, + std::vector deviceRecordOffsets) +{ + uint32_t deviceRecordCount = static_cast(deviceRecordOffsets.size()); + std::vector deviceRecords(deviceRecordCount); + + for(uint32_t deviceIndex = 0; deviceIndex < deviceRecordCount; ++deviceIndex) + { + uint32_t deviceRecordOffset = offset + deviceRecordOffsets[deviceIndex]; + // Device record word 0: + // 0:15 [16] cores: the number of individual streams of counters for one or more cores of some device + deviceRecords[deviceIndex].m_DeviceCores = profiling::ReadUint16(data, deviceRecordOffset); + // 16:31 [16] deviceUid: the unique identifier for the device + deviceRecordOffset += uint16_t_size; + deviceRecords[deviceIndex].m_DeviceUid = profiling::ReadUint16(data, deviceRecordOffset); + deviceRecordOffset += uint16_t_size; + + // Device record word 1: + // Offset from the beginning of the device record pool to the name field. + uint32_t nameOffset = profiling::ReadUint32(data, deviceRecordOffset); + + deviceRecordOffset += uint32_t_size; + deviceRecordOffset += uint32_t_size; + deviceRecordOffset += nameOffset; + + deviceRecords[deviceIndex].m_DeviceName = GetStringNameFromBuffer(data, deviceRecordOffset); + } + + return deviceRecords; +} + + +std::vector + DirectoryCaptureCommandHandler::ReadCounterSetRecords(const unsigned char* const data, + uint32_t offset, + std::vector counterSetOffsets) +{ + uint32_t counterSetRecordCount = static_cast(counterSetOffsets.size()); + std::vector counterSets(counterSetRecordCount); + + for (uint32_t counterSetIndex = 0; counterSetIndex < counterSetRecordCount; ++counterSetIndex) + { + uint32_t counterSetOffset = offset + counterSetOffsets[counterSetIndex]; + + // Counter set record word 0: + // 0:15 [16] count: the number of counters which can be active in this set at any one time + counterSets[counterSetIndex].m_CounterSetCount = profiling::ReadUint16(data, counterSetOffset); + counterSetOffset += uint16_t_size; + + // 16:31 [16] deviceUid: the unique identifier for the counter_set + counterSets[counterSetIndex].m_CounterSetUid = profiling::ReadUint16(data, counterSetOffset); + counterSetOffset += uint16_t_size; + + // Counter set record word 1: + // 0:31 [32] name_offset: offset from the beginning of the counter set pool to the name field + // The offset is always zero here, as the name field is always the first (and only) item in the pool + counterSetOffset += uint32_t_size; + counterSetOffset += uint32_t_size; + + counterSets[counterSetIndex].m_CounterSetName = GetStringNameFromBuffer(data, counterSetOffset); + } + + return counterSets; +} + +std::vector DirectoryCaptureCommandHandler::ReadCategoryRecords(const unsigned char* const data, + uint32_t offset, + std::vector categoryOffsets) +{ + uint32_t categoryRecordCount = static_cast(categoryOffsets.size()); + std::vector categories(categoryRecordCount); + + for (uint32_t categoryIndex = 0; categoryIndex < categoryRecordCount; ++categoryIndex) + { + uint32_t categoryRecordOffset = offset + categoryOffsets[categoryIndex]; + + // Category record word 0: + // 0:15 The deviceUid of a counter_set the category is associated with. + // Set to zero if the category is NOT associated with a counter set. + categories[categoryIndex].m_CounterSet = profiling::ReadUint16(data, categoryRecordOffset); + categoryRecordOffset += uint16_t_size; + + // 16:31 The deviceUid of a device element which identifies some hardware device that the category belongs to. + // Set to zero if the category is NOT associated with a device + categories[categoryIndex].m_DeviceUid = profiling::ReadUint16(data, categoryRecordOffset); + categoryRecordOffset += uint16_t_size; + + // Category record word 1: + // 0:15 Reserved, value 0x0000. + categoryRecordOffset += uint16_t_size; + // 16:31 Number of events belonging to this category. + categories[categoryIndex].m_EventCount = profiling::ReadUint16(data, categoryRecordOffset); + categoryRecordOffset += uint16_t_size; + + // Category record word 2 + // 0:31 Offset from the beginning of the category data pool to the event_pointer_table + uint32_t eventPointerTableOffset = profiling::ReadUint32(data, categoryRecordOffset); + categoryRecordOffset += uint32_t_size; + + // Category record word 3 + // 0:31 Offset from the beginning of the category data pool to the name field. + uint32_t nameOffset = profiling::ReadUint32(data, categoryRecordOffset); + categoryRecordOffset += uint32_t_size; + + //Get the events for the category + uint32_t eventCount = categories[categoryIndex].m_EventCount; + + std::vector eventRecordsOffsets(eventCount); + + eventPointerTableOffset += categoryRecordOffset; + + for (uint32_t eventIndex = 0; eventIndex < eventCount; ++eventIndex) + { + eventRecordsOffsets[eventIndex] = + profiling::ReadUint32(data, eventPointerTableOffset + uint32_t_size * eventIndex); + } + + categories[categoryIndex].m_EventRecords = ReadEventRecords(data, categoryRecordOffset, eventRecordsOffsets); + + categoryRecordOffset += uint32_t_size; + + categories[categoryIndex].m_CategoryName = GetStringNameFromBuffer(data, categoryRecordOffset + nameOffset); + } + + return categories; +} + + +std::vector DirectoryCaptureCommandHandler::ReadEventRecords(const unsigned char* const data, + uint32_t offset, + std::vector eventRecordsOffsets) +{ + uint32_t eventCount = static_cast(eventRecordsOffsets.size()); + + std::vector eventRecords(eventCount); + for (unsigned long i = 0; i < eventCount; ++i) + { + uint32_t eventRecordOffset = eventRecordsOffsets[i] + offset; + + // Event record word 0: + // 0:15 [16] count_uid: unique ID for the counter. Must be unique across all counters in all categories + eventRecords[i].m_CounterUid = profiling::ReadUint16(data, eventRecordOffset); + eventRecordOffset += uint16_t_size; + // 16:31 [16] max_counter_uid: if the device this event is associated with has more than one core and there + // is one of these counters per core this value will be set to + // (counter_uid + cores (from device_record)) - 1. + // If there is only a single core then this value will be the same as + // the counter_uid value + eventRecords[i].m_MaxCounterUid = profiling::ReadUint16(data, eventRecordOffset); + eventRecordOffset += uint16_t_size; + + // Event record word 1: + // 0:15 [16] counter_set: UID of the counter_set this event is associated with. Set to zero if the event + // is NOT associated with a counter_set + eventRecords[i].m_DeviceUid = profiling::ReadUint16(data, eventRecordOffset); + eventRecordOffset += uint16_t_size; + + // 16:31 [16] device: UID of the device this event is associated with. Set to zero if the event is NOT + // associated with a device + eventRecords[i].m_CounterSetUid = profiling::ReadUint16(data, eventRecordOffset); + eventRecordOffset += uint16_t_size; + + // Event record word 2: + // 0:15 [16] interpolation: type describing how to interpolate each data point in a stream of data points + eventRecords[i].m_CounterClass =profiling::ReadUint16(data, eventRecordOffset); + eventRecordOffset += uint16_t_size; + + // 16:31 [16] class: type describing how to treat each data point in a stream of data points + eventRecords[i].m_CounterInterpolation = profiling::ReadUint16(data, eventRecordOffset); + eventRecordOffset += uint16_t_size; + + // Event record word 3-4: + // 0:63 [64] multiplier: internal data stream is represented as integer values, this allows scaling of + // those values as if they are fixed point numbers. Zero is not a valid value + uint32_t multiplier[2] = { 0u, 0u }; + + multiplier[0] = profiling::ReadUint32(data, eventRecordOffset); + eventRecordOffset += uint32_t_size; + multiplier[1] = profiling::ReadUint32(data, eventRecordOffset); + eventRecordOffset += uint32_t_size; + + std::memcpy(&eventRecords[i].m_CounterMultiplier, &multiplier, sizeof(multiplier)); + + // Event record word 5: + // 0:31 [32] name_eventRecordOffset: eventRecordOffset from the + // beginning of the event record pool to the name field + // The eventRecordOffset is always zero here, as the name field is always the first item in the pool + eventRecordOffset += uint32_t_size; + + // Event record word 6: + // 0:31 [32] description_eventRecordOffset: eventRecordOffset from the + // beginning of the event record pool to the description field + // The size of the name buffer in bytes + uint32_t descriptionOffset = profiling::ReadUint32(data, eventRecordOffset); + eventRecordOffset += uint32_t_size; + + // Event record word 7: + // 0:31 [32] units_eventRecordOffset: (optional) eventRecordOffset from the + // beginning of the event record pool to the units field. + // An eventRecordOffset value of zero indicates this field is not provided + uint32_t unitsOffset = profiling::ReadUint32(data, eventRecordOffset); + eventRecordOffset += uint32_t_size; + eventRecordOffset += uint32_t_size; + + eventRecords[i].m_CounterName = GetStringNameFromBuffer(data, eventRecordOffset); + + eventRecords[i].m_CounterDescription = GetStringNameFromBuffer(data, eventRecordOffset + descriptionOffset); + + eventRecords[i].m_CounterUnits = GetStringNameFromBuffer(data, eventRecordOffset + unitsOffset); + } + + return eventRecords; +} + +void DirectoryCaptureCommandHandler::operator()(const profiling::Packet& packet) +{ + if (!m_QuietOperation)// Are we supposed to print to stdout? + { + std::cout << "Counter directory packet received." << std::endl; + } + + ParseData(packet); + + if (!m_QuietOperation) + { + m_CounterDirectory.print(); + } +} + +CounterDirectory DirectoryCaptureCommandHandler::GetCounterDirectory() const +{ + return m_CounterDirectory; +} + +uint32_t DirectoryCaptureCommandHandler::GetCounterDirectoryCount() const +{ + return m_CounterDirectoryCount.load(std::memory_order_acquire); +} + +} // namespace gatordmock + +} // namespace armnn \ No newline at end of file diff --git a/tests/profiling/gatordmock/DirectoryCaptureCommandHandler.hpp b/tests/profiling/gatordmock/DirectoryCaptureCommandHandler.hpp new file mode 100644 index 0000000000..4cf96be741 --- /dev/null +++ b/tests/profiling/gatordmock/DirectoryCaptureCommandHandler.hpp @@ -0,0 +1,75 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "CounterDirectory.hpp" +#include "GatordMockService.hpp" +#include "MockUtils.hpp" + + +#include "Packet.hpp" +#include "CommandHandlerFunctor.hpp" +#include "SendCounterPacket.hpp" +#include "IPeriodicCounterCapture.hpp" + + +#include +#include +#include + +namespace armnn +{ + +namespace gatordmock +{ + +class DirectoryCaptureCommandHandler : public profiling::CommandHandlerFunctor +{ + +public: + + DirectoryCaptureCommandHandler(uint32_t familyId, + uint32_t packetId, + uint32_t version, + bool quietOperation = false) + : CommandHandlerFunctor(familyId, packetId, version) + , m_QuietOperation(quietOperation) + , m_CounterDirectoryCount(0) + {} + + void operator()(const armnn::profiling::Packet &packet) override; + + CounterDirectory GetCounterDirectory() const; + uint32_t GetCounterDirectoryCount() const; + +private: + void ParseData(const armnn::profiling::Packet &packet); + + std::vector ReadCategoryRecords(const unsigned char *const data, + uint32_t offset, + std::vector categoryOffsets); + + std::vector ReadCounterSetRecords(const unsigned char *const data, + uint32_t offset, + std::vector eventRecordsOffsets); + + std::vector ReadDeviceRecords(const unsigned char *const data, + uint32_t offset, + std::vector eventRecordsOffsets); + + std::vector ReadEventRecords(const unsigned char *const data, + uint32_t offset, + std::vector eventRecordsOffsets); + + CounterDirectory m_CounterDirectory; + + bool m_QuietOperation; + std::atomic m_CounterDirectoryCount; +}; + +} // namespace gatordmock + +} // namespace armnn diff --git a/tests/profiling/gatordmock/GatordMockMain.cpp b/tests/profiling/gatordmock/GatordMockMain.cpp index dd483631ad..9dac6d976e 100644 --- a/tests/profiling/gatordmock/GatordMockMain.cpp +++ b/tests/profiling/gatordmock/GatordMockMain.cpp @@ -7,6 +7,7 @@ #include "../../../src/profiling/PeriodicCounterSelectionCommandHandler.hpp" #include "CommandFileParser.hpp" #include "CommandLineProcessor.hpp" +#include "DirectoryCaptureCommandHandler.hpp" #include "GatordMockService.hpp" #include "PeriodicCounterCaptureCommandHandler.hpp" #include "PeriodicCounterSelectionResponseHandler.hpp" @@ -34,9 +35,13 @@ int main(int argc, char* argv[]) armnn::gatordmock::PeriodicCounterCaptureCommandHandler counterCaptureCommandHandler( 1, 0, packetVersionResolver.ResolvePacketVersion(1, 0).GetEncodedValue()); + armnn::gatordmock::DirectoryCaptureCommandHandler directoryCaptureCommandHandler( + 0, 2, packetVersionResolver.ResolvePacketVersion(0, 2).GetEncodedValue()); + // Register different derived functors registry.RegisterFunctor(&periodicCounterSelectionResponseHandler); registry.RegisterFunctor(&counterCaptureCommandHandler); + registry.RegisterFunctor(&directoryCaptureCommandHandler); armnn::gatordmock::GatordMockService mockService(registry, cmdLine.IsEchoEnabled()); diff --git a/tests/profiling/gatordmock/GatordMockService.cpp b/tests/profiling/gatordmock/GatordMockService.cpp index 194b097af1..bf326a64c3 100644 --- a/tests/profiling/gatordmock/GatordMockService.cpp +++ b/tests/profiling/gatordmock/GatordMockService.cpp @@ -136,6 +136,16 @@ void GatordMockService::SendConnectionAck() SendPacket(0, 1, nullptr, 0); } +void GatordMockService::SendRequestCounterDir() +{ + if (m_EchoPackets) + { + std::cout << "Sending connection acknowledgement." << std::endl; + } + // The connection ack packet is an empty data packet with packetId == 1. + SendPacket(0, 3, nullptr, 0); +} + bool GatordMockService::LaunchReceivingThread() { if (m_EchoPackets) diff --git a/tests/profiling/gatordmock/GatordMockService.hpp b/tests/profiling/gatordmock/GatordMockService.hpp index 10bf88406e..deafcfdc92 100644 --- a/tests/profiling/gatordmock/GatordMockService.hpp +++ b/tests/profiling/gatordmock/GatordMockService.hpp @@ -69,6 +69,9 @@ public: /// Send a connection acknowledged packet back to the client. void SendConnectionAck(); + /// Send a request counter directory packet back to the client. + void SendRequestCounterDir(); + /// Start the thread that will receive all packets and print them nicely to stdout. bool LaunchReceivingThread(); diff --git a/tests/profiling/gatordmock/MockUtils.cpp b/tests/profiling/gatordmock/MockUtils.cpp new file mode 100644 index 0000000000..bdbffc9253 --- /dev/null +++ b/tests/profiling/gatordmock/MockUtils.cpp @@ -0,0 +1,57 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "MockUtils.hpp" + +namespace armnn +{ + +namespace gatordmock +{ + +std::string CentreAlignFormatting(const std::string& stringToPass, const int spacingWidth) +{ + std::stringstream outputStream, centrePadding; + int padding = spacingWidth - static_cast(stringToPass.size()); + + for (int i = 0; i < padding / 2; ++i) + { + centrePadding << " "; + } + + outputStream << centrePadding.str() << stringToPass << centrePadding.str(); + + if (padding > 0 && padding %2 != 0) + { + outputStream << " "; + } + + return outputStream.str(); +} + +std::string GetStringNameFromBuffer(const unsigned char* const data, uint32_t offset) +{ + std::string deviceName; + u_char nextChar = profiling::ReadUint8(data, offset); + + while (IsValidChar(nextChar)) + { + deviceName += static_cast(nextChar); + offset ++; + nextChar = profiling::ReadUint8(data, offset); + } + + return deviceName; +} + +bool IsValidChar(unsigned char c) +{ + // Check that the given character has ASCII 7-bit encoding, alpha-numeric, whitespace, and underscore only + return c < 128 && (std::isalnum(c) || c == '_' || c == ' '); +} + +} // gatordmock + +} // armnn diff --git a/tests/profiling/gatordmock/MockUtils.hpp b/tests/profiling/gatordmock/MockUtils.hpp new file mode 100644 index 0000000000..78bd867b0f --- /dev/null +++ b/tests/profiling/gatordmock/MockUtils.hpp @@ -0,0 +1,25 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include +#include + +namespace armnn +{ + +namespace gatordmock +{ + +std::string CentreAlignFormatting(const std::string& stringToPass, const int spacingWidth); + +std::string GetStringNameFromBuffer(const unsigned char *const data, uint32_t offset); + +bool IsValidChar(unsigned char c); + +} // gatordmock + +} // armnn diff --git a/tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.cpp b/tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.cpp index 90d52a7848..893351463f 100644 --- a/tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.cpp +++ b/tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: MIT // +#include "MockUtils.hpp" #include "PeriodicCounterCaptureCommandHandler.hpp" #include @@ -19,26 +20,6 @@ namespace gatordmock using boost::numeric_cast; -std::string CentreAlignFormatting(const std::string stringToPass, const int spacingWidth) -{ - std::stringstream outputStream, centrePadding; - int padding = spacingWidth - static_cast(stringToPass.size()); - - for (int i = 0; i < padding / 2; ++i) - { - centrePadding << " "; - } - - outputStream << centrePadding.str() << stringToPass << centrePadding.str(); - - if (padding > 0 && padding % 2 != 0) - { - outputStream << " "; - } - - return outputStream.str(); -} - void PeriodicCounterCaptureCommandHandler::ParseData(const armnn::profiling::Packet& packet) { std::vector counterIds; diff --git a/tests/profiling/gatordmock/tests/GatordMockTests.cpp b/tests/profiling/gatordmock/tests/GatordMockTests.cpp index f5e60f83b2..eb827bec69 100644 --- a/tests/profiling/gatordmock/tests/GatordMockTests.cpp +++ b/tests/profiling/gatordmock/tests/GatordMockTests.cpp @@ -5,6 +5,7 @@ #include "../GatordMockService.hpp" #include "../PeriodicCounterCaptureCommandHandler.hpp" +#include "../DirectoryCaptureCommandHandler.hpp" #include #include @@ -21,28 +22,11 @@ using namespace armnn; using namespace std::this_thread; // sleep_for, sleep_until using namespace std::chrono_literals; -// Required so build succeeds when local variable used only in assert -#define _unused(x) ((void)(x)) - -uint32_t ConstructHeader(uint32_t packetFamily, uint32_t packetClass, uint32_t packetType) -{ - return ((packetFamily & 0x3F) << 26) | ((packetClass & 0x3FF) << 19) | ((packetType & 0x3FFF) << 16); -} - -uint32_t ConstructHeader(uint32_t packetFamily, uint32_t packetId) -{ - return ((packetFamily & 0x3F) << 26) | ((packetId & 0x3FF) << 16); -} - BOOST_AUTO_TEST_CASE(CounterCaptureHandlingTest) { using boost::numeric_cast; - // Initialise functors and register into the CommandHandlerRegistry - uint32_t headerWord1 = ConstructHeader(1, 0, 0); - - // Create the Command Handler Registry - profiling::CommandHandlerRegistry registry; + profiling::PacketVersionResolver packetVersionResolver; // Data with timestamp, counter idx & counter values std::vector> indexValuePairs; @@ -98,18 +82,19 @@ BOOST_AUTO_TEST_CASE(CounterCaptureHandlingTest) offset += sizeOfUint32; } + uint32_t headerWord1 = packetVersionResolver.ResolvePacketVersion(0, 4).GetEncodedValue(); // Create packet to send through to the command functor profiling::Packet packet1(headerWord1, dataLength, uniqueData1); profiling::Packet packet2(headerWord1, dataLength, uniqueData2); - uint32_t version = 1; - gatordmock::PeriodicCounterCaptureCommandHandler commandHandler(0, 4, version, true); + gatordmock::PeriodicCounterCaptureCommandHandler commandHandler + (0, 4, headerWord1, true); // Simulate two separate packets coming in to calculate period commandHandler(packet1); commandHandler(packet2); - BOOST_ASSERT(4500 < commandHandler.m_CurrentPeriodValue && 5500 > commandHandler.m_CurrentPeriodValue); + BOOST_ASSERT(commandHandler.m_CurrentPeriodValue == 5000); for (size_t i = 0; i < commandHandler.m_CounterCaptureValues.m_Uids.size(); ++i) { @@ -122,18 +107,26 @@ BOOST_AUTO_TEST_CASE(GatorDMockEndToEnd) // The purpose of this test is to setup both sides of the profiling service and get to the point of receiving // performance data. - // Initialise functors and register into the CommandHandlerRegistry - uint32_t version = 1; + //These variables are used to wait for the profiling service + u_int32_t timeout = 2000; + u_int32_t sleepTime = 50; + u_int32_t timeSlept = 0; + + profiling::PacketVersionResolver packetVersionResolver; // Create the Command Handler Registry profiling::CommandHandlerRegistry registry; // Update with derived functors - gatordmock::PeriodicCounterCaptureCommandHandler counterCaptureCommandHandler(0, 4, version, true); + gatordmock::PeriodicCounterCaptureCommandHandler counterCaptureCommandHandler + (0, 4, packetVersionResolver.ResolvePacketVersion(0, 4).GetEncodedValue(), true); + + gatordmock::DirectoryCaptureCommandHandler directoryCaptureCommandHandler + (0, 2, packetVersionResolver.ResolvePacketVersion(0, 2).GetEncodedValue(), true); // Register different derived functors registry.RegisterFunctor(&counterCaptureCommandHandler); - + registry.RegisterFunctor(&directoryCaptureCommandHandler); // Setup the mock service to bind to the UDS. std::string udsNamespace = "gatord_namespace"; gatordmock::GatordMockService mockService(registry, false); @@ -157,10 +150,18 @@ BOOST_AUTO_TEST_CASE(GatorDMockEndToEnd) { BOOST_FAIL("Failed to connect client"); } + // Give the profiling service sending thread time start executing and send the stream metadata. - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - // We should now be in WaitingForAck state. - BOOST_CHECK(profilingService.GetCurrentState() == profiling::ProfilingState::WaitingForAck); + while (profilingService.GetCurrentState() != profiling::ProfilingState::WaitingForAck) + { + if (timeSlept >= timeout) + { + BOOST_FAIL("Timeout: Profiling service did not switch to WaitingForAck state"); + } + std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime)); + timeSlept += sleepTime; + } + profilingService.Update(); // Read the stream metadata on the mock side. if (!mockService.WaitForStreamMetaData()) @@ -169,15 +170,115 @@ BOOST_AUTO_TEST_CASE(GatorDMockEndToEnd) } // Send Ack from GatorD mockService.SendConnectionAck(); - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - // At this point the service should be in active state. - BOOST_ASSERT(profilingService.GetCurrentState() == profiling::ProfilingState::Active); - // Future tests here will add counters to the ProfilingService, increment values and examine - // PeriodicCounterCapture data received. These are yet to be integrated. + timeSlept = 0; + while (profilingService.GetCurrentState() != profiling::ProfilingState::Active) + { + if (timeSlept >= timeout) + { + BOOST_FAIL("Timeout: Profiling service did not switch to Active state"); + } + std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime)); + timeSlept += sleepTime; + } + + mockService.LaunchReceivingThread(); + mockService.SendRequestCounterDir(); + + timeSlept = 0; + while (directoryCaptureCommandHandler.GetCounterDirectoryCount() == 0) + { + if (timeSlept >= timeout) + { + BOOST_FAIL("Timeout: MockGatord did not receive counter directory packet"); + } + std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime)); + timeSlept += sleepTime; + } + const profiling::ICounterDirectory& serviceCounterDirectory = profilingService.GetCounterDirectory(); + gatordmock::CounterDirectory mockCounterDirectory = directoryCaptureCommandHandler.GetCounterDirectory(); + + BOOST_ASSERT(serviceCounterDirectory.GetDeviceCount() == mockCounterDirectory.m_DeviceRecords.size()); + BOOST_ASSERT(serviceCounterDirectory.GetCounterSetCount() == mockCounterDirectory.m_CounterSets.size()); + BOOST_ASSERT(serviceCounterDirectory.GetCategoryCount() == mockCounterDirectory.m_Categories.size()); + + const profiling::Devices& serviceDevices = serviceCounterDirectory.GetDevices(); + + uint32_t deviceIndex = 0; + for (auto& device : serviceDevices) + { + BOOST_ASSERT(device.second->m_Name.size() == + mockCounterDirectory.m_DeviceRecords[deviceIndex].m_DeviceName.size()); + + BOOST_CHECK(device.second->m_Name == mockCounterDirectory.m_DeviceRecords[deviceIndex].m_DeviceName); + BOOST_CHECK(device.second->m_Uid == mockCounterDirectory.m_DeviceRecords[deviceIndex].m_DeviceUid); + BOOST_CHECK(device.second->m_Cores == mockCounterDirectory.m_DeviceRecords[deviceIndex].m_DeviceCores); + deviceIndex++; + } + + const profiling::CounterSets & serviceCounterSets = serviceCounterDirectory.GetCounterSets(); + uint32_t counterSetIndex = 0; + for (auto& counterSet : serviceCounterSets) + { + BOOST_ASSERT(counterSet.second->m_Name.size() == + mockCounterDirectory.m_CounterSets[counterSetIndex].m_CounterSetName.size()); + + BOOST_CHECK(counterSet.second->m_Name == mockCounterDirectory.m_CounterSets[counterSetIndex].m_CounterSetName); + BOOST_CHECK(counterSet.second->m_Uid == mockCounterDirectory.m_CounterSets[counterSetIndex].m_CounterSetUid); + BOOST_CHECK(counterSet.second->m_Count == + mockCounterDirectory.m_CounterSets[counterSetIndex].m_CounterSetCount); + counterSetIndex++; + } + + const profiling::Categories& serviceCategories = serviceCounterDirectory.GetCategories(); + const std::vector mockCategories = mockCounterDirectory.m_Categories; + + uint32_t categoryIndex = 0; + for (auto& category : serviceCategories) + { + BOOST_ASSERT(category->m_Name.size() == mockCategories[categoryIndex].m_CategoryName.size()); + + BOOST_CHECK(category->m_Name == mockCategories[categoryIndex].m_CategoryName); + BOOST_CHECK(category->m_CounterSetUid == mockCategories[categoryIndex].m_CounterSet); + BOOST_CHECK(category->m_DeviceUid == mockCategories[categoryIndex].m_DeviceUid); + + const std::vector events = mockCategories[categoryIndex].m_EventRecords; + uint32_t eventIndex = 0; + for (uint16_t counterUid : category->m_Counters) + { + const profiling::Counter* counter = serviceCounterDirectory.GetCounter(counterUid); + + BOOST_CHECK(counterUid == events[eventIndex].m_CounterUid); + + BOOST_ASSERT(counter->m_Name.size() == events[eventIndex].m_CounterName.size()); + BOOST_ASSERT(counter->m_Units.size() == events[eventIndex].m_CounterUnits.size()); + BOOST_ASSERT(counter->m_Description.size() == events[eventIndex].m_CounterDescription.size()); + + BOOST_CHECK(counter->m_Name == events[eventIndex].m_CounterName); + BOOST_CHECK(counter->m_Units == events[eventIndex].m_CounterUnits); + BOOST_CHECK(counter->m_Description == events[eventIndex].m_CounterDescription); + + BOOST_CHECK(counter->m_CounterSetUid == events[eventIndex].m_CounterSetUid); + BOOST_CHECK(counter->m_DeviceUid == events[eventIndex].m_DeviceUid); + BOOST_CHECK(counter->m_Uid == events[eventIndex].m_CounterUid); + + BOOST_CHECK(counter->m_Multiplier == events[eventIndex].m_CounterMultiplier); + BOOST_CHECK(counter->m_MaxCounterUid == events[eventIndex].m_MaxCounterUid); + BOOST_CHECK(counter->m_Interpolation == events[eventIndex].m_CounterInterpolation); + BOOST_CHECK(counter->m_Class == events[eventIndex].m_CounterClass); + + eventIndex++; + } + categoryIndex++; + } + + mockService.WaitForReceivingThread(); options.m_EnableProfiling = false; profilingService.ResetExternalProfilingOptions(options, true); + + // Future tests here will add counters to the ProfilingService, increment values and examine + // PeriodicCounterCapture data received. These are yet to be integrated. } BOOST_AUTO_TEST_SUITE_END() -- cgit v1.2.1