IVGCVSW-3691 Rework the CounterDirectory class to take into consideration
the connections between components

 * Added constructors and connections to the profiling classes
 * Used hash table to keep track of the profiling objects by UID
 * Added register methods
 * Added find/check helper methods
 * Updated the makefile to include the profiling directory
 * Added unit tests for the CounterDirectory class
 * Added ICounterDirectory interface class for read-only use
 * Added custom macro to locally disable conversion warnings

Change-Id: I3f53a68663ee77b8d03ac0ef7dc01e90c6893511
Signed-off-by: Matteo Martincigh <matteo.martincigh@arm.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a04f30b..0c66eef 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -205,7 +205,10 @@
 
 list(APPEND armnn_sources
     include/armnn/ArmNN.hpp
+    include/armnn/BackendHelper.hpp
     include/armnn/BackendId.hpp
+    include/armnn/Conversion.hpp
+    include/armnn/Deprecated.hpp
     include/armnn/Descriptors.hpp
     include/armnn/DescriptorsFwd.hpp
     include/armnn/Exceptions.hpp
@@ -428,28 +431,29 @@
     src/profiling/EncodeVersion.hpp
     src/profiling/Holder.cpp
     src/profiling/Holder.hpp
+    src/profiling/IBufferWrapper.hpp
+    src/profiling/ICounterDirectory.hpp
+    src/profiling/ISendCounterPacket.hpp
+    src/profiling/IPeriodicCounterCapture.hpp
     src/profiling/IProfilingConnection.hpp
     src/profiling/Packet.cpp
     src/profiling/Packet.hpp
     src/profiling/PacketVersionResolver.cpp
     src/profiling/PacketVersionResolver.hpp
-    src/profiling/ProfilingConnectionFactory.cpp
-    src/profiling/ProfilingConnectionFactory.hpp
-    src/profiling/IBufferWrapper.hpp
-    src/profiling/IPeriodicCounterCapture.hpp
-    src/profiling/ISendCounterPacket.hpp
-    src/profiling/SendCounterPacket.hpp
-    src/profiling/SendCounterPacket.cpp
-    src/profiling/SocketProfilingConnection.cpp
-    src/profiling/SocketProfilingConnection.hpp
-    src/profiling/ProfilingUtils.hpp
-    src/profiling/ProfilingUtils.cpp
-    src/profiling/ProfilingStateMachine.cpp
-    src/profiling/ProfilingStateMachine.hpp
-    src/profiling/ProfilingService.cpp
-    src/profiling/ProfilingService.hpp
     src/profiling/PeriodicCounterSelectionCommandHandler.cpp
     src/profiling/PeriodicCounterSelectionCommandHandler.hpp
+    src/profiling/ProfilingConnectionFactory.cpp
+    src/profiling/ProfilingConnectionFactory.hpp
+    src/profiling/ProfilingService.cpp
+    src/profiling/ProfilingService.hpp
+    src/profiling/ProfilingStateMachine.cpp
+    src/profiling/ProfilingStateMachine.hpp
+    src/profiling/ProfilingUtils.cpp
+    src/profiling/ProfilingUtils.hpp
+    src/profiling/SendCounterPacket.cpp
+    src/profiling/SendCounterPacket.hpp
+    src/profiling/SocketProfilingConnection.cpp
+    src/profiling/SocketProfilingConnection.hpp
     third-party/half/half.hpp
     )
 
@@ -473,6 +477,7 @@
 target_include_directories(armnn PRIVATE src/armnn)
 target_include_directories(armnn PRIVATE src/armnnUtils)
 target_include_directories(armnn PRIVATE src/backends)
+target_include_directories(armnn PRIVATE src/profiling)
 
 target_link_libraries(armnn armnnUtils)
 
@@ -745,6 +750,7 @@
     target_include_directories(UnitTests PRIVATE src/armnn)
     target_include_directories(UnitTests PRIVATE src/armnnUtils)
     target_include_directories(UnitTests PRIVATE src/backends)
+    target_include_directories(UnitTests PRIVATE src/profiling)
 
     if(VALGRIND_FOUND)
         if(HEAP_PROFILING OR LEAK_CHECKING)
diff --git a/include/armnn/Conversion.hpp b/include/armnn/Conversion.hpp
new file mode 100644
index 0000000..37338ed
--- /dev/null
+++ b/include/armnn/Conversion.hpp
@@ -0,0 +1,40 @@
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#if __GNUC__
+#   define ARMNN_NO_CONVERSION_WARN_BEGIN \
+    _Pragma("GCC diagnostic push")  \
+    _Pragma("GCC diagnostic ignored \"-Wconversion\"")
+
+#   define ARMNN_NO_CONVERSION_WARN_END \
+    _Pragma("GCC diagnostic pop")
+
+#elif __clang__
+#   define ARMNN_NO_CONVERSION_WARN_BEGIN \
+    _Pragma("clang diagnostic push")  \
+    _Pragma("clang diagnostic ignored \"-Wconversion\"")
+
+#   define ARMNN_NO_CONVERSION_WARN_END \
+    _Pragma("clang diagnostic pop")
+
+#elif defined (_MSC_VER)
+#   define ARMNN_NO_CONVERSION_WARN_BEGIN \
+    __pragma(warning( push )) \
+    __pragma(warning(disable : 4101))
+
+#   define ARMNN_NO_CONVERSION_WARN_END \
+    __pragma(warning( pop ))
+
+#else
+#   define ARMNN_NO_CONVERSION_WARN_BEGIN
+#   define ARMNN_NO_CONVERSION_WARN_END
+#endif
+
+#define ARMNN_SUPRESS_CONVERSION_WARNING(func) \
+ARMNN_NO_CONVERSION_WARN_BEGIN \
+func; \
+ARMNN_NO_CONVERSION_WARN_END
diff --git a/src/profiling/CounterDirectory.cpp b/src/profiling/CounterDirectory.cpp
index a848979..cef3d6a 100644
--- a/src/profiling/CounterDirectory.cpp
+++ b/src/profiling/CounterDirectory.cpp
@@ -4,8 +4,12 @@
 //
 
 #include "CounterDirectory.hpp"
+#include "ProfilingUtils.hpp"
 
 #include <armnn/Exceptions.hpp>
