aboutsummaryrefslogtreecommitdiff
path: root/profiling
diff options
context:
space:
mode:
authorJim Flynn <jim.flynn@arm.com>2022-03-16 18:43:18 +0000
committerJim Flynn <jim.flynn@arm.com>2022-03-16 21:41:51 +0000
commitc454ac95267beecd67b1ec3ef8851d5089f99c4c (patch)
tree56a240e14004d05e8a4454374bac8fe80fca8d3c /profiling
parent9c85b41a9fcb3c64d7a686373e7dde75630ccd49 (diff)
downloadarmnn-c454ac95267beecd67b1ec3ef8851d5089f99c4c.tar.gz
IVGCVSW-6851 Move DirectoryCaptureCommandHandler to the profiling server library
Change-Id: Ib14fdcca15f40fedc0f992b0fd882458dc58c9ba Signed-off-by: Jim Flynn <jim.flynn@arm.com>
Diffstat (limited to 'profiling')
-rw-r--r--profiling/common/include/CommonProfilingUtils.hpp10
-rw-r--r--profiling/common/include/Counter.hpp62
-rw-r--r--profiling/common/include/CounterDirectory.hpp103
-rw-r--r--profiling/common/include/ICounterDirectory.hpp118
-rw-r--r--profiling/common/include/ICounterRegistry.hpp49
-rw-r--r--profiling/common/src/CMakeLists.txt1
-rw-r--r--profiling/common/src/CommonProfilingUtils.cpp241
-rw-r--r--profiling/common/src/CounterDirectory.cpp546
-rw-r--r--profiling/server/include/timelineDecoder/DirectoryCaptureCommandHandler.hpp90
-rw-r--r--profiling/server/src/timelineDecoder/CMakeLists.txt1
-rw-r--r--profiling/server/src/timelineDecoder/DirectoryCaptureCommandHandler.cpp341
11 files changed, 1560 insertions, 2 deletions
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 <cstdint>
#include <string>
@@ -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<uint16_t> 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 <string>
+
+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 <string>
+#include <unordered_set>
+#include <unordered_map>
+
+#include <common/include/NumericCast.hpp>
+
+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<std::string>& parentCategoryName
+ = arm::pipe::EmptyOptional()) override;
+ const CounterSet* RegisterCounterSet(const std::string& counterSetName,
+ uint16_t count = 0,
+ const arm::pipe::Optional<std::string>& 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<std::string>& units = arm::pipe::EmptyOptional(),
+ const arm::pipe::Optional<uint16_t>& numberOfCores = arm::pipe::EmptyOptional(),
+ const arm::pipe::Optional<uint16_t>& deviceUid = arm::pipe::EmptyOptional(),
+ const arm::pipe::Optional<uint16_t>& counterSetUid = arm::pipe::EmptyOptional()) override;
+
+ // Getters for counts
+ uint16_t GetCategoryCount() const override { return arm::pipe::numeric_cast<uint16_t>(m_Categories.size()); }
+ uint16_t GetDeviceCount() const override { return arm::pipe::numeric_cast<uint16_t>(m_Devices.size()); }
+ uint16_t GetCounterSetCount() const override { return arm::pipe::numeric_cast<uint16_t>(m_CounterSets.size()); }
+ uint16_t GetCounterCount() const override { return arm::pipe::numeric_cast<uint16_t>(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<uint16_t>& 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 <string>
+#include <vector>
+#include <memory>
+#include <unordered_set>
+#include <unordered_map>
+
+namespace arm
+{
+
+namespace pipe
+{
+
+// Forward declarations
+class Category;
+class Device;
+class CounterSet;
+
+// Profiling objects smart pointer types
+using CategoryPtr = std::unique_ptr<Category>;
+using DevicePtr = std::unique_ptr<Device>;
+using CounterSetPtr = std::unique_ptr<CounterSet>;
+using CounterPtr = std::shared_ptr<Counter>;
+
+// Profiling objects collection types
+using Categories = std::unordered_set<CategoryPtr>;
+using Devices = std::unordered_map<uint16_t, DevicePtr>;
+using CounterSets = std::unordered_map<uint16_t, CounterSetPtr>;
+using Counters = std::unordered_map<uint16_t, CounterPtr>;
+
+// 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<uint16_t> 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 <common/include/Optional.hpp>
+
+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<std::string>& parentCategoryName) = 0;
+
+ virtual const CounterSet* RegisterCounterSet(const std::string& counterSetName,
+ uint16_t count,
+ const arm::pipe::Optional<std::string>& 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<std::string>& units = arm::pipe::EmptyOptional(),
+ const arm::pipe::Optional<uint16_t>& numberOfCores = arm::pipe::EmptyOptional(),
+ const arm::pipe::Optional<uint16_t>& deviceUid = arm::pipe::EmptyOptional(),
+ const arm::pipe::Optional<uint16_t>& 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 <common/include/Assert.hpp>
#include <common/include/CommonProfilingUtils.hpp>
+#include <common/include/ProfilingException.hpp>
+#include <iostream>
+#include <limits>
#include <sstream>
namespace arm
@@ -140,6 +143,242 @@ std::string CentreAlignFormatting(const std::string& stringToPass, const int spa
return outputStream.str();
}
+void PrintDeviceDetails(const std::pair<const unsigned short, std::unique_ptr<Device>>& 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<const unsigned short, std::unique_ptr<CounterSet>>& 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>& 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>& category,
+ std::unordered_map<unsigned short, std::shared_ptr<Counter>> 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<int>(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<int>(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<int>(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<int>(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<uint16_t>::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<uint16_t>::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<uint16_t> 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<uint16_t> 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 <common/include/CounterDirectory.hpp>
+#include <common/include/CommonProfilingUtils.hpp>
+
+#include <common/include/Assert.hpp>
+#include <common/include/SwTrace.hpp>
+
+#include <fmt/format.h>
+
+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<arm::pipe::SwTraceNameCharPolicy>(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<Category>(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<std::string>& parentCategoryName)
+{
+ // Check that the given device name is valid
+ if (deviceName.empty() ||
+ !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceCharPolicy>(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<Device>(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<std::string>& parentCategoryName)
+{
+ // Check that the given counter set name is valid
+ if (counterSetName.empty() ||
+ !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceNameCharPolicy>(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<CounterSet>(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<std::string>& units,
+ const arm::pipe::Optional<uint16_t>& numberOfCores,
+ const arm::pipe::Optional<uint16_t>& deviceUid,
+ const arm::pipe::Optional<uint16_t>& counterSetUid)
+{
+ // Check that the given parent category name is valid
+ if (parentCategoryName.empty() ||
+ !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceNameCharPolicy>(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<arm::pipe::SwTraceCharPolicy>(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<arm::pipe::SwTraceCharPolicy>(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<arm::pipe::SwTraceNameCharPolicy>(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<uint16_t>& 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<uint16_t> 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<Counter>(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<uint16_t>& 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 <common/include/CommandHandlerFunctor.hpp>
+#include <common/include/CounterDirectory.hpp>
+
+#include <atomic>
+
+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<std::string> 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<uint32_t> categoryOffsets);
+
+ std::vector<CounterDirectoryEventRecord>
+ ReadEventRecords(const unsigned char* data, uint32_t offset, std::vector<uint32_t> 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<uint16_t, uint16_t> 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<bool> 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 <common/include/CommonProfilingUtils.hpp>
+
+#include <server/include/timelineDecoder/DirectoryCaptureCommandHandler.hpp>
+
+#include <atomic>
+#include <iostream>
+
+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<uint32_t> deviceRecordOffsets(deviceRecordCount);
+ std::vector<uint32_t> counterSetOffsets(counterSetRecordCount);
+ std::vector<uint32_t> 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<uint32_t> categoryOffsets)
+{
+ uint32_t categoryRecordCount = static_cast<uint32_t>(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<uint32_t> 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<CounterDirectoryEventRecord>& 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<CounterDirectoryEventRecord> DirectoryCaptureCommandHandler::ReadEventRecords(
+ const unsigned char* data, uint32_t offset, std::vector<uint32_t> eventRecordsOffsets)
+{
+ uint32_t eventCount = static_cast<uint32_t>(eventRecordsOffsets.size());
+
+ std::vector<CounterDirectoryEventRecord> 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<std::string>() :
+ 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<char>(nextChar);
+ offset++;
+ nextChar = ReadUint8(data, offset);
+ }
+
+ return deviceName;
+}
+
+} // namespace pipe
+
+} // namespace arm