IVGCVSW-6851 Move DirectoryCaptureCommandHandler to the profiling server library

Change-Id: Ib14fdcca15f40fedc0f992b0fd882458dc58c9ba
Signed-off-by: Jim Flynn <jim.flynn@arm.com>
diff --git a/profiling/common/src/CMakeLists.txt b/profiling/common/src/CMakeLists.txt
index e02c785..264902c 100644
--- a/profiling/common/src/CMakeLists.txt
+++ b/profiling/common/src/CMakeLists.txt
@@ -10,6 +10,7 @@
         CommandHandlerKey.cpp
         CommandHandlerRegistry.cpp
         CommonProfilingUtils.cpp
+        CounterDirectory.cpp
         LabelsAndEventClasses.cpp
         Logging.cpp
         NetworkSockets.cpp
diff --git a/profiling/common/src/CommonProfilingUtils.cpp b/profiling/common/src/CommonProfilingUtils.cpp
index fe98e0a..01bc461 100644
--- a/profiling/common/src/CommonProfilingUtils.cpp
+++ b/profiling/common/src/CommonProfilingUtils.cpp
@@ -5,7 +5,10 @@
 
 #include <common/include/Assert.hpp>
 #include <common/include/CommonProfilingUtils.hpp>
+#include <common/include/ProfilingException.hpp>
 
+#include <iostream>
+#include <limits>
 #include <sstream>
 
 namespace arm
@@ -140,6 +143,242 @@
     return outputStream.str();
 }
 
