IVGCVSW-3989 Create the Counter Directory Decoder
authorFinn Williams <Finn.Williams@arm.com>
Tue, 15 Oct 2019 13:22:13 +0000 (14:22 +0100)
committerJim Flynn Arm <jim.flynn@arm.com>
Mon, 21 Oct 2019 16:52:03 +0000 (16:52 +0000)
Signed-off-by: Finn Williams <Finn.Williams@arm.com>
Change-Id: If388e60434eae39d82b639d2275680679963624c

12 files changed:
CMakeLists.txt
tests/profiling/gatordmock/CommandFileParser.cpp
tests/profiling/gatordmock/CounterDirectory.hpp [new file with mode: 0644]
tests/profiling/gatordmock/DirectoryCaptureCommandHandler.cpp [new file with mode: 0644]
tests/profiling/gatordmock/DirectoryCaptureCommandHandler.hpp [new file with mode: 0644]
tests/profiling/gatordmock/GatordMockMain.cpp
tests/profiling/gatordmock/GatordMockService.cpp
tests/profiling/gatordmock/GatordMockService.hpp
tests/profiling/gatordmock/MockUtils.cpp [new file with mode: 0644]
tests/profiling/gatordmock/MockUtils.hpp [new file with mode: 0644]
tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.cpp
tests/profiling/gatordmock/tests/GatordMockTests.cpp

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