+#include <armnn/Conversion.hpp>
+
+#include <boost/format.hpp>
 
 namespace armnn
 {
@@ -13,149 +17,616 @@
 namespace profiling
 {
 
-CounterDirectory::CounterDirectory(uint16_t uid,
-                                   const std::string& name,
-                                   uint16_t deviceCount,
-                                   uint16_t counterCount,
-                                   uint16_t categoryCount)
-    : m_Uid(uid)
-    , m_Name(name)
-    , m_DeviceCount(deviceCount)
-    , m_CounterCount(counterCount)
-    , m_CategoryCount(categoryCount)
-    , m_DeviceIds(deviceCount)
-    , m_CounterIds(counterCount)
-    , m_CategoryIds(categoryCount)
-    , m_DeviceObjects(deviceCount)
-    , m_CounterObjects(counterCount)
-    , m_CategoryObjects(categoryCount)
-{}
-
-// Helper methods
-void CounterDirectory::CheckDeviceIndex(uint16_t index) const
+const Category* CounterDirectory::RegisterCategory(const std::string& categoryName,
+                                                   const Optional<uint16_t>& deviceUid,
+                                                   const Optional<uint16_t>& counterSetUid)
 {
-    if (index >= m_DeviceCount)
+    // Check that the given category name is valid
+    if (categoryName.empty() ||
+            !IsValidSwTraceString<SwTraceNameCharPolicy>(categoryName))
     {
-        throw InvalidArgumentException("Invalid device index");
+        throw InvalidArgumentException("Trying to register a category with an invalid name");
     }
-}
 
-void CounterDirectory::CheckCounterIndex(uint16_t index) const
-{
-    if (index >= m_CounterCount)
+    // Check that the given category is not already registered
+    if (CheckIfCategoryIsRegistered(categoryName))
     {
-        throw InvalidArgumentException("Invalid counter index");
+        throw InvalidArgumentException(
+                    boost::str(boost::format("Trying to register a category already registered (\"%1%\")")
+                               % categoryName));
     }
-}
 
-void CounterDirectory::CheckCategoryIndex(uint16_t index) const
-{
-    if (index >= m_CategoryCount)
+    // Check that a device with the given (optional) UID is already registered
+    uint16_t deviceUidValue = deviceUid.has_value() ? deviceUid.value() : 0;
+    if (deviceUidValue > 0)
     {
-        throw InvalidArgumentException("Invalid category index");
+        // Check that the (optional) device is already registered
+        if (!CheckIfDeviceIsRegistered(deviceUidValue))
+        {
+            throw InvalidArgumentException(
+                        boost::str(boost::format("Trying to connect a category (\"%1%\") to a device that is "
+                                                 "not registered (UID %2%)")
+                                   % categoryName
+                                   % deviceUidValue));
+        }
     }
+
+    // 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 (!CheckIfCounterSetIsRegistered(counterSetUidValue))
+        {
+            throw InvalidArgumentException(
+                        boost::str(boost::format("Trying to connect a category (name: \"%1%\") to a counter set "
+                                                 "that is not registered (UID: %2%)")
+                                   % categoryName
+                                   % counterSetUidValue));
+        }
+    }
+
+    // Create the category
+    CategoryPtr category = std::make_unique<Category>(categoryName, deviceUidValue, counterSetUidValue);
+    BOOST_ASSERT(category);
+
+    // Get the raw category pointer
+    const Category* categoryPtr = category.get();
+    BOOST_ASSERT(categoryPtr);
+
+    // Register the category
+    m_Categories.insert(std::move(category));
+
+    return categoryPtr;
 }
 
-// Getters for basic attributes
-uint16_t CounterDirectory::GetUid() const
+const Device* CounterDirectory::RegisterDevice(const std::string& deviceName,
+                                               uint16_t cores,
+                                               const Optional<std::string>& parentCategoryName)
 {
-    return m_Uid;
+    // Check that the given device name is valid
+    if (deviceName.empty() ||
+            !IsValidSwTraceString<SwTraceCharPolicy>(deviceName))
+    {
+        throw InvalidArgumentException("Trying to register a device with an invalid name");
+    }
+
+    // Check that a device with the given name is not already registered
+    if (CheckIfDeviceIsRegistered(deviceName))
+    {
+        throw InvalidArgumentException(
+                    boost::str(boost::format("Trying to register a device already registered (\"%1%\")")
+                               % deviceName));
+    }
+
+    // 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 device can be registered
+    uint16_t deviceUidPeek = GetNextUid(true);
+
+    // Check that a category with the given (optional) parent category name is already registered
+    Category* parentCategoryPtr = nullptr;
+    if (parentCategoryName.has_value())
+    {
+        // Get the (optional) parent category name
+        const std::string& parentCategoryNameValue = parentCategoryName.value();
+        if (parentCategoryNameValue.empty())
+        {
+            throw InvalidArgumentException(
+                        boost::str(boost::format("Trying to connect a device (name: \"%1%\") to an invalid "
+                                                 "parent category (name: \"%2%\")")
+                                   % deviceName
+                                   % parentCategoryNameValue));
+        }
+
+        // Check that the given parent category is already registered
+        auto categoryIt = FindCategory(parentCategoryNameValue);
+        if (categoryIt == m_Categories.end())
+        {
+            throw InvalidArgumentException(
+                        boost::str(boost::format("Trying to connect a device (name: \"%1%\") to a parent category that "
+                                                 "is not registered (name: \"%2%\")")
+                                   % deviceName
+                                   % parentCategoryNameValue));
+        }
+
+        // Get the parent category
+        const CategoryPtr& parentCategory = *categoryIt;
+        BOOST_ASSERT(parentCategory);
+
+        // Check that the given parent category is not already connected to another device
+        if (parentCategory->m_DeviceUid != 0 && parentCategory->m_DeviceUid != deviceUidPeek)
+        {
+            throw InvalidArgumentException(
+                        boost::str(boost::format("Trying to connect a device (UID: %1%) to a parent category that is "
+                                                 "already connected to a different device "
+                                                 "(category \"%2%\" connected to device %3%)")
+                                   % deviceUidPeek
+                                   % parentCategoryNameValue
+                                   % parentCategory->m_DeviceUid));
+        }
+
+        // The parent category can be associated to the device that is about to be registered.
+        // Get the raw pointer to the parent category (to be used later when the device is actually been
+        // registered, to make sure that the category is associated to an existing device)
+        parentCategoryPtr = parentCategory.get();
+    }
+
+    // Get the device UID
+    uint16_t deviceUid = GetNextUid();
+    BOOST_ASSERT(deviceUid == deviceUidPeek);
+
+    // Create the device
+    DevicePtr device = std::make_unique<Device>(deviceUid, deviceName, cores);
+    BOOST_ASSERT(device);
+
+    // Get the raw device pointer
+    const Device* devicePtr = device.get();
+    BOOST_ASSERT(devicePtr);
+
+    // Register the device
+    m_Devices.insert(std::make_pair(deviceUid, std::move(device)));
+
+    // Connect the device to the parent category, if required
+    if (parentCategoryPtr)
+    {
+        // Set the device UID in the parent category
+        parentCategoryPtr->m_DeviceUid = deviceUid;
+    }
+
+    return devicePtr;
 }
 
-const std::string& CounterDirectory::GetName() const
+const CounterSet* CounterDirectory::RegisterCounterSet(const std::string& counterSetName,
+                                                       uint16_t count,
+                                                       const Optional<std::string>& parentCategoryName)
 {
-    return m_Name;
+    // Check that the given counter set name is valid
+    if (counterSetName.empty() ||
+            !IsValidSwTraceString<SwTraceNameCharPolicy>(counterSetName))
+    {
+        throw 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 (CheckIfCounterSetIsRegistered(counterSetName))
+    {
+        throw InvalidArgumentException(
+                    boost::str(boost::format("Trying to register a counter set already registered (\"%1%\")")
+                               % 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
+    Category* parentCategoryPtr = nullptr;
+    if (parentCategoryName.has_value())
+    {
+        // Get the (optional) parent category name
+        const std::string& parentCategoryNameValue = parentCategoryName.value();
+        if (parentCategoryNameValue.empty())
+        {
+            throw InvalidArgumentException(
+                        boost::str(boost::format("Trying to connect a counter set (UID: %1%) to an invalid "
+                                                 "parent category (name: \"%2%\")")
+                                   % counterSetUidPeek
+                                   % parentCategoryNameValue));
+        }
+
+        // Check that the given parent category is already registered
+        auto it = FindCategory(parentCategoryNameValue);
+        if (it == m_Categories.end())
+        {
+            throw InvalidArgumentException(
+                        boost::str(boost::format("Trying to connect a counter set (UID: %1%) to a parent category "
+                                                 "that is not registered (name: \"%2%\")")
+                                   % counterSetUidPeek
+                                   % parentCategoryNameValue));
+        }
+
+        // Get the parent category
+        const CategoryPtr& parentCategory = *it;
+        BOOST_ASSERT(parentCategory);
+
+        // Check that the given parent category is not already connected to another counter set
+        if (parentCategory->m_CounterSetUid != 0 && parentCategory->m_CounterSetUid != counterSetUidPeek)
+        {
+            throw InvalidArgumentException(
+                        boost::str(boost::format("Trying to connect a counter set (UID: %1%) to a parent category "
+                                                 "that is already connected to a different counter set "
+                                                 "(category \"%2%\" connected to counter set %3%)")
+                                   % counterSetUidPeek
+                                   % parentCategoryNameValue
+                                   % parentCategory->m_CounterSetUid));
+        }
+
+        // The parent category can be associated to the counter set that is about to be registered.
+        // Get the raw pointer to the parent category (to be used later when the counter set is actually been
+        // registered, to make sure that the category is associated to an existing counter set)
+        parentCategoryPtr = parentCategory.get();
+    }
+
+    // Get the counter set UID
+    uint16_t counterSetUid = GetNextUid();
+    BOOST_ASSERT(counterSetUid == counterSetUidPeek);
+
+    // Create the counter set
+    CounterSetPtr counterSet = std::make_unique<CounterSet>(counterSetUid, counterSetName, count);
+    BOOST_ASSERT(counterSet);
+
+    // Get the raw counter set pointer
+    const CounterSet* counterSetPtr = counterSet.get();
+    BOOST_ASSERT(counterSetPtr);
+
+    // Register the counter set
+    m_CounterSets.insert(std::make_pair(counterSetUid, std::move(counterSet)));
+
+    // Connect the counter set to the parent category, if required
+    if (parentCategoryPtr)
+    {
+        // Set the counter set UID in the parent category
+        parentCategoryPtr->m_CounterSetUid = counterSetUid;
+    }
+
+    return counterSetPtr;
 }
 
-// Getters for counts
-uint16_t CounterDirectory::GetDeviceCount() const
+const Counter* CounterDirectory::RegisterCounter(const std::string& parentCategoryName,
+                                                 uint16_t counterClass,
+                                                 uint16_t interpolation,
+                                                 double multiplier,
+                                                 const std::string& name,
+                                                 const std::string& description,
+                                                 const Optional<std::string>& units,
+                                                 const Optional<uint16_t>& numberOfCores,
+                                                 const Optional<uint16_t>& deviceUid,
+                                                 const Optional<uint16_t>& counterSetUid)
 {
-    return m_DeviceCount;
+    // Check that the given parent category name is valid
+    if (parentCategoryName.empty() ||
+            !IsValidSwTraceString<SwTraceNameCharPolicy>(parentCategoryName))
+    {
+        throw 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 InvalidArgumentException("Trying to register a counter with an invalid class");
+    }
+
+    // Check that the given interpolation is valid
+    if (interpolation != 0 && interpolation != 1)
+    {
+        throw InvalidArgumentException("Trying to register a counter with an invalid interpolation");
+    }
+
+    // Check that the given multiplier is valid
+    if (multiplier == .0f)
+    {
+        throw InvalidArgumentException("Trying to register a counter with an invalid multiplier");
+    }
+
+    // Check that the given name is valid
+    if (name.empty() ||
+            !IsValidSwTraceString<SwTraceCharPolicy>(name))
+    {
+        throw InvalidArgumentException("Trying to register a counter with an invalid name");
+    }
+
+    // Check that the given description is valid
+    if (description.empty() ||
+            !IsValidSwTraceString<SwTraceCharPolicy>(description))
+    {
+        throw InvalidArgumentException("Trying to register a counter with an invalid description");
+    }
+
+    // Check that the given units are valid
+    if (units.has_value()
+            && !IsValidSwTraceString<SwTraceNameCharPolicy>(units.value()))
+    {
+        throw 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 InvalidArgumentException(
+                    boost::str(boost::format("Trying to connect a counter to a category "
+                                             "that is not registered (name: \"%1%\")")
+                               % parentCategoryName));
+    }
+
+    // Get the parent category
+    const CategoryPtr& parentCategory = *categoryIt;
+    BOOST_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);
+        BOOST_ASSERT(parentCategoryCounter);
+
+        if (parentCategoryCounter->m_Name == name)
+        {
+            throw InvalidArgumentException(
+                        boost::str(boost::format("Trying to register a counter to category \"%1%\" with a name that "
+                                                 "is already used within that category (name: \"%2%\")")
+                                   % 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 (!CheckIfCounterSetIsRegistered(counterSetUidValue))
+        {
+            throw InvalidArgumentException(
+                        boost::str(boost::format("Trying to connect a counter to a counter set that is "
+                                                 "not registered (counter set UID: %1%)")
+                                   % 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, parentCategory);
+
+    // Get the counter UIDs and calculate the max counter UID
+    std::vector<uint16_t> counterUids = GetNextCounterUids(deviceCores);
+    BOOST_ASSERT(!counterUids.empty());
+    uint16_t maxCounterUid = deviceCores <= 1 ? counterUids.front() : counterUids.back();
+
+    // Get the counter units
+    const std::string unitsValue = units.has_value() ? units.value() : "";
+
+    // Create the counter
+    CounterPtr counter = std::make_shared<Counter>(counterUids.front(),
+                                                   maxCounterUid,
+                                                   counterClass,
+                                                   interpolation,
+                                                   multiplier,
+                                                   name,
+                                                   description,
+                                                   unitsValue,
+                                                   deviceUidValue,
+                                                   counterSetUidValue);
+    BOOST_ASSERT(counter);
+
+    // Get the raw counter pointer
+    const Counter* counterPtr = counter.get();
+    BOOST_ASSERT(counterPtr);
+
+    // Process multiple counters if necessary
+    for (uint16_t counterUid : counterUids)
+    {
+        // Connect the counter to the parent category
+        parentCategory->m_Counters.push_back(counterUid);
+
+        // Register the counter
+        m_Counters.insert(std::make_pair(counterUid, counter));
+    }
+
+    return counterPtr;
 }
 
-uint16_t CounterDirectory::GetCounterCount() const
+const Category* CounterDirectory::GetCategory(const std::string& categoryName) const
 {
-    return m_CounterCount;
+    auto it = FindCategory(categoryName);
+    if (it == m_Categories.end())
+    {
+        return nullptr;
+    }
+
+    const Category* category = it->get();
+    BOOST_ASSERT(category);
+
+    return category;
 }
 
-uint16_t CounterDirectory::GetCategoryCount() const
+const Device* CounterDirectory::GetDevice(uint16_t deviceUid) const
 {
-    return m_CategoryCount;
+    auto it = FindDevice(deviceUid);
+    if (it == m_Devices.end())
+    {
+        return nullptr;
+    }
+
+    const Device* device = it->second.get();
+    BOOST_ASSERT(device);
+    BOOST_ASSERT(device->m_Uid == deviceUid);
+
+    return device;
 }
 
-// Getters and setters for devices
-void CounterDirectory::GetDeviceValue(uint16_t index, uint32_t& value) const
+const CounterSet* CounterDirectory::GetCounterSet(uint16_t counterSetUid) const
 {
-    CheckDeviceIndex(index);
-    value = m_DeviceIds[index].load();
+    auto it = FindCounterSet(counterSetUid);
+    if (it == m_CounterSets.end())
+    {
+        return nullptr;
+    }
+
+    const CounterSet* counterSet = it->second.get();
+    BOOST_ASSERT(counterSet);
+    BOOST_ASSERT(counterSet->m_Uid == counterSetUid);
+
+    return counterSet;
 }
 
-void CounterDirectory::SetDeviceValue(uint16_t index, uint32_t value)
+const Counter* CounterDirectory::GetCounter(uint16_t counterUid) const
 {
-    CheckDeviceIndex(index);
-    m_DeviceIds[index].store(value);
+    auto it = FindCounter(counterUid);
+    if (it == m_Counters.end())
+    {
+        return nullptr;
+    }
+
+    const Counter* counter = it->second.get();
+    BOOST_ASSERT(counter);
+    BOOST_ASSERT(counter->m_Uid <= counterUid);
+    BOOST_ASSERT(counter->m_Uid <= counter->m_MaxCounterUid);
+
+    return counter;
 }
 
-void CounterDirectory::GetDeviceObject(uint16_t index, Device* device) const
+CategoriesIt CounterDirectory::FindCategory(const std::string& categoryName) const
 {
-    CheckDeviceIndex(index);
-    device = m_DeviceObjects[index].load();
+    return std::find_if(m_Categories.begin(), m_Categories.end(), [&categoryName](const CategoryPtr& category)
+    {
+        BOOST_ASSERT(category);
+
+        return category->m_Name == categoryName;
+    });
 }
 
-void CounterDirectory::SetDeviceObject(uint16_t index, Device* device)
+DevicesIt CounterDirectory::FindDevice(uint16_t deviceUid) const
 {
-    CheckDeviceIndex(index);
-    m_DeviceObjects[index].store(device);
+    return m_Devices.find(deviceUid);
 }
 
-// Getters and setters for counters
-void CounterDirectory::GetCounterValue(uint16_t index, uint32_t& value) const
+DevicesIt CounterDirectory::FindDevice(const std::string& deviceName) const
 {
-    CheckCounterIndex(index);
-    value = m_CounterIds[index].load();
+    return std::find_if(m_Devices.begin(), m_Devices.end(), [&deviceName](const auto& pair)
+    {
+        BOOST_ASSERT(pair.second);
+        BOOST_ASSERT(pair.second->m_Uid == pair.first);
+
+        return pair.second->m_Name == deviceName;
+    });
 }
 
-void CounterDirectory::SetCounterValue(uint16_t index, uint32_t value)
+CounterSetsIt CounterDirectory::FindCounterSet(uint16_t counterSetUid) const
 {
-    CheckCounterIndex(index);
-    m_CounterIds[index].store(value);
+    return m_CounterSets.find(counterSetUid);
 }
 
-void CounterDirectory::GetCounterObject(uint16_t index, Counter* counter) const
+CounterSetsIt CounterDirectory::FindCounterSet(const std::string& counterSetName) const
 {
-    CheckCounterIndex(index);
-    counter = m_CounterObjects[index].load();
+    return std::find_if(m_CounterSets.begin(), m_CounterSets.end(), [&counterSetName](const auto& pair)
+    {
+        BOOST_ASSERT(pair.second);
+        BOOST_ASSERT(pair.second->m_Uid == pair.first);
+
+        return pair.second->m_Name == counterSetName;
+    });
 }
 
-void CounterDirectory::SetCounterObject(uint16_t index, Counter* counter)
+CountersIt CounterDirectory::FindCounter(uint16_t counterUid) const
 {
-    CheckCounterIndex(index);
-    m_CounterObjects[index].store(counter);
+    return m_Counters.find(counterUid);
 }
 
-// Getters and setters for categories
-void CounterDirectory::GetCategoryValue(uint16_t index, uint32_t& value) const
+bool CounterDirectory::CheckIfCategoryIsRegistered(const std::string& categoryName) const
 {
-    CheckCategoryIndex(index);
-    value = m_CategoryIds[index].load();
+    auto it = FindCategory(categoryName);
+
+    return it != m_Categories.end();
 }
 
-void CounterDirectory::SetCategoryValue(uint16_t index, uint32_t value)
+bool CounterDirectory::CheckIfDeviceIsRegistered(uint16_t deviceUid) const
 {
-    CheckCategoryIndex(index);
-    m_CategoryIds[index].store(value);
+    auto it = FindDevice(deviceUid);
+
+    return it != m_Devices.end();
 }
 
-void CounterDirectory::GetCategoryObject(uint16_t index, Category* category) const
+bool CounterDirectory::CheckIfDeviceIsRegistered(const std::string& deviceName) const
 {
-    CheckCategoryIndex(index);
-    category = m_CategoryObjects[index].load();
+    auto it = FindDevice(deviceName);
+
+    return it != m_Devices.end();
 }
 
-void CounterDirectory::SetCategoryObject(uint16_t index, Category* category)
+bool CounterDirectory::CheckIfCounterSetIsRegistered(uint16_t counterSetUid) const
 {
-    CheckCategoryIndex(index);
-    m_CategoryObjects[index].store(category);
+    auto it = FindCounterSet(counterSetUid);
+
+    return it != m_CounterSets.end();
+}
+
+bool CounterDirectory::CheckIfCounterSetIsRegistered(const std::string& counterSetName) const
+{
+    auto it = FindCounterSet(counterSetName);
+
+    return it != m_CounterSets.end();
+}
+
+uint16_t CounterDirectory::GetNumberOfCores(const Optional<uint16_t>& numberOfCores,
+                                            uint16_t deviceUid,
+                                            const CategoryPtr& parentCategory)
+{
+    BOOST_ASSERT(parentCategory);
+
+    // 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 neither of the above is set then look at the category to see if it has a device associated with it,
+    //    if it does then take that device's numberOfCores as the deviceCores value
+    // 4. 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 InvalidArgumentException(
+                        boost::str(boost::format("Trying to connect a counter to a device that is "
+                                                 "not registered (device UID %1%)")
+                                   % deviceUid));
+        }
+
+        // Get the associated device
+        const DevicePtr& device = deviceIt->second;
+        BOOST_ASSERT(device);
+
+        // Get the number of cores of the associated device
+        return device->m_Cores;
+    }
+
+    // 3. If neither of the above is set then look at the category to see if it has a device associated with it,
+    //    if it does then take that device's numberOfCores as the deviceCores value
+    uint16_t parentCategoryDeviceUid = parentCategory->m_DeviceUid;
+    if (parentCategoryDeviceUid > 0)
+    {
+        // Check that the device associated to the parent category is already registered
+        auto deviceIt = FindDevice(parentCategoryDeviceUid);
+        if (deviceIt == m_Devices.end())
+        {
+            throw InvalidArgumentException(
+                        boost::str(boost::format("Trying to get the number of cores from a device that is "
+                                                 "not registered (device UID %1%)")
+                                   % parentCategoryDeviceUid));
+        }
+
+        // Get the associated device
+        const DevicePtr& device = deviceIt->second;
+        BOOST_ASSERT(device);
+
+        // Get the number of cores of the device associated to the parent category
+        return device->m_Cores;
+    }
+
+    // 4. If none of the above holds then set deviceCores to zero
+    return 0;
 }
 
 } // namespace profiling
diff --git a/src/profiling/CounterDirectory.hpp b/src/profiling/CounterDirectory.hpp
index ec1ac27..a756a9a 100644
--- a/src/profiling/CounterDirectory.hpp
+++ b/src/profiling/CounterDirectory.hpp
@@ -5,9 +5,15 @@
 
 #pragma once
 
-#include <atomic>
+#include "ICounterDirectory.hpp"
+
+#include <armnn/Optional.hpp>
+
 #include <string>
-#include <vector>
+#include <unordered_set>
+#include <unordered_map>
+
+#include <boost/numeric/conversion/cast.hpp>
 
 namespace armnn
 {
@@ -15,96 +21,73 @@
 namespace profiling
 {
 
-class Category
+class CounterDirectory final : public ICounterDirectory
 {
 public:
-    std::string m_Name;
-};
-
-class Device
-{
-public:
-    uint16_t    m_Uid;
-    std::string m_Name;
-    uint16_t    m_Cores;
-};
-
-class Counter
-{
-public:
-    uint16_t    m_Uid;
-    uint16_t    m_MaxCounterUid;
-    uint16_t    m_Class;
-    uint16_t    m_Interpolation;
-    float       m_Multiplier;
-    std::string m_Name;
-    std::string m_Description;
-    std::string m_Units;
-};
-
-class CounterSet
-{
-public:
-    uint16_t    m_Uid;
-    std::string m_Name;
-    uint16_t    m_Count;
-};
-
-class CounterDirectory final
-{
-public:
-    CounterDirectory(uint16_t uid,
-                     const std::string& name,
-                     uint16_t deviceCount,
-                     uint16_t counterCount,
-                     uint16_t categoryCount);
-
+    CounterDirectory() = default;
     ~CounterDirectory() = default;
 
-    uint16_t GetUid() const;
-    const std::string& GetName() const;
+    // Register profiling objects
+    const Category*   RegisterCategory  (const std::string& categoryName,
+                                         const Optional<uint16_t>& deviceUid = EmptyOptional(),
+                                         const Optional<uint16_t>& counterSetUid = EmptyOptional());
+    const Device*     RegisterDevice    (const std::string& deviceName,
+                                         uint16_t cores = 0,
+                                         const Optional<std::string>& parentCategoryName = EmptyOptional());
+    const CounterSet* RegisterCounterSet(const std::string& counterSetName,
+                                         uint16_t count = 0,
+                                         const Optional<std::string>& parentCategoryName = EmptyOptional());
+    const Counter*    RegisterCounter   (const std::string& parentCategoryName,
+                                         uint16_t counterClass,
+                                         uint16_t interpolation,
+                                         double multiplier,
+                                         const std::string& name,
+                                         const std::string& description,
+                                         const Optional<std::string>& units = EmptyOptional(),
+                                         const Optional<uint16_t>& numberOfCores = EmptyOptional(),
+                                         const Optional<uint16_t>& deviceUid = EmptyOptional(),
+                                         const Optional<uint16_t>& counterSetUid = EmptyOptional());
 
-    uint16_t GetDeviceCount() const;
-    uint16_t GetCounterCount() const;
-    uint16_t GetCategoryCount() const;
+    // Getters for counts
+    uint16_t GetCategoryCount()   const override { return boost::numeric_cast<uint16_t>(m_Categories.size());  }
+    uint16_t GetDeviceCount()     const override { return boost::numeric_cast<uint16_t>(m_Devices.size());     }
+    uint16_t GetCounterSetCount() const override { return boost::numeric_cast<uint16_t>(m_CounterSets.size()); }
+    uint16_t GetCounterCount()    const override { return boost::numeric_cast<uint16_t>(m_Counters.size());    }
 
-    void GetDeviceValue(uint16_t index, uint32_t& value) const;
-    void SetDeviceValue(uint16_t index, uint32_t value);
+    // 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;    }
 
-    void GetDeviceObject(uint16_t index, Device* counter) const;
-    void SetDeviceObject(uint16_t index, Device* counter);
-
-    void GetCounterValue(uint16_t index, uint32_t& value) const;
-    void SetCounterValue(uint16_t index, uint32_t value);
-
-    void GetCounterObject(uint16_t index, Counter* counter) const;
-    void SetCounterObject(uint16_t index, Counter* counter);
-
-    void GetCategoryValue(uint16_t index, uint32_t& value) const;
-    void SetCategoryValue(uint16_t index, uint32_t value);
-
-    void GetCategoryObject(uint16_t index, Category* counter) const;
-    void SetCategoryObject(uint16_t index, Category* counter);
+    // 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;
 
 private:
-    uint16_t    m_Uid;
-    std::string m_Name;
+    // The profiling collections owned by the counter directory
+    Categories  m_Categories;
+    Devices     m_Devices;
+    CounterSets m_CounterSets;
+    Counters    m_Counters;
 
-    uint16_t m_DeviceCount;
-    uint16_t m_CounterCount;
-    uint16_t m_CategoryCount;
-
-    std::vector<std::atomic<uint32_t>> m_DeviceIds;
-    std::vector<std::atomic<uint32_t>> m_CounterIds;
-    std::vector<std::atomic<uint32_t>> m_CategoryIds;
-
-    std::vector<std::atomic<Device*>>   m_DeviceObjects;
-    std::vector<std::atomic<Counter*>>  m_CounterObjects;
-    std::vector<std::atomic<Category*>> m_CategoryObjects;
-
-    void CheckDeviceIndex(uint16_t index) const;
-    void CheckCounterIndex(uint16_t index) const;
-    void CheckCategoryIndex(uint16_t index) const;
+    // 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;
+    bool CheckIfCategoryIsRegistered(const std::string& categoryName) const;
+    bool CheckIfDeviceIsRegistered(uint16_t deviceUid) const;
+    bool CheckIfDeviceIsRegistered(const std::string& deviceName) const;
+    bool CheckIfCounterSetIsRegistered(uint16_t counterSetUid) const;
+    bool CheckIfCounterSetIsRegistered(const std::string& counterSetName) const;
+    uint16_t GetNumberOfCores(const Optional<uint16_t>& numberOfCores,
+                              uint16_t deviceUid,
+                              const CategoryPtr& parentCategory);
 };
 
 } // namespace profiling
diff --git a/src/profiling/ICounterDirectory.hpp b/src/profiling/ICounterDirectory.hpp
new file mode 100644
index 0000000..c7259ab
--- /dev/null
+++ b/src/profiling/ICounterDirectory.hpp
@@ -0,0 +1,164 @@
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <unordered_set>
+#include <unordered_map>
+
+#include <boost/numeric/conversion/cast.hpp>
+
+namespace armnn
+{
+
+namespace profiling
+{
+
+// Forward declarations
+class Category;
+class Device;
+class CounterSet;
+class Counter;
+
+// 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, uint16_t deviceUid, uint16_t counterSetUid)
+        : m_Name(name)
+        , m_DeviceUid(deviceUid)
+        , m_CounterSetUid(counterSetUid)
+    {}
+
+    // Fields
+    std::string m_Name;
+
+    // Connections
+    std::vector<uint16_t> m_Counters;      // The UIDs of the counters associated with this category
+    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
+};
+
+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 Counter final
+{
+public:
+    // Constructors
+    Counter(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_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
+    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
+};
+
+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 profiling
+
+} // namespace armnn
diff --git a/src/profiling/ProfilingUtils.cpp b/src/profiling/ProfilingUtils.cpp
index ef67f03..e356ed7 100644
--- a/src/profiling/ProfilingUtils.cpp
+++ b/src/profiling/ProfilingUtils.cpp
@@ -6,12 +6,12 @@
 #include "ProfilingUtils.hpp"
 
 #include <armnn/Version.hpp>
+#include <armnn/Conversion.hpp>
 
 #include <boost/assert.hpp>
 
 #include <fstream>
 #include <limits>
-#include <mutex>
 
 namespace armnn
 {
@@ -19,25 +19,72 @@
 namespace profiling
 {
 
-uint16_t GetNextUid()
+namespace
 {
-    // Static mutex for reading and modifying the global UID a single thread at the time
-    static std::mutex mutex;
-    std::unique_lock<std::mutex> lock(mutex);
 
-    // The UID used for profiling objects and events. The first valid UID is 1, as 0 is a reserved value
-    // (it is used to indicate that a record is not associated with any device)
-    static uint16_t uid{ 0 };
-
+void ThrowIfCantGenerateNextUid(uint16_t uid, uint16_t cores = 0)
+{
     // Check that it is possible to generate the next UID without causing an overflow
-    if (uid == std::numeric_limits<uint16_t>::max())
+    switch (cores)
     {
-        throw RuntimeException("Generating the next UID for profiling would result in an overflow");
+    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 RuntimeException("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 RuntimeException("Generating the next UID for profiling would result in an overflow");
+        }
+        break;
     }
+}
 
-    // Thread safe increment, the value that is incremented is the value checked for overflow,
-    // as this whole function is mutexed
-    return ++uid;
+} // 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 cores)
+{
+    // The UID used for counters only. The first valid UID is 0
+    static uint16_t counterUid = 0;
+
+    // Check that it is possible to generate the next counter UID without causing an overflow (throws in case of error)
+    ThrowIfCantGenerateNextUid(counterUid, 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] = counterUid++;
+    }
+    return counterUids;
 }
 
 void WriteUint64(unsigned char* buffer, unsigned int offset, uint64_t value)
diff --git a/src/profiling/ProfilingUtils.hpp b/src/profiling/ProfilingUtils.hpp
index 0e94e61..793f94d 100644
--- a/src/profiling/ProfilingUtils.hpp
+++ b/src/profiling/ProfilingUtils.hpp
@@ -7,8 +7,12 @@
 
 #include <armnn/Exceptions.hpp>
 
+#include <boost/numeric/conversion/cast.hpp>
+
 #include <string>
-#include <stdint.h>
+#include <vector>
+#include <algorithm>
+#include <cstring>
 
 namespace armnn
 {
@@ -16,7 +20,65 @@
 namespace profiling
 {
 
-uint16_t GetNextUid();
+struct SwTraceCharPolicy
+{
+    static bool IsValidChar(unsigned char c)
+    {
+        // Check that the given character has ASCII 7-bit encoding
+        return c < 128;
+    }
+};
+
+struct SwTraceNameCharPolicy
+{
+    static bool IsValidChar(unsigned char c)
+    {
+        // Check that the given character has ASCII 7-bit encoding, alpha-numeric and underscore only
+        return c < 128 && (std::isalnum(c) || c == '_');
+    }
+};
+
+template <typename SwTracePolicy>
+bool IsValidSwTraceString(const std::string& s)
+{
+    // Check that all the characters in the given string conform to the given policy
+    return std::all_of(s.begin(), s.end(), [](unsigned char c)
+    {
+        return SwTracePolicy::IsValidChar(c);
+    });
+}
+
+template <typename SwTracePolicy>
+bool StringToSwTraceString(const std::string& s, std::vector<uint32_t>& outputBuffer)
+{
+    // Converts the given string to an SWTrace "string" (i.e. a string of "chars"), and writes it into
+    // the given buffer including the null-terminator. It also pads it to the next uint32_t if necessary
+
+    // Clear the output buffer
+    outputBuffer.clear();
+
+    // Check that the given string is a valid SWTrace "string" (i.e. a string of "chars")
+    if (!IsValidSwTraceString<SwTracePolicy>(s))
+    {
+        return false;
+    }
+
+    // Prepare the output buffer
+    size_t s_size = s.size() + 1; // The size of the string (in chars) plus the null-terminator
+    size_t uint32_t_size = sizeof(uint32_t);
+    size_t outBufferSize = 1 + s_size / uint32_t_size + (s_size % uint32_t_size != 0 ? 1 : 0);
+    outputBuffer.resize(outBufferSize, '\0');
+
+    // Write the SWTrace string to the output buffer
+    outputBuffer[0] = boost::numeric_cast<uint32_t>(s_size);
+    std::memcpy(outputBuffer.data() + 1, s.data(), s_size);
+
+    return true;
+}
+
+uint16_t GetNextUid(bool peekOnly = false);
+
+std::vector<uint16_t> GetNextCounterUids(uint16_t cores);
 
 void WriteUint64(unsigned char* buffer, unsigned int offset, uint64_t value);
 
diff --git a/src/profiling/test/ProfilingTests.cpp b/src/profiling/test/ProfilingTests.cpp
index 55524a4..d16479a 100644
--- a/src/profiling/test/ProfilingTests.cpp
+++ b/src/profiling/test/ProfilingTests.cpp
@@ -3,23 +3,24 @@
 // SPDX-License-Identifier: MIT
 //
 
-#include "../CommandHandlerKey.hpp"
-#include "../CommandHandlerFunctor.hpp"
-#include "../CommandHandlerRegistry.hpp"
-#include "../EncodeVersion.hpp"
-#include "../Holder.hpp"
-#include "../Packet.hpp"
-#include "../PacketVersionResolver.hpp"
-#include "../ProfilingService.hpp"
-#include "../ProfilingStateMachine.hpp"
-#include "../PeriodicCounterSelectionCommandHandler.hpp"
-#include "../ProfilingUtils.hpp"
-#include "../SocketProfilingConnection.hpp"
-#include "../IPeriodicCounterCapture.hpp"
 #include "SendCounterPacketTests.hpp"
 
+#include <CommandHandlerKey.hpp>
+#include <CommandHandlerFunctor.hpp>
+#include <CommandHandlerRegistry.hpp>
+#include <CounterDirectory.hpp>
+#include <EncodeVersion.hpp>
+#include <Holder.hpp>
+#include <Packet.hpp>
+#include <PacketVersionResolver.hpp>
+#include <PeriodicCounterSelectionCommandHandler.hpp>
+#include <ProfilingStateMachine.hpp>
+#include <ProfilingService.hpp>
+#include <ProfilingUtils.hpp>
 #include <Runtime.hpp>
+#include <SocketProfilingConnection.hpp>
 
+#include <armnn/Conversion.hpp>
 
 #include <boost/test/unit_test.hpp>
 #include <boost/numeric/conversion/cast.hpp>
@@ -538,30 +539,909 @@
     BOOST_CHECK(service.GetCurrentState() ==  ProfilingState::WaitingForAck);
 }
 
-void GetNextUidTestImpl(uint16_t& outUid)
+BOOST_AUTO_TEST_CASE(CheckProfilingObjectUids)
 {
-    outUid = GetNextUid();
+    uint16_t uid = 0;
+    BOOST_CHECK_NO_THROW(uid = GetNextUid());
+    BOOST_CHECK(uid >= 1);
+
+    uint16_t nextUid = 0;
+    BOOST_CHECK_NO_THROW(nextUid = GetNextUid());
+    BOOST_CHECK(nextUid > uid);
+
+    std::vector<uint16_t> counterUids;
+    BOOST_CHECK_NO_THROW(counterUids = GetNextCounterUids(0));
+    BOOST_CHECK(counterUids.size() == 1);
+    BOOST_CHECK(counterUids[0] >= 0);
+
+    std::vector<uint16_t> nextCounterUids;
+    BOOST_CHECK_NO_THROW(nextCounterUids = GetNextCounterUids(1));
+    BOOST_CHECK(nextCounterUids.size() == 1);
+    BOOST_CHECK(nextCounterUids[0] > counterUids[0]);
+
+    std::vector<uint16_t> counterUidsMultiCore;
+    uint16_t numberOfCores = 13;
+    BOOST_CHECK_NO_THROW(counterUidsMultiCore = GetNextCounterUids(numberOfCores));
+    BOOST_CHECK(counterUidsMultiCore.size() == numberOfCores);
+    BOOST_CHECK(counterUidsMultiCore.front() >= nextCounterUids[0]);
+    for (size_t i = 1; i < numberOfCores; i ++)
+    {
+        BOOST_CHECK(counterUidsMultiCore[i] == counterUidsMultiCore[i - 1] + 1);
+    }
+    BOOST_CHECK(counterUidsMultiCore.back() == counterUidsMultiCore.front() + numberOfCores - 1);
 }
 
-BOOST_AUTO_TEST_CASE(GetNextUidTest)
+BOOST_AUTO_TEST_CASE(CheckCounterDirectoryRegisterCategory)
 {
-    uint16_t uid0 = 0;
-    uint16_t uid1 = 0;
-    uint16_t uid2 = 0;
+    CounterDirectory counterDirectory;
+    BOOST_CHECK(counterDirectory.GetCategoryCount()   == 0);
+    BOOST_CHECK(counterDirectory.GetDeviceCount()     == 0);
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 0);
+    BOOST_CHECK(counterDirectory.GetCounterCount()    == 0);
 
-    std::thread thread1(GetNextUidTestImpl, std::ref(uid0));
-    std::thread thread2(GetNextUidTestImpl, std::ref(uid1));
-    std::thread thread3(GetNextUidTestImpl, std::ref(uid2));
-    thread1.join();
-    thread2.join();
-    thread3.join();
+    // Register a category with an invalid name
+    const Category* noCategory = nullptr;
+    BOOST_CHECK_THROW(noCategory = counterDirectory.RegisterCategory(""), armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 0);
+    BOOST_CHECK(!noCategory);
 
-    BOOST_TEST(uid0 > 0);
-    BOOST_TEST(uid1 > 0);
-    BOOST_TEST(uid2 > 0);
-    BOOST_TEST(uid0 != uid1);
-    BOOST_TEST(uid0 != uid2);
-    BOOST_TEST(uid1 != uid2);
+    // Register a category with an invalid name
+    BOOST_CHECK_THROW(noCategory = counterDirectory.RegisterCategory("invalid category"),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 0);
+    BOOST_CHECK(!noCategory);
+
+    // Register a new category
+    const std::string categoryName = "some_category";
+    const Category* category = nullptr;
+    BOOST_CHECK_NO_THROW(category = counterDirectory.RegisterCategory(categoryName));
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 1);
+    BOOST_CHECK(category);
+    BOOST_CHECK(category->m_Name == categoryName);
+    BOOST_CHECK(category->m_Counters.empty());
+    BOOST_CHECK(category->m_DeviceUid == 0);
+    BOOST_CHECK(category->m_CounterSetUid == 0);
+
+    // Get the registered category
+    const Category* registeredCategory = counterDirectory.GetCategory(categoryName);
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 1);
+    BOOST_CHECK(registeredCategory);
+    BOOST_CHECK(registeredCategory == category);
+
+    // Try to get a category not registered
+    const Category* notRegisteredCategory = counterDirectory.GetCategory("not_registered_category");
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 1);
+    BOOST_CHECK(!notRegisteredCategory);
+
+    // Register a category already registered
+    const Category* anotherCategory = nullptr;
+    BOOST_CHECK_THROW(anotherCategory = counterDirectory.RegisterCategory(categoryName),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 1);
+    BOOST_CHECK(!anotherCategory);
+
+    // Register a device for testing
+    const std::string deviceName = "some_device";
+    const Device* device = nullptr;
+    BOOST_CHECK_NO_THROW(device = counterDirectory.RegisterDevice(deviceName));
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 1);
+    BOOST_CHECK(device);
+    BOOST_CHECK(device->m_Uid >= 1);
+    BOOST_CHECK(device->m_Name == deviceName);
+    BOOST_CHECK(device->m_Cores == 0);
+
+    // Register a new category not associated to any device
+    const std::string categoryWoDeviceName = "some_category_without_device";
+    const Category* categoryWoDevice = nullptr;
+    BOOST_CHECK_NO_THROW(categoryWoDevice = counterDirectory.RegisterCategory(categoryWoDeviceName, 0));
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 2);
+    BOOST_CHECK(categoryWoDevice);
+    BOOST_CHECK(categoryWoDevice->m_Name == categoryWoDeviceName);
+    BOOST_CHECK(categoryWoDevice->m_Counters.empty());
+    BOOST_CHECK(categoryWoDevice->m_DeviceUid == 0);
+    BOOST_CHECK(categoryWoDevice->m_CounterSetUid == 0);
+
+    // Register a new category associated to an invalid device
+    const std::string categoryWInvalidDeviceName = "some_category_with_invalid_device";
+
+    ARMNN_NO_CONVERSION_WARN_BEGIN
+    uint16_t invalidDeviceUid = device->m_Uid + 10;
+    ARMNN_NO_CONVERSION_WARN_END
+
+    const Category* categoryWInvalidDevice = nullptr;
+    BOOST_CHECK_THROW(categoryWInvalidDevice
+                      = counterDirectory.RegisterCategory(categoryWInvalidDeviceName,
+                                                          invalidDeviceUid),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 2);
+    BOOST_CHECK(!categoryWInvalidDevice);
+
+    // Register a new category associated to a valid device
+    const std::string categoryWValidDeviceName = "some_category_with_valid_device";
+    const Category* categoryWValidDevice = nullptr;
+    BOOST_CHECK_NO_THROW(categoryWValidDevice
+                         = counterDirectory.RegisterCategory(categoryWValidDeviceName,
+                                                             device->m_Uid));
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 3);
+    BOOST_CHECK(categoryWValidDevice);
+    BOOST_CHECK(categoryWValidDevice != category);
+    BOOST_CHECK(categoryWValidDevice->m_Name == categoryWValidDeviceName);
+    BOOST_CHECK(categoryWValidDevice->m_DeviceUid == device->m_Uid);
+    BOOST_CHECK(categoryWValidDevice->m_CounterSetUid == 0);
+
+    // Register a counter set for testing
+    const std::string counterSetName = "some_counter_set";
+    const CounterSet* counterSet = nullptr;
+    BOOST_CHECK_NO_THROW(counterSet = counterDirectory.RegisterCounterSet(counterSetName));
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 1);
+    BOOST_CHECK(counterSet);
+    BOOST_CHECK(counterSet->m_Uid >= 1);
+    BOOST_CHECK(counterSet->m_Name == counterSetName);
+    BOOST_CHECK(counterSet->m_Count == 0);
+
+    // Register a new category not associated to any counter set
+    const std::string categoryWoCounterSetName = "some_category_without_counter_set";
+    const Category* categoryWoCounterSet = nullptr;
+    BOOST_CHECK_NO_THROW(categoryWoCounterSet
+                         = counterDirectory.RegisterCategory(categoryWoCounterSetName,
+                                                             armnn::EmptyOptional(),
+                                                             0));
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 4);
+    BOOST_CHECK(categoryWoCounterSet);
+    BOOST_CHECK(categoryWoCounterSet->m_Name == categoryWoCounterSetName);
+    BOOST_CHECK(categoryWoCounterSet->m_DeviceUid == 0);
+    BOOST_CHECK(categoryWoCounterSet->m_CounterSetUid == 0);
+
+    // Register a new category associated to an invalid counter set
+    const std::string categoryWInvalidCounterSetName = "some_category_with_invalid_counter_set";
+
+    ARMNN_NO_CONVERSION_WARN_BEGIN
+    uint16_t invalidCunterSetUid = counterSet->m_Uid + 10;
+    ARMNN_NO_CONVERSION_WARN_END
+
+    const Category* categoryWInvalidCounterSet = nullptr;
+    BOOST_CHECK_THROW(categoryWInvalidCounterSet
+                      = counterDirectory.RegisterCategory(categoryWInvalidCounterSetName,
+                                                          armnn::EmptyOptional(),
+                                                          invalidCunterSetUid),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 4);
+    BOOST_CHECK(!categoryWInvalidCounterSet);
+
+    // Register a new category associated to a valid counter set
+    const std::string categoryWValidCounterSetName = "some_category_with_valid_counter_set";
+    const Category* categoryWValidCounterSet = nullptr;
+    BOOST_CHECK_NO_THROW(categoryWValidCounterSet
+                         = counterDirectory.RegisterCategory(categoryWValidCounterSetName,
+                                                             armnn::EmptyOptional(),
+                                                             counterSet->m_Uid));
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 5);
+    BOOST_CHECK(categoryWValidCounterSet);
+    BOOST_CHECK(categoryWValidCounterSet != category);
+    BOOST_CHECK(categoryWValidCounterSet->m_Name == categoryWValidCounterSetName);
+    BOOST_CHECK(categoryWValidCounterSet->m_DeviceUid == 0);
+    BOOST_CHECK(categoryWValidCounterSet->m_CounterSetUid == counterSet->m_Uid);
+
+    // Register a new category associated to a valid device and counter set
+    const std::string categoryWValidDeviceAndValidCounterSetName = "some_category_with_valid_device_and_counter_set";
+    const Category* categoryWValidDeviceAndValidCounterSet = nullptr;
+    BOOST_CHECK_NO_THROW(categoryWValidDeviceAndValidCounterSet
+                         = counterDirectory.RegisterCategory(categoryWValidDeviceAndValidCounterSetName,
+                                                             device->m_Uid,
+                                                             counterSet->m_Uid));
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 6);
+    BOOST_CHECK(categoryWValidDeviceAndValidCounterSet);
+    BOOST_CHECK(categoryWValidDeviceAndValidCounterSet != category);
+    BOOST_CHECK(categoryWValidDeviceAndValidCounterSet->m_Name == categoryWValidDeviceAndValidCounterSetName);
+    BOOST_CHECK(categoryWValidDeviceAndValidCounterSet->m_DeviceUid == device->m_Uid);
+    BOOST_CHECK(categoryWValidDeviceAndValidCounterSet->m_CounterSetUid == counterSet->m_Uid);
+}
+
+BOOST_AUTO_TEST_CASE(CheckCounterDirectoryRegisterDevice)
+{
+    CounterDirectory counterDirectory;
+    BOOST_CHECK(counterDirectory.GetCategoryCount()   == 0);
+    BOOST_CHECK(counterDirectory.GetDeviceCount()     == 0);
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 0);
+    BOOST_CHECK(counterDirectory.GetCounterCount()    == 0);
+
+    // Register a device with an invalid name
+    const Device* noDevice = nullptr;
+    BOOST_CHECK_THROW(noDevice = counterDirectory.RegisterDevice(""), armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 0);
+    BOOST_CHECK(!noDevice);
+
+    // Register a device with an invalid name
+    BOOST_CHECK_THROW(noDevice = counterDirectory.RegisterDevice("inv@lid nam€"), armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 0);
+    BOOST_CHECK(!noDevice);
+
+    // Register a new device with no cores or parent category
+    const std::string deviceName = "some_device";
+    const Device* device = nullptr;
+    BOOST_CHECK_NO_THROW(device = counterDirectory.RegisterDevice(deviceName));
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 1);
+    BOOST_CHECK(device);
+    BOOST_CHECK(device->m_Name == deviceName);
+    BOOST_CHECK(device->m_Uid >= 1);
+    BOOST_CHECK(device->m_Cores == 0);
+
+    // Get the registered device
+    const Device* registeredDevice = counterDirectory.GetDevice(device->m_Uid);
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 1);
+    BOOST_CHECK(registeredDevice);
+    BOOST_CHECK(registeredDevice == device);
+
+    // Register a new device with cores and no parent category
+    const std::string deviceWCoresName = "some_device_with_cores";
+    const Device* deviceWCores = nullptr;
+    BOOST_CHECK_NO_THROW(deviceWCores = counterDirectory.RegisterDevice(deviceWCoresName, 2));
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 2);
+    BOOST_CHECK(deviceWCores);
+    BOOST_CHECK(deviceWCores->m_Name == deviceWCoresName);
+    BOOST_CHECK(deviceWCores->m_Uid >= 1);
+    BOOST_CHECK(deviceWCores->m_Uid > device->m_Uid);
+    BOOST_CHECK(deviceWCores->m_Cores == 2);
+
+    // Get the registered device
+    const Device* registeredDeviceWCores = counterDirectory.GetDevice(deviceWCores->m_Uid);
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 2);
+    BOOST_CHECK(registeredDeviceWCores);
+    BOOST_CHECK(registeredDeviceWCores == deviceWCores);
+    BOOST_CHECK(registeredDeviceWCores != device);
+
+    // Register a new device with cores and invalid parent category
+    const std::string deviceWCoresWInvalidParentCategoryName = "some_device_with_cores_with_invalid_parent_category";
+    const Device* deviceWCoresWInvalidParentCategory = nullptr;
+    BOOST_CHECK_THROW(deviceWCoresWInvalidParentCategory
+                      = counterDirectory.RegisterDevice(deviceWCoresWInvalidParentCategoryName,
+                                                        3,
+                                                        std::string("")),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 2);
+    BOOST_CHECK(!deviceWCoresWInvalidParentCategory);
+
+    // Register a new device with cores and invalid parent category
+    const std::string deviceWCoresWInvalidParentCategoryName2 = "some_device_with_cores_with_invalid_parent_category2";
+    const Device* deviceWCoresWInvalidParentCategory2 = nullptr;
+    BOOST_CHECK_THROW(deviceWCoresWInvalidParentCategory2
+                      = counterDirectory.RegisterDevice(deviceWCoresWInvalidParentCategoryName2,
+                                                        3,
+                                                        std::string("invalid_parent_category")),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 2);
+    BOOST_CHECK(!deviceWCoresWInvalidParentCategory2);
+
+    // Register a category for testing
+    const std::string categoryName = "some_category";
+    const Category* category = nullptr;
+    BOOST_CHECK_NO_THROW(category = counterDirectory.RegisterCategory(categoryName));
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 1);
+    BOOST_CHECK(category);
+    BOOST_CHECK(category->m_Name == categoryName);
+    BOOST_CHECK(category->m_Counters.empty());
+    BOOST_CHECK(category->m_DeviceUid == 0);
+    BOOST_CHECK(category->m_CounterSetUid == 0);
+
+    // Register a new device with cores and valid parent category
+    const std::string deviceWCoresWValidParentCategoryName = "some_device_with_cores_with_valid_parent_category";
+    const Device* deviceWCoresWValidParentCategory = nullptr;
+    BOOST_CHECK_NO_THROW(deviceWCoresWValidParentCategory
+                         = counterDirectory.RegisterDevice(deviceWCoresWValidParentCategoryName,
+                                                           4,
+                                                           categoryName));
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 3);
+    BOOST_CHECK(deviceWCoresWValidParentCategory);
+    BOOST_CHECK(deviceWCoresWValidParentCategory->m_Name == deviceWCoresWValidParentCategoryName);
+    BOOST_CHECK(deviceWCoresWValidParentCategory->m_Uid >= 1);
+    BOOST_CHECK(deviceWCoresWValidParentCategory->m_Uid > device->m_Uid);
+    BOOST_CHECK(deviceWCoresWValidParentCategory->m_Uid > deviceWCores->m_Uid);
+    BOOST_CHECK(deviceWCoresWValidParentCategory->m_Cores == 4);
+    BOOST_CHECK(category->m_DeviceUid == deviceWCoresWValidParentCategory->m_Uid);
+}
+
+BOOST_AUTO_TEST_CASE(CheckCounterDirectoryRegisterCounterSet)
+{
+    CounterDirectory counterDirectory;
+    BOOST_CHECK(counterDirectory.GetCategoryCount()   == 0);
+    BOOST_CHECK(counterDirectory.GetDeviceCount()     == 0);
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 0);
+    BOOST_CHECK(counterDirectory.GetCounterCount()    == 0);
+
+    // Register a counter set with an invalid name
+    const CounterSet* noCounterSet = nullptr;
+    BOOST_CHECK_THROW(noCounterSet = counterDirectory.RegisterCounterSet(""), armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 0);
+    BOOST_CHECK(!noCounterSet);
+
+    // Register a counter set with an invalid name
+    BOOST_CHECK_THROW(noCounterSet = counterDirectory.RegisterCounterSet("invalid name"),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 0);
+    BOOST_CHECK(!noCounterSet);
+
+    // Register a new counter set with no count or parent category
+    const std::string counterSetName = "some_counter_set";
+    const CounterSet* counterSet = nullptr;
+    BOOST_CHECK_NO_THROW(counterSet = counterDirectory.RegisterCounterSet(counterSetName));
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 1);
+    BOOST_CHECK(counterSet);
+    BOOST_CHECK(counterSet->m_Name == counterSetName);
+    BOOST_CHECK(counterSet->m_Uid >= 1);
+    BOOST_CHECK(counterSet->m_Count == 0);
+
+    // Get the registered counter set
+    const CounterSet* registeredCounterSet = counterDirectory.GetCounterSet(counterSet->m_Uid);
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 1);
+    BOOST_CHECK(registeredCounterSet);
+    BOOST_CHECK(registeredCounterSet == counterSet);
+
+    // Register a new counter set with count and no parent category
+    const std::string counterSetWCountName = "some_counter_set_with_count";
+    const CounterSet* counterSetWCount = nullptr;
+    BOOST_CHECK_NO_THROW(counterSetWCount = counterDirectory.RegisterCounterSet(counterSetWCountName, 37));
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 2);
+    BOOST_CHECK(counterSetWCount);
+    BOOST_CHECK(counterSetWCount->m_Name == counterSetWCountName);
+    BOOST_CHECK(counterSetWCount->m_Uid >= 1);
+    BOOST_CHECK(counterSetWCount->m_Uid > counterSet->m_Uid);
+    BOOST_CHECK(counterSetWCount->m_Count == 37);
+
+    // Get the registered counter set
+    const CounterSet* registeredCounterSetWCount = counterDirectory.GetCounterSet(counterSetWCount->m_Uid);
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 2);
+    BOOST_CHECK(registeredCounterSetWCount);
+    BOOST_CHECK(registeredCounterSetWCount == counterSetWCount);
+    BOOST_CHECK(registeredCounterSetWCount != counterSet);
+
+    // Register a new counter set with count and invalid parent category
+    const std::string counterSetWCountWInvalidParentCategoryName = "some_counter_set_with_count_"
+                                                                   "with_invalid_parent_category";
+    const CounterSet* counterSetWCountWInvalidParentCategory = nullptr;
+    BOOST_CHECK_THROW(counterSetWCountWInvalidParentCategory
+                      = counterDirectory.RegisterCounterSet(counterSetWCountWInvalidParentCategoryName,
+                                                            42,
+                                                            std::string("")),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 2);
+    BOOST_CHECK(!counterSetWCountWInvalidParentCategory);
+
+    // Register a new counter set with count and invalid parent category
+    const std::string counterSetWCountWInvalidParentCategoryName2 = "some_counter_set_with_count_"
+                                                                    "with_invalid_parent_category2";
+    const CounterSet* counterSetWCountWInvalidParentCategory2 = nullptr;
+    BOOST_CHECK_THROW(counterSetWCountWInvalidParentCategory2
+                      = counterDirectory.RegisterCounterSet(counterSetWCountWInvalidParentCategoryName2,
+                                                            42,
+                                                            std::string("invalid_parent_category")),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 2);
+    BOOST_CHECK(!counterSetWCountWInvalidParentCategory2);
+
+    // Register a category for testing
+    const std::string categoryName = "some_category";
+    const Category* category = nullptr;
+    BOOST_CHECK_NO_THROW(category = counterDirectory.RegisterCategory(categoryName));
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 1);
+    BOOST_CHECK(category);
+    BOOST_CHECK(category->m_Name == categoryName);
+    BOOST_CHECK(category->m_Counters.empty());
+    BOOST_CHECK(category->m_DeviceUid == 0);
+    BOOST_CHECK(category->m_CounterSetUid == 0);
+
+    // Register a new counter set with count and valid parent category
+    const std::string counterSetWCountWValidParentCategoryName = "some_counter_set_with_count_"
+                                                                 "with_valid_parent_category";
+    const CounterSet* counterSetWCountWValidParentCategory = nullptr;
+    BOOST_CHECK_NO_THROW(counterSetWCountWValidParentCategory
+                         = counterDirectory.RegisterCounterSet(counterSetWCountWValidParentCategoryName,
+                                                               42,
+                                                               std::string(categoryName)));
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 3);
+    BOOST_CHECK(counterSetWCountWValidParentCategory);
+    BOOST_CHECK(counterSetWCountWValidParentCategory->m_Name == counterSetWCountWValidParentCategoryName);
+    BOOST_CHECK(counterSetWCountWValidParentCategory->m_Uid >= 1);
+    BOOST_CHECK(counterSetWCountWValidParentCategory->m_Uid > counterSet->m_Uid);
+    BOOST_CHECK(counterSetWCountWValidParentCategory->m_Uid > counterSetWCount->m_Uid);
+    BOOST_CHECK(counterSetWCountWValidParentCategory->m_Count == 42);
+    BOOST_CHECK(category->m_CounterSetUid == counterSetWCountWValidParentCategory->m_Uid);
+}
+
+BOOST_AUTO_TEST_CASE(CheckCounterDirectoryRegisterCounter)
+{
+    CounterDirectory counterDirectory;
+    BOOST_CHECK(counterDirectory.GetCategoryCount()   == 0);
+    BOOST_CHECK(counterDirectory.GetDeviceCount()     == 0);
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 0);
+    BOOST_CHECK(counterDirectory.GetCounterCount()    == 0);
+
+    // Register a counter with an invalid parent category name
+    const Counter* noCounter = nullptr;
+    BOOST_CHECK_THROW(noCounter = counterDirectory.RegisterCounter("",
+                                                                   0,
+                                                                   1,
+                                                                   123.45f,
+                                                                   "valid name",
+                                                                   "valid description"),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 0);
+    BOOST_CHECK(!noCounter);
+
+    // Register a counter with an invalid parent category name
+    BOOST_CHECK_THROW(noCounter = counterDirectory.RegisterCounter("invalid parent category",
+                                                                   0,
+                                                                   1,
+                                                                   123.45f,
+                                                                   "valid name",
+                                                                   "valid description"),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 0);
+    BOOST_CHECK(!noCounter);
+
+    // Register a counter with an invalid class
+    BOOST_CHECK_THROW(noCounter = counterDirectory.RegisterCounter("valid_parent_category",
+                                                                   2,
+                                                                   1,
+                                                                   123.45f,
+                                                                   "valid name",
+                                                                   "valid description"),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 0);
+    BOOST_CHECK(!noCounter);
+
+    // Register a counter with an invalid interpolation
+    BOOST_CHECK_THROW(noCounter = counterDirectory.RegisterCounter("valid_parent_category",
+                                                                   0,
+                                                                   3,
+                                                                   123.45f,
+                                                                   "valid name",
+                                                                   "valid description"),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 0);
+    BOOST_CHECK(!noCounter);
+
+    // Register a counter with an invalid multiplier
+    BOOST_CHECK_THROW(noCounter = counterDirectory.RegisterCounter("valid_parent_category",
+                                                                   0,
+                                                                   1,
+                                                                   .0f,
+                                                                   "valid name",
+                                                                   "valid description"),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 0);
+    BOOST_CHECK(!noCounter);
+
+    // Register a counter with an invalid name
+    BOOST_CHECK_THROW(noCounter = counterDirectory.RegisterCounter("valid_parent_category",
+                                                                   0,
+                                                                   1,
+                                                                   123.45f,
+                                                                   "",
+                                                                   "valid description"),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 0);
+    BOOST_CHECK(!noCounter);
+
+    // Register a counter with an invalid name
+    BOOST_CHECK_THROW(noCounter = counterDirectory.RegisterCounter("valid_parent_category",
+                                                                   0,
+                                                                   1,
+                                                                   123.45f,
+                                                                   "invalid nam€",
+                                                                   "valid description"),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 0);
+    BOOST_CHECK(!noCounter);
+
+    // Register a counter with an invalid description
+    BOOST_CHECK_THROW(noCounter = counterDirectory.RegisterCounter("valid_parent_category",
+                                                                   0,
+                                                                   1,
+                                                                   123.45f,
+                                                                   "valid name",
+                                                                   ""),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 0);
+    BOOST_CHECK(!noCounter);
+
+    // Register a counter with an invalid description
+    BOOST_CHECK_THROW(noCounter = counterDirectory.RegisterCounter("valid_parent_category",
+                                                                   0,
+                                                                   1,
+                                                                   123.45f,
+                                                                   "valid name",
+                                                                   "inv@lid description"),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 0);
+    BOOST_CHECK(!noCounter);
+
+    // Register a counter with an invalid unit2
+    BOOST_CHECK_THROW(noCounter = counterDirectory.RegisterCounter("valid_parent_category",
+                                                                   0,
+                                                                   1,
+                                                                   123.45f,
+                                                                   "valid name",
+                                                                   "valid description",
+                                                                   std::string("Mb/s2")),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 0);
+    BOOST_CHECK(!noCounter);
+
+    // Register a counter with a non-existing parent category name
+    BOOST_CHECK_THROW(noCounter = counterDirectory.RegisterCounter("invalid_parent_category",
+                                                                   0,
+                                                                   1,
+                                                                   123.45f,
+                                                                   "valid name",
+                                                                   "valid description"),
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 0);
+    BOOST_CHECK(!noCounter);
+
+    // Register a category for testing
+    const std::string categoryName = "some_category";
+    const Category* category = nullptr;
+    BOOST_CHECK_NO_THROW(category = counterDirectory.RegisterCategory(categoryName));
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 1);
+    BOOST_CHECK(category);
+    BOOST_CHECK(category->m_Name == categoryName);
+    BOOST_CHECK(category->m_Counters.empty());
+    BOOST_CHECK(category->m_DeviceUid == 0);
+    BOOST_CHECK(category->m_CounterSetUid == 0);
+
+    // Register a counter with a valid parent category name
+    const Counter* counter = nullptr;
+    BOOST_CHECK_NO_THROW(counter = counterDirectory.RegisterCounter(categoryName,
+                                                                    0,
+                                                                    1,
+                                                                    123.45f,
+                                                                    "valid name",
+                                                                    "valid description"));
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 1);
+    BOOST_CHECK(counter);
+    BOOST_CHECK(counter->m_Uid >= 0);
+    BOOST_CHECK(counter->m_MaxCounterUid == counter->m_Uid);
+    BOOST_CHECK(counter->m_Class == 0);
+    BOOST_CHECK(counter->m_Interpolation == 1);
+    BOOST_CHECK(counter->m_Multiplier == 123.45f);
+    BOOST_CHECK(counter->m_Name == "valid name");
+    BOOST_CHECK(counter->m_Description == "valid description");
+    BOOST_CHECK(counter->m_Units == "");
+    BOOST_CHECK(counter->m_DeviceUid == 0);
+    BOOST_CHECK(counter->m_CounterSetUid == 0);
+    BOOST_CHECK(category->m_Counters.size() == 1);
+    BOOST_CHECK(category->m_Counters.back() == counter->m_Uid);
+
+    // Register a counter with a valid parent category name and units
+    const Counter* counterWUnits = nullptr;
+    BOOST_CHECK_NO_THROW(counterWUnits = counterDirectory.RegisterCounter(categoryName,
+                                                                          0,
+                                                                          1,
+                                                                          123.45f,
+                                                                          "valid name 2",
+                                                                          "valid description",
+                                                                          std::string("Mnnsq2"))); // Units
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 2);
+    BOOST_CHECK(counterWUnits);
+    BOOST_CHECK(counterWUnits->m_Uid >= 0);
+    BOOST_CHECK(counterWUnits->m_Uid > counter->m_Uid);
+    BOOST_CHECK(counterWUnits->m_MaxCounterUid == counterWUnits->m_Uid);
+    BOOST_CHECK(counterWUnits->m_Class == 0);
+    BOOST_CHECK(counterWUnits->m_Interpolation == 1);
+    BOOST_CHECK(counterWUnits->m_Multiplier == 123.45f);
+    BOOST_CHECK(counterWUnits->m_Name == "valid name 2");
+    BOOST_CHECK(counterWUnits->m_Description == "valid description");
+    BOOST_CHECK(counterWUnits->m_Units == "Mnnsq2");
+    BOOST_CHECK(counterWUnits->m_DeviceUid == 0);
+    BOOST_CHECK(counterWUnits->m_CounterSetUid == 0);
+    BOOST_CHECK(category->m_Counters.size() == 2);
+    BOOST_CHECK(category->m_Counters.back() == counterWUnits->m_Uid);
+
+    // Register a counter with a valid parent category name and not associated with a device
+    const Counter* counterWoDevice = nullptr;
+    BOOST_CHECK_NO_THROW(counterWoDevice = counterDirectory.RegisterCounter(categoryName,
+                                                                            0,
+                                                                            1,
+                                                                            123.45f,
+                                                                            "valid name 3",
+                                                                            "valid description",
+                                                                            armnn::EmptyOptional(), // Units
+                                                                            armnn::EmptyOptional(), // Number of cores
+                                                                            0));                    // Device UID
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 3);
+    BOOST_CHECK(counterWoDevice);
+    BOOST_CHECK(counterWoDevice->m_Uid >= 0);
+    BOOST_CHECK(counterWoDevice->m_Uid > counter->m_Uid);
+    BOOST_CHECK(counterWoDevice->m_MaxCounterUid == counterWoDevice->m_Uid);
+    BOOST_CHECK(counterWoDevice->m_Class == 0);
+    BOOST_CHECK(counterWoDevice->m_Interpolation == 1);
+    BOOST_CHECK(counterWoDevice->m_Multiplier == 123.45f);
+    BOOST_CHECK(counterWoDevice->m_Name == "valid name 3");
+    BOOST_CHECK(counterWoDevice->m_Description == "valid description");
+    BOOST_CHECK(counterWoDevice->m_Units == "");
+    BOOST_CHECK(counterWoDevice->m_DeviceUid == 0);
+    BOOST_CHECK(counterWoDevice->m_CounterSetUid == 0);
+    BOOST_CHECK(category->m_Counters.size() == 3);
+    BOOST_CHECK(category->m_Counters.back() == counterWoDevice->m_Uid);
+
+    // Register a counter with a valid parent category name and associated to an invalid device
+    BOOST_CHECK_THROW(noCounter = counterDirectory.RegisterCounter(categoryName,
+                                                                   0,
+                                                                   1,
+                                                                   123.45f,
+                                                                   "valid name 4",
+                                                                   "valid description",
+                                                                   armnn::EmptyOptional(), // Units
+                                                                   armnn::EmptyOptional(), // Number of cores
+                                                                   100),                   // Device UID
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 3);
+    BOOST_CHECK(!noCounter);
+
+    // Register a device for testing
+    const std::string deviceName = "some_device";
+    const Device* device = nullptr;
+    BOOST_CHECK_NO_THROW(device = counterDirectory.RegisterDevice(deviceName));
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 1);
+    BOOST_CHECK(device);
+    BOOST_CHECK(device->m_Name == deviceName);
+    BOOST_CHECK(device->m_Uid >= 1);
+    BOOST_CHECK(device->m_Cores == 0);
+
+    // Register a counter with a valid parent category name and associated to a device
+    const Counter* counterWDevice = nullptr;
+    BOOST_CHECK_NO_THROW(counterWDevice = counterDirectory.RegisterCounter(categoryName,
+                                                                           0,
+                                                                           1,
+                                                                           123.45f,
+                                                                           "valid name 5",
+                                                                           "valid description",
+                                                                           armnn::EmptyOptional(), // Units
+                                                                           armnn::EmptyOptional(), // Number of cores
+                                                                           device->m_Uid));        // Device UID
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 4);
+    BOOST_CHECK(counterWDevice);
+    BOOST_CHECK(counterWDevice->m_Uid >= 0);
+    BOOST_CHECK(counterWDevice->m_Uid > counter->m_Uid);
+    BOOST_CHECK(counterWDevice->m_MaxCounterUid == counterWDevice->m_Uid);
+    BOOST_CHECK(counterWDevice->m_Class == 0);
+    BOOST_CHECK(counterWDevice->m_Interpolation == 1);
+    BOOST_CHECK(counterWDevice->m_Multiplier == 123.45f);
+    BOOST_CHECK(counterWDevice->m_Name == "valid name 5");
+    BOOST_CHECK(counterWDevice->m_Description == "valid description");
+    BOOST_CHECK(counterWDevice->m_Units == "");
+    BOOST_CHECK(counterWDevice->m_DeviceUid == device->m_Uid);
+    BOOST_CHECK(counterWDevice->m_CounterSetUid == 0);
+    BOOST_CHECK(category->m_Counters.size() == 4);
+    BOOST_CHECK(category->m_Counters.back() == counterWDevice->m_Uid);
+
+    // Register a counter with a valid parent category name and not associated with a counter set
+    const Counter* counterWoCounterSet = nullptr;
+    BOOST_CHECK_NO_THROW(counterWoCounterSet
+                         = counterDirectory.RegisterCounter(categoryName,
+                                                            0,
+                                                            1,
+                                                            123.45f,
+                                                            "valid name 6",
+                                                            "valid description",
+                                                            armnn::EmptyOptional(), // Units
+                                                            armnn::EmptyOptional(), // Number of cores
+                                                            armnn::EmptyOptional(), // Device UID
+                                                            0));                    // Counter set UID
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 5);
+    BOOST_CHECK(counterWoCounterSet);
+    BOOST_CHECK(counterWoCounterSet->m_Uid >= 0);
+    BOOST_CHECK(counterWoCounterSet->m_Uid > counter->m_Uid);
+    BOOST_CHECK(counterWoCounterSet->m_MaxCounterUid == counterWoCounterSet->m_Uid);
+    BOOST_CHECK(counterWoCounterSet->m_Class == 0);
+    BOOST_CHECK(counterWoCounterSet->m_Interpolation == 1);
+    BOOST_CHECK(counterWoCounterSet->m_Multiplier == 123.45f);
+    BOOST_CHECK(counterWoCounterSet->m_Name == "valid name 6");
+    BOOST_CHECK(counterWoCounterSet->m_Description == "valid description");
+    BOOST_CHECK(counterWoCounterSet->m_Units == "");
+    BOOST_CHECK(counterWoCounterSet->m_DeviceUid == 0);
+    BOOST_CHECK(counterWoCounterSet->m_CounterSetUid == 0);
+    BOOST_CHECK(category->m_Counters.size() == 5);
+    BOOST_CHECK(category->m_Counters.back() == counterWoCounterSet->m_Uid);
+
+    // Register a counter with a valid parent category name and associated to an invalid counter set
+    BOOST_CHECK_THROW(noCounter = counterDirectory.RegisterCounter(categoryName,
+                                                                   0,
+                                                                   1,
+                                                                   123.45f,
+                                                                   "valid name 7",
+                                                                   "valid description",
+                                                                   armnn::EmptyOptional(), // Units
+                                                                   armnn::EmptyOptional(), // Number of cores
+                                                                   armnn::EmptyOptional(), // Device UID
+                                                                   100),                   // Counter set UID
+                      armnn::InvalidArgumentException);
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 5);
+    BOOST_CHECK(!noCounter);
+
+    // Register a counter with a valid parent category name and with a given number of cores
+    const Counter* counterWNumberOfCores = nullptr;
+    uint16_t numberOfCores = 15;
+    BOOST_CHECK_NO_THROW(counterWNumberOfCores
+                         = counterDirectory.RegisterCounter(categoryName,
+                                                            0,
+                                                            1,
+                                                            123.45f,
+                                                            "valid name 8",
+                                                            "valid description",
+                                                            armnn::EmptyOptional(),   // Units
+                                                            numberOfCores,            // Number of cores
+                                                            armnn::EmptyOptional(),   // Device UID
+                                                            armnn::EmptyOptional())); // Counter set UID
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 20);
+    BOOST_CHECK(counterWNumberOfCores);
+    BOOST_CHECK(counterWNumberOfCores->m_Uid >= 0);
+    BOOST_CHECK(counterWNumberOfCores->m_Uid > counter->m_Uid);
+    BOOST_CHECK(counterWNumberOfCores->m_MaxCounterUid == counterWNumberOfCores->m_Uid + numberOfCores - 1);
+    BOOST_CHECK(counterWNumberOfCores->m_Class == 0);
+    BOOST_CHECK(counterWNumberOfCores->m_Interpolation == 1);
+    BOOST_CHECK(counterWNumberOfCores->m_Multiplier == 123.45f);
+    BOOST_CHECK(counterWNumberOfCores->m_Name == "valid name 8");
+    BOOST_CHECK(counterWNumberOfCores->m_Description == "valid description");
+    BOOST_CHECK(counterWNumberOfCores->m_Units == "");
+    BOOST_CHECK(counterWNumberOfCores->m_DeviceUid == 0);
+    BOOST_CHECK(counterWNumberOfCores->m_CounterSetUid == 0);
+    BOOST_CHECK(category->m_Counters.size() == 20);
+    for (size_t i = 0; i < numberOfCores; i ++)
+    {
+        BOOST_CHECK(category->m_Counters[category->m_Counters.size() - numberOfCores + i] ==
+                    counterWNumberOfCores->m_Uid + i);
+    }
+
+    // Register a multi-core device for testing
+    const std::string multiCoreDeviceName = "some_multi_core_device";
+    const Device* multiCoreDevice = nullptr;
+    BOOST_CHECK_NO_THROW(multiCoreDevice = counterDirectory.RegisterDevice(multiCoreDeviceName, 4));
+    BOOST_CHECK(counterDirectory.GetDeviceCount() == 2);
+    BOOST_CHECK(multiCoreDevice);
+    BOOST_CHECK(multiCoreDevice->m_Name == multiCoreDeviceName);
+    BOOST_CHECK(multiCoreDevice->m_Uid >= 1);
+    BOOST_CHECK(multiCoreDevice->m_Cores == 4);
+
+    // Register a counter with a valid parent category name and associated to the multi-core device
+    const Counter* counterWMultiCoreDevice = nullptr;
+    BOOST_CHECK_NO_THROW(counterWMultiCoreDevice
+                         = counterDirectory.RegisterCounter(categoryName,
+                                                            0,
+                                                            1,
+                                                            123.45f,
+                                                            "valid name 9",
+                                                            "valid description",
+                                                            armnn::EmptyOptional(),   // Units
+                                                            armnn::EmptyOptional(),   // Number of cores
+                                                            multiCoreDevice->m_Uid,   // Device UID
+                                                            armnn::EmptyOptional())); // Counter set UID
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 24);
+    BOOST_CHECK(counterWMultiCoreDevice);
+    BOOST_CHECK(counterWMultiCoreDevice->m_Uid >= 0);
+    BOOST_CHECK(counterWMultiCoreDevice->m_Uid > counter->m_Uid);
+    BOOST_CHECK(counterWMultiCoreDevice->m_MaxCounterUid ==
+                counterWMultiCoreDevice->m_Uid + multiCoreDevice->m_Cores - 1);
+    BOOST_CHECK(counterWMultiCoreDevice->m_Class == 0);
+    BOOST_CHECK(counterWMultiCoreDevice->m_Interpolation == 1);
+    BOOST_CHECK(counterWMultiCoreDevice->m_Multiplier == 123.45f);
+    BOOST_CHECK(counterWMultiCoreDevice->m_Name == "valid name 9");
+    BOOST_CHECK(counterWMultiCoreDevice->m_Description == "valid description");
+    BOOST_CHECK(counterWMultiCoreDevice->m_Units == "");
+    BOOST_CHECK(counterWMultiCoreDevice->m_DeviceUid == multiCoreDevice->m_Uid);
+    BOOST_CHECK(counterWMultiCoreDevice->m_CounterSetUid == 0);
+    BOOST_CHECK(category->m_Counters.size() == 24);
+    for (size_t i = 0; i < 4; i ++)
+    {
+        BOOST_CHECK(category->m_Counters[category->m_Counters.size() - 4 + i] == counterWMultiCoreDevice->m_Uid + i);
+    }
+
+    // Register a counter set for testing
+    const std::string counterSetName = "some_counter_set";
+    const CounterSet* counterSet = nullptr;
+    BOOST_CHECK_NO_THROW(counterSet = counterDirectory.RegisterCounterSet(counterSetName));
+    BOOST_CHECK(counterDirectory.GetCounterSetCount() == 1);
+    BOOST_CHECK(counterSet);
+    BOOST_CHECK(counterSet->m_Name == counterSetName);
+    BOOST_CHECK(counterSet->m_Uid >= 1);
+    BOOST_CHECK(counterSet->m_Count == 0);
+
+    // Register a counter with a valid parent category name and associated to a counter set
+    const Counter* counterWCounterSet = nullptr;
+    BOOST_CHECK_NO_THROW(counterWCounterSet
+                         = counterDirectory.RegisterCounter(categoryName,
+                                                            0,
+                                                            1,
+                                                            123.45f,
+                                                            "valid name 10",
+                                                            "valid description",
+                                                            armnn::EmptyOptional(), // Units
+                                                            armnn::EmptyOptional(), // Number of cores
+                                                            armnn::EmptyOptional(), // Device UID
+                                                            counterSet->m_Uid));    // Counter set UID
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 25);
+    BOOST_CHECK(counterWCounterSet);
+    BOOST_CHECK(counterWCounterSet->m_Uid >= 0);
+    BOOST_CHECK(counterWCounterSet->m_Uid > counter->m_Uid);
+    BOOST_CHECK(counterWCounterSet->m_MaxCounterUid == counterWCounterSet->m_Uid);
+    BOOST_CHECK(counterWCounterSet->m_Class == 0);
+    BOOST_CHECK(counterWCounterSet->m_Interpolation == 1);
+    BOOST_CHECK(counterWCounterSet->m_Multiplier == 123.45f);
+    BOOST_CHECK(counterWCounterSet->m_Name == "valid name 10");
+    BOOST_CHECK(counterWCounterSet->m_Description == "valid description");
+    BOOST_CHECK(counterWCounterSet->m_Units == "");
+    BOOST_CHECK(counterWCounterSet->m_DeviceUid == 0);
+    BOOST_CHECK(counterWCounterSet->m_CounterSetUid == counterSet->m_Uid);
+    BOOST_CHECK(category->m_Counters.size() == 25);
+    BOOST_CHECK(category->m_Counters.back() == counterWCounterSet->m_Uid);
+
+    // Register a counter with a valid parent category name and associated to a device and a counter set
+    const Counter* counterWDeviceWCounterSet = nullptr;
+    BOOST_CHECK_NO_THROW(counterWDeviceWCounterSet
+                         = counterDirectory.RegisterCounter(categoryName,
+                                                            0,
+                                                            1,
+                                                            123.45f,
+                                                            "valid name 11",
+                                                            "valid description",
+                                                            armnn::EmptyOptional(), // Units
+                                                            armnn::EmptyOptional(), // Number of cores
+                                                            device->m_Uid,          // Device UID
+                                                            counterSet->m_Uid));    // Counter set UID
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 26);
+    BOOST_CHECK(counterWDeviceWCounterSet);
+    BOOST_CHECK(counterWDeviceWCounterSet->m_Uid >= 0);
+    BOOST_CHECK(counterWDeviceWCounterSet->m_Uid > counter->m_Uid);
+    BOOST_CHECK(counterWDeviceWCounterSet->m_MaxCounterUid == counterWDeviceWCounterSet->m_Uid);
+    BOOST_CHECK(counterWDeviceWCounterSet->m_Class == 0);
+    BOOST_CHECK(counterWDeviceWCounterSet->m_Interpolation == 1);
+    BOOST_CHECK(counterWDeviceWCounterSet->m_Multiplier == 123.45f);
+    BOOST_CHECK(counterWDeviceWCounterSet->m_Name == "valid name 11");
+    BOOST_CHECK(counterWDeviceWCounterSet->m_Description == "valid description");
+    BOOST_CHECK(counterWDeviceWCounterSet->m_Units == "");
+    BOOST_CHECK(counterWDeviceWCounterSet->m_DeviceUid == device->m_Uid);
+    BOOST_CHECK(counterWDeviceWCounterSet->m_CounterSetUid == counterSet->m_Uid);
+    BOOST_CHECK(category->m_Counters.size() == 26);
+    BOOST_CHECK(category->m_Counters.back() == counterWDeviceWCounterSet->m_Uid);
+
+    // Register another category for testing
+    const std::string anotherCategoryName = "some_other_category";
+    const Category* anotherCategory = nullptr;
+    BOOST_CHECK_NO_THROW(anotherCategory = counterDirectory.RegisterCategory(anotherCategoryName));
+    BOOST_CHECK(counterDirectory.GetCategoryCount() == 2);
+    BOOST_CHECK(anotherCategory);
+    BOOST_CHECK(anotherCategory != category);
+    BOOST_CHECK(anotherCategory->m_Name == anotherCategoryName);
+    BOOST_CHECK(anotherCategory->m_Counters.empty());
+    BOOST_CHECK(anotherCategory->m_DeviceUid == 0);
+    BOOST_CHECK(anotherCategory->m_CounterSetUid == 0);
+
+    // Register a counter to the other category
+    const Counter* anotherCounter = nullptr;
+    BOOST_CHECK_NO_THROW(anotherCounter = counterDirectory.RegisterCounter(anotherCategoryName,
+                                                                           1,
+                                                                           0,
+                                                                           .00043f,
+                                                                           "valid name",
+                                                                           "valid description",
+                                                                           armnn::EmptyOptional(), // Units
+                                                                           armnn::EmptyOptional(), // Number of cores
+                                                                           device->m_Uid,          // Device UID
+                                                                           counterSet->m_Uid));    // Counter set UID
+    BOOST_CHECK(counterDirectory.GetCounterCount() == 27);
+    BOOST_CHECK(anotherCounter);
+    BOOST_CHECK(anotherCounter->m_Uid >= 0);
+    BOOST_CHECK(anotherCounter->m_MaxCounterUid == anotherCounter->m_Uid);
+    BOOST_CHECK(anotherCounter->m_Class == 1);
+    BOOST_CHECK(anotherCounter->m_Interpolation == 0);
+    BOOST_CHECK(anotherCounter->m_Multiplier == .00043f);
+    BOOST_CHECK(anotherCounter->m_Name == "valid name");
+    BOOST_CHECK(anotherCounter->m_Description == "valid description");
+    BOOST_CHECK(anotherCounter->m_Units == "");
+    BOOST_CHECK(anotherCounter->m_DeviceUid == device->m_Uid);
+    BOOST_CHECK(anotherCounter->m_CounterSetUid == counterSet->m_Uid);
+    BOOST_CHECK(anotherCategory->m_Counters.size() == 1);
+    BOOST_CHECK(anotherCategory->m_Counters.back() == anotherCounter->m_Uid);
 }
 
 BOOST_AUTO_TEST_CASE(CounterSelectionCommandHandlerParseData)