+void PrintDeviceDetails(const std::pair<const unsigned short, std::unique_ptr<Device>>& devicePair)
+{
+    std::string body;
+
+    body.append(CentreAlignFormatting(devicePair.second->m_Name, 20));
+    body.append(" | ");
+    body.append(CentreAlignFormatting(std::to_string(devicePair.first), 13));
+    body.append(" | ");
+    body.append(CentreAlignFormatting(std::to_string(devicePair.second->m_Cores), 10));
+    body.append("\n");
+
+    std::cout << std::string(body.size(), '-') << "\n";
+    std::cout<< body;
+}
+
+void PrintCounterSetDetails(const std::pair<const unsigned short, std::unique_ptr<CounterSet>>& counterSetPair)
+{
+    std::string body;
+
+    body.append(CentreAlignFormatting(counterSetPair.second->m_Name, 20));
+    body.append(" | ");
+    body.append(CentreAlignFormatting(std::to_string(counterSetPair.first), 13));
+    body.append(" | ");
+    body.append(CentreAlignFormatting(std::to_string(counterSetPair.second->m_Count), 10));
+    body.append("\n");
+
+    std::cout << std::string(body.size(), '-') << "\n";
+
+    std::cout<< body;
+}
+
+void PrintCounterDetails(std::shared_ptr<Counter>& counter)
+{
+    std::string body;
+
+    body.append(CentreAlignFormatting(counter->m_Name, 20));
+    body.append(" | ");
+    body.append(CentreAlignFormatting(counter->m_Description, 50));
+    body.append(" | ");
+    body.append(CentreAlignFormatting(counter->m_Units, 14));
+    body.append(" | ");
+    body.append(CentreAlignFormatting(std::to_string(counter->m_Uid), 6));
+    body.append(" | ");
+    body.append(CentreAlignFormatting(std::to_string(counter->m_MaxCounterUid), 10));
+    body.append(" | ");
+    body.append(CentreAlignFormatting(std::to_string(counter->m_Class), 8));
+    body.append(" | ");
+    body.append(CentreAlignFormatting(std::to_string(counter->m_Interpolation), 14));
+    body.append(" | ");
+    body.append(CentreAlignFormatting(std::to_string(counter->m_Multiplier), 20));
+    body.append(" | ");
+    body.append(CentreAlignFormatting(std::to_string(counter->m_CounterSetUid), 16));
+    body.append(" | ");
+    body.append(CentreAlignFormatting(std::to_string(counter->m_DeviceUid), 14));
+
+    body.append("\n");
+
+    std::cout << std::string(body.size(), '-') << "\n";
+
+    std::cout << body;
+}
+
+void PrintCategoryDetails(const std::unique_ptr<Category>& category,
+                          std::unordered_map<unsigned short, std::shared_ptr<Counter>> counterMap)
+{
+    std::string categoryBody;
+    std::string categoryHeader;
+
+    categoryHeader.append(CentreAlignFormatting("Name", 20));
+    categoryHeader.append(" | ");
+    categoryHeader.append(CentreAlignFormatting("Event Count", 14));
+    categoryHeader.append("\n");
+
+    categoryBody.append(CentreAlignFormatting(category->m_Name, 20));
+    categoryBody.append(" | ");
+    categoryBody.append(CentreAlignFormatting(std::to_string(category->m_Counters.size()), 14));
+
+    std::cout << "\n" << "\n";
+    std::cout << CentreAlignFormatting("CATEGORY", static_cast<int>(categoryHeader.size()));
+    std::cout << "\n";
+    std::cout << std::string(categoryHeader.size(), '=') << "\n";
+
+    std::cout << categoryHeader;
+
+    std::cout << std::string(categoryBody.size(), '-') << "\n";
+
+    std::cout << categoryBody;
+
+    std::string counterHeader;
+
+    counterHeader.append(CentreAlignFormatting("Counter Name", 20));
+    counterHeader.append(" | ");
+    counterHeader.append(CentreAlignFormatting("Description", 50));
+    counterHeader.append(" | ");
+    counterHeader.append(CentreAlignFormatting("Units", 14));
+    counterHeader.append(" | ");
+    counterHeader.append(CentreAlignFormatting("UID", 6));
+    counterHeader.append(" | ");
+    counterHeader.append(CentreAlignFormatting("Max UID", 10));
+    counterHeader.append(" | ");
+    counterHeader.append(CentreAlignFormatting("Class", 8));
+    counterHeader.append(" | ");
+    counterHeader.append(CentreAlignFormatting("Interpolation", 14));
+    counterHeader.append(" | ");
+    counterHeader.append(CentreAlignFormatting("Multiplier", 20));
+    counterHeader.append(" | ");
+    counterHeader.append(CentreAlignFormatting("Counter set UID", 16));
+    counterHeader.append(" | ");
+    counterHeader.append(CentreAlignFormatting("Device UID", 14));
+    counterHeader.append("\n");
+
+    std::cout << "\n" << "\n";
+    std::cout << CentreAlignFormatting("EVENTS IN CATEGORY: " + category->m_Name,
+                                       static_cast<int>(counterHeader.size()));
+    std::cout << "\n";
+    std::cout << std::string(counterHeader.size(), '=') << "\n";
+    std::cout << counterHeader;
+    for (auto& it: category->m_Counters) {
+        auto search = counterMap.find(it);
+        if(search != counterMap.end()) {
+            PrintCounterDetails(search->second);
+        }
+    }
+}
+
+void PrintCounterDirectory(ICounterDirectory& counterDirectory)
+{
+    std::string devicesHeader;
+
+    devicesHeader.append(CentreAlignFormatting("Device name", 20));
+    devicesHeader.append(" | ");
+    devicesHeader.append(CentreAlignFormatting("UID", 13));
+    devicesHeader.append(" | ");
+    devicesHeader.append(CentreAlignFormatting("Cores", 10));
+    devicesHeader.append("\n");
+
+    std::cout << "\n" << "\n";
+    std::cout << CentreAlignFormatting("DEVICES", static_cast<int>(devicesHeader.size()));
+    std::cout << "\n";
+    std::cout << std::string(devicesHeader.size(), '=') << "\n";
+    std::cout << devicesHeader;
+    for (auto& it: counterDirectory.GetDevices()) {
+        PrintDeviceDetails(it);
+    }
+
+    std::string counterSetHeader;
+
+    counterSetHeader.append(CentreAlignFormatting("Counter set name", 20));
+    counterSetHeader.append(" | ");
+    counterSetHeader.append(CentreAlignFormatting("UID", 13));
+    counterSetHeader.append(" | ");
+    counterSetHeader.append(CentreAlignFormatting("Count", 10));
+    counterSetHeader.append("\n");
+
+    std::cout << "\n" << "\n";
+    std::cout << CentreAlignFormatting("COUNTER SETS", static_cast<int>(counterSetHeader.size()));
+    std::cout << "\n";
+    std::cout << std::string(counterSetHeader.size(), '=') << "\n";
+
+    std::cout << counterSetHeader;
+
+    for (auto& it: counterDirectory.GetCounterSets()) {
+        PrintCounterSetDetails(it);
+    }
+
+    auto counters = counterDirectory.GetCounters();
+    for (auto& it: counterDirectory.GetCategories()) {
+        PrintCategoryDetails(it, counters);
+    }
+    std::cout << "\n";
+}
+
+namespace
+{
+
+void ThrowIfCantGenerateNextUid(uint16_t uid, uint16_t cores = 0)
+{
+    // Check that it is possible to generate the next UID without causing an overflow
+    switch (cores)
+    {
+    case 0:
+    case 1:
+        // Number of cores not specified or set to 1 (a value of zero indicates the device is not capable of
+        // running multiple parallel workloads and will not provide multiple streams of data for each event)
+        if (uid == std::numeric_limits<uint16_t>::max())
+        {
+            throw arm::pipe::ProfilingException("Generating the next UID for profiling would result in an overflow");
+        }
+        break;
+    default: // cores > 1
+        // Multiple cores available, as max_counter_uid has to be set to: counter_uid + cores - 1, the maximum
+        // allowed value for a counter UID is consequently: uint16_t_max - cores + 1
+        if (uid >= std::numeric_limits<uint16_t>::max() - cores + 1)
+        {
+            throw arm::pipe::ProfilingException("Generating the next UID for profiling would result in an overflow");
+        }
+        break;
+    }
+}
+
+} // Anonymous namespace
+
+uint16_t GetNextUid(bool peekOnly)
+{
+    // The UID used for profiling objects and events. The first valid UID is 1, as 0 is a reserved value
+    static uint16_t uid = 1;
+
+    // Check that it is possible to generate the next UID without causing an overflow (throws in case of error)
+    ThrowIfCantGenerateNextUid(uid);
+
+    if (peekOnly)
+    {
+        // Peek only
+        return uid;
+    }
+    else
+    {
+        // Get the next UID
+        return uid++;
+    }
+}
+
+std::vector<uint16_t> GetNextCounterUids(uint16_t firstUid, uint16_t cores)
+{
+    // Check that it is possible to generate the next counter UID without causing an overflow (throws in case of error)
+    ThrowIfCantGenerateNextUid(firstUid, cores);
+
+    // Get the next counter UIDs
+    size_t counterUidsSize = cores == 0 ? 1 : cores;
+    std::vector<uint16_t> counterUids(counterUidsSize, 0);
+    for (size_t i = 0; i < counterUidsSize; i++)
+    {
+        counterUids[i] = firstUid++;
+    }
+    return counterUids;
+}
 
 } // namespace pipe
