From c454ac95267beecd67b1ec3ef8851d5089f99c4c Mon Sep 17 00:00:00 2001 From: Jim Flynn Date: Wed, 16 Mar 2022 18:43:18 +0000 Subject: IVGCVSW-6851 Move DirectoryCaptureCommandHandler to the profiling server library Change-Id: Ib14fdcca15f40fedc0f992b0fd882458dc58c9ba Signed-off-by: Jim Flynn --- profiling/common/include/CommonProfilingUtils.hpp | 10 +- profiling/common/include/Counter.hpp | 62 +++ profiling/common/include/CounterDirectory.hpp | 103 ++++ profiling/common/include/ICounterDirectory.hpp | 118 +++++ profiling/common/include/ICounterRegistry.hpp | 49 ++ profiling/common/src/CMakeLists.txt | 1 + profiling/common/src/CommonProfilingUtils.cpp | 241 ++++++++- profiling/common/src/CounterDirectory.cpp | 546 +++++++++++++++++++++ .../DirectoryCaptureCommandHandler.hpp | 90 ++++ .../server/src/timelineDecoder/CMakeLists.txt | 1 + .../DirectoryCaptureCommandHandler.cpp | 341 +++++++++++++ 11 files changed, 1560 insertions(+), 2 deletions(-) create mode 100644 profiling/common/include/Counter.hpp create mode 100644 profiling/common/include/CounterDirectory.hpp create mode 100644 profiling/common/include/ICounterDirectory.hpp create mode 100644 profiling/common/include/ICounterRegistry.hpp create mode 100644 profiling/common/src/CounterDirectory.cpp create mode 100644 profiling/server/include/timelineDecoder/DirectoryCaptureCommandHandler.hpp create mode 100644 profiling/server/src/timelineDecoder/DirectoryCaptureCommandHandler.cpp (limited to 'profiling') diff --git a/profiling/common/include/CommonProfilingUtils.hpp b/profiling/common/include/CommonProfilingUtils.hpp index 68fe6bb8ca..c07beafedf 100644 --- a/profiling/common/include/CommonProfilingUtils.hpp +++ b/profiling/common/include/CommonProfilingUtils.hpp @@ -4,6 +4,8 @@ // #pragma once +#include "ICounterDirectory.hpp" + #include #include @@ -34,5 +36,11 @@ void WriteUint8(unsigned char* buffer, unsigned int offset, uint8_t value); std::string CentreAlignFormatting(const std::string& stringToPass, const int spacingWidth); +void PrintCounterDirectory(ICounterDirectory& counterDirectory); + +uint16_t GetNextUid(bool peekOnly = false); + + std::vector GetNextCounterUids(uint16_t firstUid, uint16_t cores); + } // namespace pipe -} // namespace arm \ No newline at end of file +} // namespace arm diff --git a/profiling/common/include/Counter.hpp b/profiling/common/include/Counter.hpp new file mode 100644 index 0000000000..ff96d257e7 --- /dev/null +++ b/profiling/common/include/Counter.hpp @@ -0,0 +1,62 @@ +// +// Copyright © 2022 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include + +namespace arm +{ + +namespace pipe +{ + +class Counter final +{ +public: + // Constructors + Counter(const std::string& backendId, + uint16_t counterUid, + uint16_t maxCounterUid, + uint16_t counterClass, + uint16_t interpolation, + double multiplier, + const std::string& name, + const std::string& description, + const std::string& units, + uint16_t deviceUid, + uint16_t counterSetUid) + : m_BackendId(backendId) + , m_Uid(counterUid) + , m_MaxCounterUid(maxCounterUid) + , m_Class(counterClass) + , m_Interpolation(interpolation) + , m_Multiplier(multiplier) + , m_Name(name) + , m_Description(description) + , m_Units(units) + , m_DeviceUid(deviceUid) + , m_CounterSetUid(counterSetUid) + {} + + // Fields + std::string m_BackendId; + uint16_t m_Uid; + uint16_t m_MaxCounterUid; + uint16_t m_Class; + uint16_t m_Interpolation; + double m_Multiplier; + std::string m_Name; + std::string m_Description; + std::string m_Units; // Optional, leave empty if the counter does not need units + + // Connections + uint16_t m_DeviceUid; // Optional, set to zero if the counter is not associated with a device + uint16_t m_CounterSetUid; // Optional, set to zero if the counter is not associated with a counter set +}; + +} // namespace pipe + +} // namespace arm diff --git a/profiling/common/include/CounterDirectory.hpp b/profiling/common/include/CounterDirectory.hpp new file mode 100644 index 0000000000..ecc349edff --- /dev/null +++ b/profiling/common/include/CounterDirectory.hpp @@ -0,0 +1,103 @@ +// +// Copyright © 2017 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "ICounterDirectory.hpp" +#include "ICounterRegistry.hpp" + +#include +#include +#include + +#include + +namespace arm +{ + +namespace pipe +{ + +class CounterDirectory final : public ICounterDirectory, public ICounterRegistry +{ +public: + CounterDirectory() = default; + ~CounterDirectory() = default; + + // Register profiling objects + const Category* RegisterCategory (const std::string& categoryName) override; + const Device* RegisterDevice (const std::string& deviceName, + uint16_t cores = 0, + const arm::pipe::Optional& parentCategoryName + = arm::pipe::EmptyOptional()) override; + const CounterSet* RegisterCounterSet(const std::string& counterSetName, + uint16_t count = 0, + const arm::pipe::Optional& parentCategoryName + = arm::pipe::EmptyOptional()) override; + const Counter* RegisterCounter(const std::string& 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 arm::pipe::Optional& units = arm::pipe::EmptyOptional(), + const arm::pipe::Optional& numberOfCores = arm::pipe::EmptyOptional(), + const arm::pipe::Optional& deviceUid = arm::pipe::EmptyOptional(), + const arm::pipe::Optional& counterSetUid = arm::pipe::EmptyOptional()) override; + + // Getters for counts + uint16_t GetCategoryCount() const override { return arm::pipe::numeric_cast(m_Categories.size()); } + uint16_t GetDeviceCount() const override { return arm::pipe::numeric_cast(m_Devices.size()); } + uint16_t GetCounterSetCount() const override { return arm::pipe::numeric_cast(m_CounterSets.size()); } + uint16_t GetCounterCount() const override { return arm::pipe::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; + const Device* GetDevice(uint16_t uid) const override; + const CounterSet* GetCounterSet(uint16_t uid) const override; + const Counter* GetCounter(uint16_t uid) const override; + + // Queries for profiling objects + bool IsCategoryRegistered(const std::string& categoryName) const; + bool IsDeviceRegistered(uint16_t deviceUid) const; + bool IsDeviceRegistered(const std::string& deviceName) const; + bool IsCounterSetRegistered(uint16_t counterSetUid) const; + bool IsCounterSetRegistered(const std::string& counterSetName) const; + bool IsCounterRegistered(uint16_t counterUid) const; + bool IsCounterRegistered(const std::string& counterName) const; + + // Clears all the counter directory contents + void Clear(); + +private: + // The profiling collections owned by the counter directory + Categories m_Categories; + Devices m_Devices; + CounterSets m_CounterSets; + Counters m_Counters; + + // Helper functions + CategoriesIt FindCategory(const std::string& categoryName) const; + DevicesIt FindDevice(uint16_t deviceUid) const; + DevicesIt FindDevice(const std::string& deviceName) const; + CounterSetsIt FindCounterSet(uint16_t counterSetUid) const; + CounterSetsIt FindCounterSet(const std::string& counterSetName) const; + CountersIt FindCounter(uint16_t counterUid) const; + CountersIt FindCounter(const std::string& counterName) const; + uint16_t GetNumberOfCores(const arm::pipe::Optional& numberOfCores, + uint16_t deviceUid); +}; + +} // namespace pipe + +} // namespace arm diff --git a/profiling/common/include/ICounterDirectory.hpp b/profiling/common/include/ICounterDirectory.hpp new file mode 100644 index 0000000000..d024516ab8 --- /dev/null +++ b/profiling/common/include/ICounterDirectory.hpp @@ -0,0 +1,118 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "Counter.hpp" + +#include +#include +#include +#include +#include + +namespace arm +{ + +namespace pipe +{ + +// Forward declarations +class Category; +class Device; +class CounterSet; + +// Profiling objects smart pointer types +using CategoryPtr = std::unique_ptr; +using DevicePtr = std::unique_ptr; +using CounterSetPtr = std::unique_ptr; +using CounterPtr = std::shared_ptr; + +// Profiling objects collection types +using Categories = std::unordered_set; +using Devices = std::unordered_map; +using CounterSets = std::unordered_map; +using Counters = std::unordered_map; + +// Profiling objects collection iterator types +using CategoriesIt = Categories::const_iterator; +using DevicesIt = Devices::const_iterator; +using CounterSetsIt = CounterSets::const_iterator; +using CountersIt = Counters::const_iterator; + +class Category final +{ +public: + // Constructors + Category(const std::string& name) + : m_Name(name) + {} + + // Fields + std::string m_Name; + + // Connections + std::vector m_Counters; // The UIDs of the counters associated with this category +}; + +class Device final +{ +public: + // Constructors + Device(uint16_t deviceUid, const std::string& name, uint16_t cores) + : m_Uid(deviceUid) + , m_Name(name) + , m_Cores(cores) + {} + + // Fields + uint16_t m_Uid; + std::string m_Name; + uint16_t m_Cores; +}; + +class CounterSet final +{ +public: + // Constructors + CounterSet(uint16_t counterSetUid, const std::string& name, uint16_t count) + : m_Uid(counterSetUid) + , m_Name(name) + , m_Count(count) + {} + + // Fields + uint16_t m_Uid; + std::string m_Name; + uint16_t m_Count; +}; + +class ICounterDirectory +{ +public: + virtual ~ICounterDirectory() {} + + // Getters for counts + virtual uint16_t GetCategoryCount() const = 0; + virtual uint16_t GetDeviceCount() const = 0; + virtual uint16_t GetCounterSetCount() const = 0; + virtual uint16_t GetCounterCount() const = 0; + + // Getters for collections + virtual const Categories& GetCategories() const = 0; + virtual const Devices& GetDevices() const = 0; + virtual const CounterSets& GetCounterSets() const = 0; + virtual const Counters& GetCounters() const = 0; + + // Getters for profiling objects + virtual const Category* GetCategory(const std::string& name) const = 0; + virtual const Device* GetDevice(uint16_t uid) const = 0; + virtual const CounterSet* GetCounterSet(uint16_t uid) const = 0; + virtual const Counter* GetCounter(uint16_t uid) const = 0; +}; + +} // namespace pipe + +} // namespace arm diff --git a/profiling/common/include/ICounterRegistry.hpp b/profiling/common/include/ICounterRegistry.hpp new file mode 100644 index 0000000000..5c34e7a3de --- /dev/null +++ b/profiling/common/include/ICounterRegistry.hpp @@ -0,0 +1,49 @@ +// +// Copyright © 2020 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include + +namespace arm +{ + +namespace pipe +{ + +class ICounterRegistry +{ +public: + virtual ~ICounterRegistry() {} + + // Register profiling objects + virtual const Category* RegisterCategory (const std::string& categoryName) = 0; + + virtual const Device* RegisterDevice (const std::string& deviceName, + uint16_t cores, + const arm::pipe::Optional& parentCategoryName) = 0; + + virtual const CounterSet* RegisterCounterSet(const std::string& counterSetName, + uint16_t count, + const arm::pipe::Optional& parentCategoryName) = 0; + + virtual const Counter* RegisterCounter(const std::string& 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 arm::pipe::Optional& units = arm::pipe::EmptyOptional(), + const arm::pipe::Optional& numberOfCores = arm::pipe::EmptyOptional(), + const arm::pipe::Optional& deviceUid = arm::pipe::EmptyOptional(), + const arm::pipe::Optional& counterSetUid = arm::pipe::EmptyOptional()) = 0; + +}; + +} // namespace pipe + +} // namespace arm diff --git a/profiling/common/src/CMakeLists.txt b/profiling/common/src/CMakeLists.txt index e02c785581..264902c0dc 100644 --- a/profiling/common/src/CMakeLists.txt +++ b/profiling/common/src/CMakeLists.txt @@ -10,6 +10,7 @@ if(BUILD_TIMELINE_DECODER) CommandHandlerKey.cpp CommandHandlerRegistry.cpp CommonProfilingUtils.cpp + CounterDirectory.cpp LabelsAndEventClasses.cpp Logging.cpp NetworkSockets.cpp diff --git a/profiling/common/src/CommonProfilingUtils.cpp b/profiling/common/src/CommonProfilingUtils.cpp index fe98e0aaa9..01bc461dd2 100644 --- a/profiling/common/src/CommonProfilingUtils.cpp +++ b/profiling/common/src/CommonProfilingUtils.cpp @@ -5,7 +5,10 @@ #include #include +#include +#include +#include #include namespace arm @@ -140,6 +143,242 @@ std::string CentreAlignFormatting(const std::string& stringToPass, const int spa return outputStream.str(); } +void PrintDeviceDetails(const std::pair>& devicePair) +{ + std::string body; + + body.append(CentreAlignFormatting(devicePair.second->m_Name, 20)); + body.append(" | "); + body.append(CentreAlignFormatting(std::to_string(devicePair.first), 13)); + body.append(" | "); + body.append(CentreAlignFormatting(std::to_string(devicePair.second->m_Cores), 10)); + body.append("\n"); + + std::cout << std::string(body.size(), '-') << "\n"; + std::cout<< body; +} + +void PrintCounterSetDetails(const std::pair>& counterSetPair) +{ + std::string body; + + body.append(CentreAlignFormatting(counterSetPair.second->m_Name, 20)); + body.append(" | "); + body.append(CentreAlignFormatting(std::to_string(counterSetPair.first), 13)); + body.append(" | "); + body.append(CentreAlignFormatting(std::to_string(counterSetPair.second->m_Count), 10)); + body.append("\n"); + + std::cout << std::string(body.size(), '-') << "\n"; + + std::cout<< body; +} + +void PrintCounterDetails(std::shared_ptr& counter) +{ + std::string body; + + body.append(CentreAlignFormatting(counter->m_Name, 20)); + body.append(" | "); + body.append(CentreAlignFormatting(counter->m_Description, 50)); + body.append(" | "); + body.append(CentreAlignFormatting(counter->m_Units, 14)); + body.append(" | "); + body.append(CentreAlignFormatting(std::to_string(counter->m_Uid), 6)); + body.append(" | "); + body.append(CentreAlignFormatting(std::to_string(counter->m_MaxCounterUid), 10)); + body.append(" | "); + body.append(CentreAlignFormatting(std::to_string(counter->m_Class), 8)); + body.append(" | "); + body.append(CentreAlignFormatting(std::to_string(counter->m_Interpolation), 14)); + body.append(" | "); + body.append(CentreAlignFormatting(std::to_string(counter->m_Multiplier), 20)); + body.append(" | "); + body.append(CentreAlignFormatting(std::to_string(counter->m_CounterSetUid), 16)); + body.append(" | "); + body.append(CentreAlignFormatting(std::to_string(counter->m_DeviceUid), 14)); + + body.append("\n"); + + std::cout << std::string(body.size(), '-') << "\n"; + + std::cout << body; +} + +void PrintCategoryDetails(const std::unique_ptr& category, + std::unordered_map> counterMap) +{ + std::string categoryBody; + std::string categoryHeader; + + categoryHeader.append(CentreAlignFormatting("Name", 20)); + categoryHeader.append(" | "); + categoryHeader.append(CentreAlignFormatting("Event Count", 14)); + categoryHeader.append("\n"); + + categoryBody.append(CentreAlignFormatting(category->m_Name, 20)); + categoryBody.append(" | "); + categoryBody.append(CentreAlignFormatting(std::to_string(category->m_Counters.size()), 14)); + + std::cout << "\n" << "\n"; + std::cout << CentreAlignFormatting("CATEGORY", static_cast(categoryHeader.size())); + std::cout << "\n"; + std::cout << std::string(categoryHeader.size(), '=') << "\n"; + + std::cout << categoryHeader; + + std::cout << std::string(categoryBody.size(), '-') << "\n"; + + std::cout << categoryBody; + + std::string counterHeader; + + counterHeader.append(CentreAlignFormatting("Counter Name", 20)); + counterHeader.append(" | "); + counterHeader.append(CentreAlignFormatting("Description", 50)); + counterHeader.append(" | "); + counterHeader.append(CentreAlignFormatting("Units", 14)); + counterHeader.append(" | "); + counterHeader.append(CentreAlignFormatting("UID", 6)); + counterHeader.append(" | "); + counterHeader.append(CentreAlignFormatting("Max UID", 10)); + counterHeader.append(" | "); + counterHeader.append(CentreAlignFormatting("Class", 8)); + counterHeader.append(" | "); + counterHeader.append(CentreAlignFormatting("Interpolation", 14)); + counterHeader.append(" | "); + counterHeader.append(CentreAlignFormatting("Multiplier", 20)); + counterHeader.append(" | "); + counterHeader.append(CentreAlignFormatting("Counter set UID", 16)); + counterHeader.append(" | "); + counterHeader.append(CentreAlignFormatting("Device UID", 14)); + counterHeader.append("\n"); + + std::cout << "\n" << "\n"; + std::cout << CentreAlignFormatting("EVENTS IN CATEGORY: " + category->m_Name, + static_cast(counterHeader.size())); + std::cout << "\n"; + std::cout << std::string(counterHeader.size(), '=') << "\n"; + std::cout << counterHeader; + for (auto& it: category->m_Counters) { + auto search = counterMap.find(it); + if(search != counterMap.end()) { + PrintCounterDetails(search->second); + } + } +} + +void PrintCounterDirectory(ICounterDirectory& counterDirectory) +{ + std::string devicesHeader; + + devicesHeader.append(CentreAlignFormatting("Device name", 20)); + devicesHeader.append(" | "); + devicesHeader.append(CentreAlignFormatting("UID", 13)); + devicesHeader.append(" | "); + devicesHeader.append(CentreAlignFormatting("Cores", 10)); + devicesHeader.append("\n"); + + std::cout << "\n" << "\n"; + std::cout << CentreAlignFormatting("DEVICES", static_cast(devicesHeader.size())); + std::cout << "\n"; + std::cout << std::string(devicesHeader.size(), '=') << "\n"; + std::cout << devicesHeader; + for (auto& it: counterDirectory.GetDevices()) { + PrintDeviceDetails(it); + } + + std::string counterSetHeader; + + counterSetHeader.append(CentreAlignFormatting("Counter set name", 20)); + counterSetHeader.append(" | "); + counterSetHeader.append(CentreAlignFormatting("UID", 13)); + counterSetHeader.append(" | "); + counterSetHeader.append(CentreAlignFormatting("Count", 10)); + counterSetHeader.append("\n"); + + std::cout << "\n" << "\n"; + std::cout << CentreAlignFormatting("COUNTER SETS", static_cast(counterSetHeader.size())); + std::cout << "\n"; + std::cout << std::string(counterSetHeader.size(), '=') << "\n"; + + std::cout << counterSetHeader; + + for (auto& it: counterDirectory.GetCounterSets()) { + PrintCounterSetDetails(it); + } + + auto counters = counterDirectory.GetCounters(); + for (auto& it: counterDirectory.GetCategories()) { + PrintCategoryDetails(it, counters); + } + std::cout << "\n"; +} + +namespace +{ + +void ThrowIfCantGenerateNextUid(uint16_t uid, uint16_t cores = 0) +{ + // Check that it is possible to generate the next UID without causing an overflow + switch (cores) + { + case 0: + case 1: + // Number of cores not specified or set to 1 (a value of zero indicates the device is not capable of + // running multiple parallel workloads and will not provide multiple streams of data for each event) + if (uid == std::numeric_limits::max()) + { + throw arm::pipe::ProfilingException("Generating the next UID for profiling would result in an overflow"); + } + break; + default: // cores > 1 + // Multiple cores available, as max_counter_uid has to be set to: counter_uid + cores - 1, the maximum + // allowed value for a counter UID is consequently: uint16_t_max - cores + 1 + if (uid >= std::numeric_limits::max() - cores + 1) + { + throw arm::pipe::ProfilingException("Generating the next UID for profiling would result in an overflow"); + } + break; + } +} + +} // Anonymous namespace + +uint16_t GetNextUid(bool peekOnly) +{ + // The UID used for profiling objects and events. The first valid UID is 1, as 0 is a reserved value + static uint16_t uid = 1; + + // Check that it is possible to generate the next UID without causing an overflow (throws in case of error) + ThrowIfCantGenerateNextUid(uid); + + if (peekOnly) + { + // Peek only + return uid; + } + else + { + // Get the next UID + return uid++; + } +} + +std::vector GetNextCounterUids(uint16_t firstUid, uint16_t cores) +{ + // Check that it is possible to generate the next counter UID without causing an overflow (throws in case of error) + ThrowIfCantGenerateNextUid(firstUid, cores); + + // Get the next counter UIDs + size_t counterUidsSize = cores == 0 ? 1 : cores; + std::vector counterUids(counterUidsSize, 0); + for (size_t i = 0; i < counterUidsSize; i++) + { + counterUids[i] = firstUid++; + } + return counterUids; +} } // namespace pipe -} // namespace arm \ No newline at end of file +} // namespace arm diff --git a/profiling/common/src/CounterDirectory.cpp b/profiling/common/src/CounterDirectory.cpp new file mode 100644 index 0000000000..f338858498 --- /dev/null +++ b/profiling/common/src/CounterDirectory.cpp @@ -0,0 +1,546 @@ +// +// Copyright © 2017 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include +#include + +#include +#include + +#include + +namespace arm +{ + +namespace pipe +{ + +const Category* CounterDirectory::RegisterCategory(const std::string& categoryName) +{ + // Check that the given category name is valid + if (categoryName.empty() || + !arm::pipe::IsValidSwTraceString(categoryName)) + { + throw arm::pipe::InvalidArgumentException("Trying to register a category with an invalid name"); + } + + // Check that the given category is not already registered + if (IsCategoryRegistered(categoryName)) + { + throw arm::pipe::InvalidArgumentException(fmt::format( + "Trying to register a category already registered (\"{}\")", + categoryName)); + } + + // Create the category + CategoryPtr category = std::make_unique(categoryName); + ARM_PIPE_ASSERT(category); + + // Get the raw category pointer + const Category* categoryPtr = category.get(); + ARM_PIPE_ASSERT(categoryPtr); + + // Register the category + m_Categories.insert(std::move(category)); + + return categoryPtr; +} + +const Device* CounterDirectory::RegisterDevice(const std::string& deviceName, + uint16_t cores, + const arm::pipe::Optional& parentCategoryName) +{ + // Check that the given device name is valid + if (deviceName.empty() || + !arm::pipe::IsValidSwTraceString(deviceName)) + { + throw arm::pipe::InvalidArgumentException("Trying to register a device with an invalid name"); + } + + // Check that a device with the given name is not already registered + if (IsDeviceRegistered(deviceName)) + { + throw arm::pipe::InvalidArgumentException(fmt::format( + "Trying to register a device already registered (\"{}\")", + deviceName)); + } + + // Check that a category with the given (optional) parent category name is already registered + if (parentCategoryName.has_value()) + { + // Get the (optional) parent category name + const std::string& parentCategoryNameValue = parentCategoryName.value(); + if (parentCategoryNameValue.empty()) + { + throw arm::pipe::InvalidArgumentException( + fmt::format("Trying to connect a device (name: \"{}\") to an invalid " + "parent category (name: \"{}\")", + deviceName, + parentCategoryNameValue)); + } + + // Check that the given parent category is already registered + auto categoryIt = FindCategory(parentCategoryNameValue); + if (categoryIt == m_Categories.end()) + { + throw arm::pipe::InvalidArgumentException( + fmt::format("Trying to connect a device (name: \"{}\") to a parent category that " + "is not registered (name: \"{}\")", + deviceName, + parentCategoryNameValue)); + } + } + + // Get the device UID + uint16_t deviceUid = GetNextUid(); + + // Create the device + DevicePtr device = std::make_unique(deviceUid, deviceName, cores); + ARM_PIPE_ASSERT(device); + + // Get the raw device pointer + const Device* devicePtr = device.get(); + ARM_PIPE_ASSERT(devicePtr); + + // Register the device + m_Devices.insert(std::make_pair(deviceUid, std::move(device))); + + return devicePtr; +} + +const CounterSet* CounterDirectory::RegisterCounterSet(const std::string& counterSetName, + uint16_t count, + const arm::pipe::Optional& parentCategoryName) +{ + // Check that the given counter set name is valid + if (counterSetName.empty() || + !arm::pipe::IsValidSwTraceString(counterSetName)) + { + throw arm::pipe::InvalidArgumentException("Trying to register a counter set with an invalid name"); + } + + // Check that a counter set with the given name is not already registered + if (IsCounterSetRegistered(counterSetName)) + { + throw arm::pipe::InvalidArgumentException( + fmt::format("Trying to register a counter set already registered (\"{}\")", counterSetName)); + } + + // Peek the next UID, do not get an actual valid UID just now as we don't want to waste a good UID in case + // the registration fails. We'll get a proper one once we're sure that the counter set can be registered + uint16_t counterSetUidPeek = GetNextUid(true); + + // Check that a category with the given (optional) parent category name is already registered + if (parentCategoryName.has_value()) + { + // Get the (optional) parent category name + const std::string& parentCategoryNameValue = parentCategoryName.value(); + if (parentCategoryNameValue.empty()) + { + throw arm::pipe::InvalidArgumentException( + fmt::format("Trying to connect a counter set (UID: {}) to an invalid " + "parent category (name: \"{}\")", + counterSetUidPeek, + parentCategoryNameValue)); + } + + // Check that the given parent category is already registered + auto it = FindCategory(parentCategoryNameValue); + if (it == m_Categories.end()) + { + throw arm::pipe::InvalidArgumentException( + fmt::format("Trying to connect a counter set (UID: {}) to a parent category " + "that is not registered (name: \"{}\")", + counterSetUidPeek, + parentCategoryNameValue)); + } + } + + // Get the counter set UID + uint16_t counterSetUid = GetNextUid(); + ARM_PIPE_ASSERT(counterSetUid == counterSetUidPeek); + + // Create the counter set + CounterSetPtr counterSet = std::make_unique(counterSetUid, counterSetName, count); + ARM_PIPE_ASSERT(counterSet); + + // Get the raw counter set pointer + const CounterSet* counterSetPtr = counterSet.get(); + ARM_PIPE_ASSERT(counterSetPtr); + + // Register the counter set + m_CounterSets.insert(std::make_pair(counterSetUid, std::move(counterSet))); + + return counterSetPtr; +} + +const Counter* CounterDirectory::RegisterCounter(const std::string& applicationName, + 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 arm::pipe::Optional& units, + const arm::pipe::Optional& numberOfCores, + const arm::pipe::Optional& deviceUid, + const arm::pipe::Optional& counterSetUid) +{ + // Check that the given parent category name is valid + if (parentCategoryName.empty() || + !arm::pipe::IsValidSwTraceString(parentCategoryName)) + { + throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid parent category name"); + } + + // Check that the given class is valid + if (counterClass != 0 && counterClass != 1) + { + throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid class"); + } + + // Check that the given interpolation is valid + if (interpolation != 0 && interpolation != 1) + { + throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid interpolation"); + } + + // Check that the given multiplier is valid + if (multiplier == .0f) + { + throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid multiplier"); + } + + // Check that the given name is valid + if (name.empty() || + !arm::pipe::IsValidSwTraceString(name)) + { + throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid name"); + } + + // Check that the given description is valid + if (description.empty() || + !arm::pipe::IsValidSwTraceString(description)) + { + throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid description"); + } + + // Check that the given units are valid + if (units.has_value() + && !arm::pipe::IsValidSwTraceString(units.value())) + { + throw arm::pipe::InvalidArgumentException("Trying to register a counter with a invalid units"); + } + + // Check that the given parent category is registered + auto categoryIt = FindCategory(parentCategoryName); + if (categoryIt == m_Categories.end()) + { + throw arm::pipe::InvalidArgumentException( + fmt::format("Trying to connect a counter to a category that is not registered (name: \"{}\")", + parentCategoryName)); + } + + // Get the parent category + const CategoryPtr& parentCategory = *categoryIt; + ARM_PIPE_ASSERT(parentCategory); + + // Check that a counter with the given name is not already registered within the parent category + const std::vector& parentCategoryCounters = parentCategory->m_Counters; + for (uint16_t parentCategoryCounterUid : parentCategoryCounters) + { + const Counter* parentCategoryCounter = GetCounter(parentCategoryCounterUid); + ARM_PIPE_ASSERT(parentCategoryCounter); + + if (parentCategoryCounter->m_Name == name) + { + throw arm::pipe::InvalidArgumentException( + fmt::format("Trying to register a counter to category \"{}\" with a name that " + "is already used within that category (name: \"{}\")", + parentCategoryName, + name)); + } + } + + // Check that a counter set with the given (optional) UID is already registered + uint16_t counterSetUidValue = counterSetUid.has_value() ? counterSetUid.value() : 0; + if (counterSetUidValue > 0) + { + // Check that the (optional) counter set is already registered + if (!IsCounterSetRegistered(counterSetUidValue)) + { + throw InvalidArgumentException( + fmt::format("Trying to connect a counter to a counter set that is " + "not registered (counter set UID: {})", + counterSetUidValue)); + } + } + + // Get the number of cores (this call may throw) + uint16_t deviceUidValue = deviceUid.has_value() ? deviceUid.value() : 0; + uint16_t deviceCores = GetNumberOfCores(numberOfCores, deviceUidValue); + + // Get the counter UIDs and calculate the max counter UID + std::vector counterUids = GetNextCounterUids(uid, deviceCores); + ARM_PIPE_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(applicationName, + counterUids.front(), + maxCounterUid, + counterClass, + interpolation, + multiplier, + name, + description, + unitsValue, + deviceUidValue, + counterSetUidValue); + ARM_PIPE_ASSERT(counter); + + // Get the raw counter pointer + const Counter* counterPtr = counter.get(); + ARM_PIPE_ASSERT(counterPtr); + + // Process multiple counters if necessary + for (uint16_t counterUid : counterUids) + { + // Connect the counter to the parent category + parentCategory->m_Counters.push_back(counterUid); + + // Register the counter + m_Counters.insert(std::make_pair(counterUid, counter)); + } + + return counterPtr; +} + +const Category* CounterDirectory::GetCategory(const std::string& categoryName) const +{ + auto it = FindCategory(categoryName); + if (it == m_Categories.end()) + { + return nullptr; + } + + const Category* category = it->get(); + ARM_PIPE_ASSERT(category); + + return category; +} + +const Device* CounterDirectory::GetDevice(uint16_t deviceUid) const +{ + auto it = FindDevice(deviceUid); + if (it == m_Devices.end()) + { + return nullptr; + } + + const Device* device = it->second.get(); + ARM_PIPE_ASSERT(device); + ARM_PIPE_ASSERT(device->m_Uid == deviceUid); + + return device; +} + +const CounterSet* CounterDirectory::GetCounterSet(uint16_t counterSetUid) const +{ + auto it = FindCounterSet(counterSetUid); + if (it == m_CounterSets.end()) + { + return nullptr; + } + + const CounterSet* counterSet = it->second.get(); + ARM_PIPE_ASSERT(counterSet); + ARM_PIPE_ASSERT(counterSet->m_Uid == counterSetUid); + + return counterSet; +} + +const Counter* CounterDirectory::GetCounter(uint16_t counterUid) const +{ + auto it = FindCounter(counterUid); + if (it == m_Counters.end()) + { + return nullptr; + } + + const Counter* counter = it->second.get(); + ARM_PIPE_ASSERT(counter); + ARM_PIPE_ASSERT(counter->m_Uid <= counterUid); + ARM_PIPE_ASSERT(counter->m_Uid <= counter->m_MaxCounterUid); + + return counter; +} + +bool CounterDirectory::IsCategoryRegistered(const std::string& categoryName) const +{ + auto it = FindCategory(categoryName); + + return it != m_Categories.end(); +} + +bool CounterDirectory::IsDeviceRegistered(uint16_t deviceUid) const +{ + auto it = FindDevice(deviceUid); + + return it != m_Devices.end(); +} + +bool CounterDirectory::IsDeviceRegistered(const std::string& deviceName) const +{ + auto it = FindDevice(deviceName); + + return it != m_Devices.end(); +} + +bool CounterDirectory::IsCounterSetRegistered(uint16_t counterSetUid) const +{ + auto it = FindCounterSet(counterSetUid); + + return it != m_CounterSets.end(); +} + +bool CounterDirectory::IsCounterSetRegistered(const std::string& counterSetName) const +{ + auto it = FindCounterSet(counterSetName); + + return it != m_CounterSets.end(); +} + +bool CounterDirectory::IsCounterRegistered(uint16_t counterUid) const +{ + auto it = FindCounter(counterUid); + + return it != m_Counters.end(); +} + +bool CounterDirectory::IsCounterRegistered(const std::string& counterName) const +{ + auto it = FindCounter(counterName); + + return it != m_Counters.end(); +} + +void CounterDirectory::Clear() +{ + // Clear all the counter directory contents + m_Categories.clear(); + m_Devices.clear(); + m_CounterSets.clear(); + m_Counters.clear(); +} + +CategoriesIt CounterDirectory::FindCategory(const std::string& categoryName) const +{ + return std::find_if(m_Categories.begin(), m_Categories.end(), [&categoryName](const CategoryPtr& category) + { + ARM_PIPE_ASSERT(category); + + return category->m_Name == categoryName; + }); +} + +DevicesIt CounterDirectory::FindDevice(uint16_t deviceUid) const +{ + return m_Devices.find(deviceUid); +} + +DevicesIt CounterDirectory::FindDevice(const std::string& deviceName) const +{ + return std::find_if(m_Devices.begin(), m_Devices.end(), [&deviceName](const auto& pair) + { + ARM_PIPE_ASSERT(pair.second); + ARM_PIPE_ASSERT(pair.second->m_Uid == pair.first); + + return pair.second->m_Name == deviceName; + }); +} + +CounterSetsIt CounterDirectory::FindCounterSet(uint16_t counterSetUid) const +{ + return m_CounterSets.find(counterSetUid); +} + +CounterSetsIt CounterDirectory::FindCounterSet(const std::string& counterSetName) const +{ + return std::find_if(m_CounterSets.begin(), m_CounterSets.end(), [&counterSetName](const auto& pair) + { + ARM_PIPE_ASSERT(pair.second); + ARM_PIPE_ASSERT(pair.second->m_Uid == pair.first); + + return pair.second->m_Name == counterSetName; + }); +} + +CountersIt CounterDirectory::FindCounter(uint16_t counterUid) const +{ + return m_Counters.find(counterUid); +} + +CountersIt CounterDirectory::FindCounter(const std::string& counterName) const +{ + return std::find_if(m_Counters.begin(), m_Counters.end(), [&counterName](const auto& pair) + { + ARM_PIPE_ASSERT(pair.second); + ARM_PIPE_ASSERT(pair.first >= pair.second->m_Uid && pair.first <= pair.second->m_MaxCounterUid); + + return pair.second->m_Name == counterName; + }); +} + +uint16_t CounterDirectory::GetNumberOfCores(const arm::pipe::Optional& numberOfCores, + uint16_t deviceUid) +{ + // To get the number of cores, apply the following rules: + // + // 1. If numberOfCores is set then take it as the deviceCores value + // 2. If numberOfCores is not set then check to see if this counter is directly associated with a device, + // if so then that devices number of cores is taken as the deviceCores value + // 3. If none of the above holds then set deviceCores to zero + + // 1. If numberOfCores is set then take it as the deviceCores value + if (numberOfCores.has_value()) + { + // Get the number of cores + return numberOfCores.value(); + } + + // 2. If numberOfCores is not set then check to see if this counter is directly associated with a device, + // if so then that devices number of cores is taken as the deviceCores value + if (deviceUid > 0) + { + // Check that the (optional) device is already registered + auto deviceIt = FindDevice(deviceUid); + if (deviceIt == m_Devices.end()) + { + throw arm::pipe::InvalidArgumentException( + fmt::format("Trying to connect a counter to a device that is not registered (device UID {})", + deviceUid)); + } + + // Get the associated device + const DevicePtr& device = deviceIt->second; + ARM_PIPE_ASSERT(device); + + + // Get the number of cores of the associated device + return device->m_Cores; + } + + // 3. If none of the above holds then set deviceCores to zero + return 0; +} + +} // namespace pipe + +} // namespace arm diff --git a/profiling/server/include/timelineDecoder/DirectoryCaptureCommandHandler.hpp b/profiling/server/include/timelineDecoder/DirectoryCaptureCommandHandler.hpp new file mode 100644 index 0000000000..de10ec8892 --- /dev/null +++ b/profiling/server/include/timelineDecoder/DirectoryCaptureCommandHandler.hpp @@ -0,0 +1,90 @@ +// +// Copyright © 2019 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include +#include + +#include + +namespace arm +{ + +namespace pipe +{ + +struct CounterDirectoryEventRecord +{ + uint16_t m_CounterClass; + std::string m_CounterDescription; + uint16_t m_CounterInterpolation; + double m_CounterMultiplier; + std::string m_CounterName; + uint16_t m_CounterSetUid; + uint16_t m_CounterUid; + arm::pipe::Optional m_CounterUnits; + uint16_t m_DeviceUid; + uint16_t m_MaxCounterUid; +}; + +class DirectoryCaptureCommandHandler : public arm::pipe::CommandHandlerFunctor +{ + +public: + DirectoryCaptureCommandHandler(const std::string& applicationName, + uint32_t familyId, + uint32_t packetId, + uint32_t version, + bool quietOperation = true) + : CommandHandlerFunctor(familyId, packetId, version) + , m_ApplicationName(applicationName) + , m_QuietOperation(quietOperation) + , m_AlreadyParsed(false) + {} + + void operator()(const arm::pipe::Packet& packet) override; + + const ICounterDirectory& GetCounterDirectory() const; + + bool ParsedCounterDirectory() + { + return m_AlreadyParsed.load(); + } + + /** + * Given a Uid that came from a copy of the counter directory translate it to the original. + * + * @param copyUid + * @return the original Uid that the copy maps to. + */ + uint16_t TranslateUIDCopyToOriginal(uint16_t copyUid) + { + return m_UidTranslation[copyUid]; + } + +private: + void ParseData(const arm::pipe::Packet& packet); + + void ReadCategoryRecords(const unsigned char* data, uint32_t offset, std::vector categoryOffsets); + + std::vector + ReadEventRecords(const unsigned char* data, uint32_t offset, std::vector eventRecordsOffsets); + + std::string GetStringNameFromBuffer(const unsigned char* data, uint32_t offset); + bool IsValidChar(unsigned char c); + + std::string m_ApplicationName; + CounterDirectory m_CounterDirectory; + std::unordered_map m_UidTranslation; + bool m_QuietOperation; + // We can only parse the counter directory once per instance. It won't change anyway as it's static + // per instance of ArmNN. + std::atomic m_AlreadyParsed; +}; + +} // namespace pipe + +} // namespace arm diff --git a/profiling/server/src/timelineDecoder/CMakeLists.txt b/profiling/server/src/timelineDecoder/CMakeLists.txt index 7154722fa6..4ac68a52af 100644 --- a/profiling/server/src/timelineDecoder/CMakeLists.txt +++ b/profiling/server/src/timelineDecoder/CMakeLists.txt @@ -6,6 +6,7 @@ if(BUILD_TIMELINE_DECODER) set(timelineDecoder_sources) list(APPEND timelineDecoder_sources + DirectoryCaptureCommandHandler.cpp TimelineCaptureCommandHandler.cpp TimelineDecoder.cpp TimelineDirectoryCaptureCommandHandler.cpp) diff --git a/profiling/server/src/timelineDecoder/DirectoryCaptureCommandHandler.cpp b/profiling/server/src/timelineDecoder/DirectoryCaptureCommandHandler.cpp new file mode 100644 index 0000000000..48dce40dd1 --- /dev/null +++ b/profiling/server/src/timelineDecoder/DirectoryCaptureCommandHandler.cpp @@ -0,0 +1,341 @@ +// +// Copyright © 2019 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include + +#include + +#include +#include + +namespace arm +{ + +namespace pipe +{ + +// Utils +uint32_t uint16_t_size = sizeof(uint16_t); +uint32_t uint32_t_size = sizeof(uint32_t); + +void DirectoryCaptureCommandHandler::ParseData(const arm::pipe::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 = 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 = 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 + const uint32_t deviceRecordsPointerTableOffset = ReadUint32(data, offset); + 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 = 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 + const uint32_t counterPointerTableSetOffset = 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 = ReadUint16(data, offset); + offset += uint16_t_size; + + // Body header word 5: + // 0:31 [32] categories_pointer_table_offset: offset to the categories_pointer_table + const uint32_t categoriesPointerTableOffset = ReadUint32(data, offset); + offset += uint32_t_size; + + std::vector deviceRecordOffsets(deviceRecordCount); + std::vector counterSetOffsets(counterSetRecordCount); + std::vector categoryOffsets(categoryRecordCount); + + offset = deviceRecordsPointerTableOffset; + for (uint32_t i = 0; i < deviceRecordCount; ++i) + { + deviceRecordOffsets[i] = ReadUint32(data, offset); + offset += uint32_t_size; + } + + offset = counterPointerTableSetOffset; + for (uint32_t i = 0; i < counterSetRecordCount; ++i) + { + counterSetOffsets[i] = ReadUint32(data, offset); + offset += uint32_t_size; + } + + offset = categoriesPointerTableOffset; + for (uint32_t i = 0; i < categoryRecordCount; ++i) + { + categoryOffsets[i] = ReadUint32(data, offset); + offset += uint32_t_size; + } + + offset = deviceRecordsPointerTableOffset; + 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 + uint16_t deviceCores = ReadUint16(data, deviceRecordOffset); + // 16:31 [16] deviceUid: the unique identifier for the device + deviceRecordOffset += uint16_t_size; + uint16_t deviceUid = 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 = ReadUint32(data, deviceRecordOffset); + + deviceRecordOffset = deviceRecordsPointerTableOffset + nameOffset; + + const std::string& deviceName = GetStringNameFromBuffer(data, deviceRecordOffset); + const Device* registeredDevice = m_CounterDirectory.RegisterDevice(deviceName, deviceCores); + m_UidTranslation[registeredDevice->m_Uid] = deviceUid; + } + + offset = counterPointerTableSetOffset; + 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 + uint16_t counterSetCount = ReadUint16(data, counterSetOffset); + counterSetOffset += uint16_t_size; + + // 16:31 [16] deviceUid: the unique identifier for the counter_set + uint16_t counterSetUid = 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; + + auto counterSet = + m_CounterDirectory.RegisterCounterSet(GetStringNameFromBuffer(data, counterSetOffset), counterSetCount); + m_UidTranslation[counterSet->m_Uid] = counterSetUid; + } + ReadCategoryRecords(data, categoriesPointerTableOffset, categoryOffsets); +} + +void DirectoryCaptureCommandHandler::ReadCategoryRecords(const unsigned char* const data, + uint32_t offset, + std::vector categoryOffsets) +{ + uint32_t categoryRecordCount = static_cast(categoryOffsets.size()); + + for (uint32_t categoryIndex = 0; categoryIndex < categoryRecordCount; ++categoryIndex) + { + uint32_t categoryRecordOffset = offset + categoryOffsets[categoryIndex]; + + // Category record word 1: + // 0:15 Reserved, value 0x0000. + categoryRecordOffset += uint16_t_size; + // 16:31 Number of events belonging to this category. + uint32_t eventCount = 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 = 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 = ReadUint32(data, categoryRecordOffset); + categoryRecordOffset += uint32_t_size; + + std::vector eventRecordsOffsets(eventCount); + + eventPointerTableOffset += offset + categoryOffsets[categoryIndex]; + + for (uint32_t eventIndex = 0; eventIndex < eventCount; ++eventIndex) + { + eventRecordsOffsets[eventIndex] = + ReadUint32(data, eventPointerTableOffset + uint32_t_size * eventIndex); + } + + const std::vector& eventRecords = + ReadEventRecords(data, eventPointerTableOffset, eventRecordsOffsets); + + const Category* category = m_CounterDirectory.RegisterCategory( + GetStringNameFromBuffer(data, offset + categoryOffsets[categoryIndex] + nameOffset + uint32_t_size)); + for (auto& counter : eventRecords) + { + const Counter* registeredCounter = m_CounterDirectory.RegisterCounter(m_ApplicationName, + counter.m_CounterUid, + category->m_Name, + counter.m_CounterClass, + counter.m_CounterInterpolation, + counter.m_CounterMultiplier, + counter.m_CounterName, + counter.m_CounterDescription, + counter.m_CounterUnits); + m_UidTranslation[registeredCounter->m_Uid] = counter.m_CounterUid; + } + } +} + +std::vector DirectoryCaptureCommandHandler::ReadEventRecords( + const unsigned char* 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 = 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 = 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_CounterSetUid = 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_DeviceUid = 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_CounterInterpolation = 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_CounterClass = 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] = ReadUint32(data, eventRecordOffset); + eventRecordOffset += uint32_t_size; + multiplier[1] = 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 + uint32_t nameOffset = ReadUint32(data, eventRecordOffset); + 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 = 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 = ReadUint32(data, eventRecordOffset); + + eventRecords[i].m_CounterName = GetStringNameFromBuffer(data, offset + + eventRecordsOffsets[i] + + nameOffset + + uint32_t_size); + + eventRecords[i].m_CounterDescription = GetStringNameFromBuffer(data, offset + + eventRecordsOffsets[i] + + descriptionOffset + + uint32_t_size); + + eventRecords[i].m_CounterUnits = unitsOffset == 0 ? arm::pipe::Optional() : + GetStringNameFromBuffer(data, eventRecordsOffsets[i] + offset + unitsOffset + uint32_t_size); + } + + return eventRecords; +} + +void DirectoryCaptureCommandHandler::operator()(const arm::pipe::Packet& packet) +{ + if (!m_QuietOperation) // Are we supposed to print to stdout? + { + std::cout << "Counter directory packet received." << std::endl; + } + + // The ArmNN counter directory is static per ArmNN instance. Ensure we don't parse it a second time. + if (!ParsedCounterDirectory()) + { + ParseData(packet); + m_AlreadyParsed = true; + } + + if (!m_QuietOperation) + { + PrintCounterDirectory(m_CounterDirectory); + } +} + +const ICounterDirectory& DirectoryCaptureCommandHandler::GetCounterDirectory() const +{ + return m_CounterDirectory; +} + +std::string DirectoryCaptureCommandHandler::GetStringNameFromBuffer(const unsigned char* const data, uint32_t offset) +{ + std::string deviceName; + uint8_t nextChar = ReadUint8(data, offset); + + while (isprint(nextChar)) + { + deviceName += static_cast(nextChar); + offset++; + nextChar = ReadUint8(data, offset); + } + + return deviceName; +} + +} // namespace pipe + +} // namespace arm -- cgit v1.2.1