diff --git a/src/profiling/test/SendCounterPacketTests.cpp b/src/profiling/test/SendCounterPacketTests.cpp
index 4435ab6..c060f16 100644
--- a/src/profiling/test/SendCounterPacketTests.cpp
+++ b/src/profiling/test/SendCounterPacketTests.cpp
@@ -3,11 +3,12 @@
 // SPDX-License-Identifier: MIT
 //
 
-#include "../ProfilingUtils.hpp"
-#include "../EncodeVersion.hpp"
-#include "../SendCounterPacket.hpp"
 #include "SendCounterPacketTests.hpp"
 
+#include <ProfilingUtils.hpp>
+#include <EncodeVersion.hpp>
+#include <SendCounterPacket.hpp>
+
 #include <armnn/Exceptions.hpp>
 
 #include <boost/test/unit_test.hpp>
@@ -30,7 +31,7 @@
 
     BOOST_TEST(strcmp(buffer, "SendStreamMetaDataPacket") == 0);
 
-    CounterDirectory counterDirectory(1, "counter_directory", 0, 0, 0);
+    CounterDirectory counterDirectory;
     sendCounterPacket.SendCounterDirectoryPacket(counterDirectory);
 
     BOOST_TEST(strcmp(buffer, "SendCounterDirectoryPacket") == 0);