-} // namespace arm
\ No newline at end of file
+} // namespace arm
diff --git a/profiling/common/src/CounterDirectory.cpp b/profiling/common/src/CounterDirectory.cpp
new file mode 100644
index 0000000..f338858
--- /dev/null
+++ b/profiling/common/src/CounterDirectory.cpp
@@ -0,0 +1,546 @@
+//
+// Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include <common/include/CounterDirectory.hpp>
+#include <common/include/CommonProfilingUtils.hpp>
+
+#include <common/include/Assert.hpp>
+#include <common/include/SwTrace.hpp>
+
+#include <fmt/format.h>
+
+namespace arm
+{
+
+namespace pipe
+{
+
+const Category* CounterDirectory::RegisterCategory(const std::string& categoryName)
+{
+    // Check that the given category name is valid
+    if (categoryName.empty() ||
+            !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceNameCharPolicy>(categoryName))
+    {
+        throw arm::pipe::InvalidArgumentException("Trying to register a category with an invalid name");
+    }
+
+    // Check that the given category is not already registered
+    if (IsCategoryRegistered(categoryName))
+    {
+        throw arm::pipe::InvalidArgumentException(fmt::format(
+            "Trying to register a category already registered (\"{}\")",
+            categoryName));
+    }
+
+    // Create the category
+    CategoryPtr category = std::make_unique<Category>(categoryName);
+    ARM_PIPE_ASSERT(category);
+
+    // Get the raw category pointer
+    const Category* categoryPtr = category.get();
+    ARM_PIPE_ASSERT(categoryPtr);
+
+    // Register the category
+    m_Categories.insert(std::move(category));
+
+    return categoryPtr;
+}
+
+const Device* CounterDirectory::RegisterDevice(const std::string& deviceName,
+                                               uint16_t cores,
+                                               const arm::pipe::Optional<std::string>& parentCategoryName)
+{
+    // Check that the given device name is valid
+    if (deviceName.empty() ||
+            !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceCharPolicy>(deviceName))
+    {
+        throw arm::pipe::InvalidArgumentException("Trying to register a device with an invalid name");
+    }
+
+    // Check that a device with the given name is not already registered
+    if (IsDeviceRegistered(deviceName))
+    {
+        throw arm::pipe::InvalidArgumentException(fmt::format(
+            "Trying to register a device already registered (\"{}\")",
+            deviceName));
+    }
+
+    // Check that a category with the given (optional) parent category name is already registered
+    if (parentCategoryName.has_value())
+    {
+        // Get the (optional) parent category name
+        const std::string& parentCategoryNameValue = parentCategoryName.value();
+        if (parentCategoryNameValue.empty())
+        {
+            throw arm::pipe::InvalidArgumentException(
+                fmt::format("Trying to connect a device (name: \"{}\") to an invalid "
+                            "parent category (name: \"{}\")",
+                            deviceName,
+                            parentCategoryNameValue));
+        }
+
+        // Check that the given parent category is already registered
+        auto categoryIt = FindCategory(parentCategoryNameValue);
+        if (categoryIt == m_Categories.end())
+        {
+            throw arm::pipe::InvalidArgumentException(
+                fmt::format("Trying to connect a device (name: \"{}\") to a parent category that "
+                            "is not registered (name: \"{}\")",
+                            deviceName,
+                            parentCategoryNameValue));
+        }
+    }
+
+    // Get the device UID
+    uint16_t deviceUid = GetNextUid();
+
+    // Create the device
+    DevicePtr device = std::make_unique<Device>(deviceUid, deviceName, cores);
+    ARM_PIPE_ASSERT(device);
+
+    // Get the raw device pointer
+    const Device* devicePtr = device.get();
+    ARM_PIPE_ASSERT(devicePtr);
+
+    // Register the device
+    m_Devices.insert(std::make_pair(deviceUid, std::move(device)));
+
+    return devicePtr;
+}
+
+const CounterSet* CounterDirectory::RegisterCounterSet(const std::string& counterSetName,
+                                                       uint16_t count,
+                                                       const arm::pipe::Optional<std::string>& parentCategoryName)
+{
+    // Check that the given counter set name is valid
+    if (counterSetName.empty() ||
+            !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceNameCharPolicy>(counterSetName))
+    {
+        throw arm::pipe::InvalidArgumentException("Trying to register a counter set with an invalid name");
+    }
+
+    // Check that a counter set with the given name is not already registered
+    if (IsCounterSetRegistered(counterSetName))
+    {
+        throw arm::pipe::InvalidArgumentException(
+            fmt::format("Trying to register a counter set already registered (\"{}\")", counterSetName));
+    }
+
+    // Peek the next UID, do not get an actual valid UID just now as we don't want to waste a good UID in case
+    // the registration fails. We'll get a proper one once we're sure that the counter set can be registered
+    uint16_t counterSetUidPeek = GetNextUid(true);
+
+    // Check that a category with the given (optional) parent category name is already registered
+    if (parentCategoryName.has_value())
+    {
+        // Get the (optional) parent category name
+        const std::string& parentCategoryNameValue = parentCategoryName.value();
+        if (parentCategoryNameValue.empty())
+        {
+            throw arm::pipe::InvalidArgumentException(
+                fmt::format("Trying to connect a counter set (UID: {}) to an invalid "
+                                    "parent category (name: \"{}\")",
+                                    counterSetUidPeek,
+                                    parentCategoryNameValue));
+        }
+
+        // Check that the given parent category is already registered
+        auto it = FindCategory(parentCategoryNameValue);
+        if (it == m_Categories.end())
+        {
+            throw arm::pipe::InvalidArgumentException(
+                fmt::format("Trying to connect a counter set (UID: {}) to a parent category "
+                            "that is not registered (name: \"{}\")",
+                            counterSetUidPeek,
+                            parentCategoryNameValue));
+        }
+    }
+
+    // Get the counter set UID
+    uint16_t counterSetUid = GetNextUid();
+    ARM_PIPE_ASSERT(counterSetUid == counterSetUidPeek);
+
+    // Create the counter set
+    CounterSetPtr counterSet = std::make_unique<CounterSet>(counterSetUid, counterSetName, count);
+    ARM_PIPE_ASSERT(counterSet);
+
+    // Get the raw counter set pointer
+    const CounterSet* counterSetPtr = counterSet.get();
+    ARM_PIPE_ASSERT(counterSetPtr);
+
+    // Register the counter set
+    m_CounterSets.insert(std::make_pair(counterSetUid, std::move(counterSet)));
+
+    return counterSetPtr;
+}
+
+const Counter* CounterDirectory::RegisterCounter(const std::string& applicationName,
+                                                 const uint16_t uid,
+                                                 const std::string& parentCategoryName,
+                                                 uint16_t counterClass,
+                                                 uint16_t interpolation,
+                                                 double multiplier,
+                                                 const std::string& name,
+                                                 const std::string& description,
+                                                 const arm::pipe::Optional<std::string>& units,
+                                                 const arm::pipe::Optional<uint16_t>& numberOfCores,
+                                                 const arm::pipe::Optional<uint16_t>& deviceUid,
+                                                 const arm::pipe::Optional<uint16_t>& counterSetUid)
+{
+    // Check that the given parent category name is valid
+    if (parentCategoryName.empty() ||
+            !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceNameCharPolicy>(parentCategoryName))
+    {
+        throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid parent category name");
+    }
+
+    // Check that the given class is valid
+    if (counterClass != 0 && counterClass != 1)
+    {
+        throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid class");
+    }
+
+    // Check that the given interpolation is valid
+    if (interpolation != 0 && interpolation != 1)
+    {
+        throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid interpolation");
+    }
+
+    // Check that the given multiplier is valid
+    if (multiplier == .0f)
+    {
+        throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid multiplier");
+    }
+
+    // Check that the given name is valid
+    if (name.empty() ||
+            !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceCharPolicy>(name))
+    {
+        throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid name");
+    }
+
+    // Check that the given description is valid
+    if (description.empty() ||
+            !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceCharPolicy>(description))
+    {
+        throw arm::pipe::InvalidArgumentException("Trying to register a counter with an invalid description");
+    }
+
+    // Check that the given units are valid
+    if (units.has_value()
+            && !arm::pipe::IsValidSwTraceString<arm::pipe::SwTraceNameCharPolicy>(units.value()))
+    {
+        throw arm::pipe::InvalidArgumentException("Trying to register a counter with a invalid units");
+    }
+
+    // Check that the given parent category is registered
+    auto categoryIt = FindCategory(parentCategoryName);
+    if (categoryIt == m_Categories.end())
+    {
+        throw arm::pipe::InvalidArgumentException(
+            fmt::format("Trying to connect a counter to a category that is not registered (name: \"{}\")",
+                        parentCategoryName));
+    }
+
+    // Get the parent category
+    const CategoryPtr& parentCategory = *categoryIt;
+    ARM_PIPE_ASSERT(parentCategory);
+
+    // Check that a counter with the given name is not already registered within the parent category
+    const std::vector<uint16_t>& parentCategoryCounters = parentCategory->m_Counters;
+    for (uint16_t parentCategoryCounterUid : parentCategoryCounters)
+    {
+        const Counter* parentCategoryCounter = GetCounter(parentCategoryCounterUid);
+        ARM_PIPE_ASSERT(parentCategoryCounter);
+
+        if (parentCategoryCounter->m_Name == name)
+        {
+            throw arm::pipe::InvalidArgumentException(
+                fmt::format("Trying to register a counter to category \"{}\" with a name that "
+                            "is already used within that category (name: \"{}\")",
+                            parentCategoryName,
+                            name));
+        }
+    }
+
+    // Check that a counter set with the given (optional) UID is already registered
+    uint16_t counterSetUidValue = counterSetUid.has_value() ? counterSetUid.value() : 0;
+    if (counterSetUidValue > 0)
+    {
+        // Check that the (optional) counter set is already registered
+        if (!IsCounterSetRegistered(counterSetUidValue))
+        {
+            throw InvalidArgumentException(
+                        fmt::format("Trying to connect a counter to a counter set that is "
+                                    "not registered (counter set UID: {})",
+                                    counterSetUidValue));
+        }
+    }
+
+    // Get the number of cores (this call may throw)
+    uint16_t deviceUidValue = deviceUid.has_value() ? deviceUid.value() : 0;
+    uint16_t deviceCores = GetNumberOfCores(numberOfCores, deviceUidValue);
+
+    // Get the counter UIDs and calculate the max counter UID
+    std::vector<uint16_t> counterUids = GetNextCounterUids(uid, deviceCores);
+    ARM_PIPE_ASSERT(!counterUids.empty());
+    uint16_t maxCounterUid = deviceCores <= 1 ? counterUids.front() : counterUids.back();
+
+    // Get the counter units
+    const std::string unitsValue = units.has_value() ? units.value() : "";
+
+    // Create the counter
+    CounterPtr counter = std::make_shared<Counter>(applicationName,
+                                                   counterUids.front(),
+                                                   maxCounterUid,
+                                                   counterClass,
+                                                   interpolation,
+                                                   multiplier,
+                                                   name,
+                                                   description,
+                                                   unitsValue,
+                                                   deviceUidValue,
+                                                   counterSetUidValue);
+    ARM_PIPE_ASSERT(counter);
+
+    // Get the raw counter pointer
+    const Counter* counterPtr = counter.get();
+    ARM_PIPE_ASSERT(counterPtr);
+
+    // Process multiple counters if necessary
+    for (uint16_t counterUid : counterUids)
+    {
+        // Connect the counter to the parent category
+        parentCategory->m_Counters.push_back(counterUid);
+
+        // Register the counter
+        m_Counters.insert(std::make_pair(counterUid, counter));
+    }
+
+    return counterPtr;
+}
+
+const Category* CounterDirectory::GetCategory(const std::string& categoryName) const
+{
+    auto it = FindCategory(categoryName);
+    if (it == m_Categories.end())
+    {
+        return nullptr;
+    }
+
+    const Category* category = it->get();
+    ARM_PIPE_ASSERT(category);
+
+    return category;
+}
+
+const Device* CounterDirectory::GetDevice(uint16_t deviceUid) const
+{
+    auto it = FindDevice(deviceUid);
+    if (it == m_Devices.end())
+    {
+        return nullptr;
+    }
+
+    const Device* device = it->second.get();
+    ARM_PIPE_ASSERT(device);
+    ARM_PIPE_ASSERT(device->m_Uid == deviceUid);
+
+    return device;
+}
+
+const CounterSet* CounterDirectory::GetCounterSet(uint16_t counterSetUid) const
+{
+    auto it = FindCounterSet(counterSetUid);
+    if (it == m_CounterSets.end())
+    {
+        return nullptr;
+    }
+
+    const CounterSet* counterSet = it->second.get();
+    ARM_PIPE_ASSERT(counterSet);
+    ARM_PIPE_ASSERT(counterSet->m_Uid == counterSetUid);
+
+    return counterSet;
+}
+
+const Counter* CounterDirectory::GetCounter(uint16_t counterUid) const
+{
+    auto it = FindCounter(counterUid);
+    if (it == m_Counters.end())
+    {
+        return nullptr;
+    }
+
+    const Counter* counter = it->second.get();
+    ARM_PIPE_ASSERT(counter);
+    ARM_PIPE_ASSERT(counter->m_Uid <= counterUid);
+    ARM_PIPE_ASSERT(counter->m_Uid <= counter->m_MaxCounterUid);
+
+    return counter;
+}
+
+bool CounterDirectory::IsCategoryRegistered(const std::string& categoryName) const
+{
+    auto it = FindCategory(categoryName);
+
+    return it != m_Categories.end();
+}
+
+bool CounterDirectory::IsDeviceRegistered(uint16_t deviceUid) const
+{
+    auto it = FindDevice(deviceUid);
+
+    return it != m_Devices.end();
+}
+
+bool CounterDirectory::IsDeviceRegistered(const std::string& deviceName) const
+{
+    auto it = FindDevice(deviceName);
+
+    return it != m_Devices.end();
+}
+
+bool CounterDirectory::IsCounterSetRegistered(uint16_t counterSetUid) const
+{
+    auto it = FindCounterSet(counterSetUid);
+
+    return it != m_CounterSets.end();
+}
+
+bool CounterDirectory::IsCounterSetRegistered(const std::string& counterSetName) const
+{
+    auto it = FindCounterSet(counterSetName);
+
+    return it != m_CounterSets.end();
+}
+
+bool CounterDirectory::IsCounterRegistered(uint16_t counterUid) const
+{
+    auto it = FindCounter(counterUid);
+
+    return it != m_Counters.end();
+}
+
+bool CounterDirectory::IsCounterRegistered(const std::string& counterName) const
+{
+    auto it = FindCounter(counterName);
+
+    return it != m_Counters.end();
+}
+
+void CounterDirectory::Clear()
+{
+    // Clear all the counter directory contents
+    m_Categories.clear();
+    m_Devices.clear();
+    m_CounterSets.clear();
+    m_Counters.clear();
+}
+
+CategoriesIt CounterDirectory::FindCategory(const std::string& categoryName) const
+{
+    return std::find_if(m_Categories.begin(), m_Categories.end(), [&categoryName](const CategoryPtr& category)
+    {
+        ARM_PIPE_ASSERT(category);
+
+        return category->m_Name == categoryName;
+    });
+}
+
+DevicesIt CounterDirectory::FindDevice(uint16_t deviceUid) const
+{
+    return m_Devices.find(deviceUid);
+}
+
+DevicesIt CounterDirectory::FindDevice(const std::string& deviceName) const
+{
+    return std::find_if(m_Devices.begin(), m_Devices.end(), [&deviceName](const auto& pair)
+    {
+        ARM_PIPE_ASSERT(pair.second);
+        ARM_PIPE_ASSERT(pair.second->m_Uid == pair.first);
+
+        return pair.second->m_Name == deviceName;
+    });
+}
+
+CounterSetsIt CounterDirectory::FindCounterSet(uint16_t counterSetUid) const
+{
+    return m_CounterSets.find(counterSetUid);
+}
+
+CounterSetsIt CounterDirectory::FindCounterSet(const std::string& counterSetName) const
+{
+    return std::find_if(m_CounterSets.begin(), m_CounterSets.end(), [&counterSetName](const auto& pair)
+    {
+        ARM_PIPE_ASSERT(pair.second);
+        ARM_PIPE_ASSERT(pair.second->m_Uid == pair.first);
+
+        return pair.second->m_Name == counterSetName;
+    });
+}
+
+CountersIt CounterDirectory::FindCounter(uint16_t counterUid) const
+{
+    return m_Counters.find(counterUid);
+}
+
+CountersIt CounterDirectory::FindCounter(const std::string& counterName) const
+{
+    return std::find_if(m_Counters.begin(), m_Counters.end(), [&counterName](const auto& pair)
+    {
+        ARM_PIPE_ASSERT(pair.second);
+        ARM_PIPE_ASSERT(pair.first >= pair.second->m_Uid && pair.first <= pair.second->m_MaxCounterUid);
+
+        return pair.second->m_Name == counterName;
+    });
+}
+
+uint16_t CounterDirectory::GetNumberOfCores(const arm::pipe::Optional<uint16_t>& numberOfCores,
+                                            uint16_t deviceUid)
+{
+    // To get the number of cores, apply the following rules:
+    //
+    // 1. If numberOfCores is set then take it as the deviceCores value
+    // 2. If numberOfCores is not set then check to see if this counter is directly associated with a device,
+    //    if so then that devices number of cores is taken as the deviceCores value
+    // 3. If none of the above holds then set deviceCores to zero
+
+    // 1. If numberOfCores is set then take it as the deviceCores value
+    if (numberOfCores.has_value())
+    {
+        // Get the number of cores
+        return numberOfCores.value();
+    }
+
+    // 2. If numberOfCores is not set then check to see if this counter is directly associated with a device,
+    //    if so then that devices number of cores is taken as the deviceCores value
+    if (deviceUid > 0)
+    {
+        // Check that the (optional) device is already registered
+        auto deviceIt = FindDevice(deviceUid);
+        if (deviceIt == m_Devices.end())
+        {
+            throw arm::pipe::InvalidArgumentException(
+                fmt::format("Trying to connect a counter to a device that is not registered (device UID {})",
+                            deviceUid));
+        }
+
+        // Get the associated device
+        const DevicePtr& device = deviceIt->second;
+        ARM_PIPE_ASSERT(device);
+
+
+        // Get the number of cores of the associated device
+        return device->m_Cores;
+    }
+
+    // 3. If none of the above holds then set deviceCores to zero
+    return 0;
+}
+
+} // namespace pipe
+
+} // namespace arm