* Receive and process the stream metadata from the client.
* Send the connection ack packet.
* Wait in a receiving thread and print the packets.
* GatordMockTest and Impl for PeriodicCounterCapture CommandHandler
* CaptureData class to retain packet data
* MockUtils
* Update SocketProfilingConnection to fix non blocking receipt of packets.
* Restructure directory layout following review comments.
* Extract the mock service into a static library in the cmake files.
Signed-off-by: Colm Donelan <Colm.Donelan@arm.com>
Signed-off-by: Keith Davis <keith.davis@arm.com>
Signed-off-by: Mike Kelly <mike.kelly@arm.com>
Signed-off-by: Finn Williams <Finn.Williams@arm.com>
Signed-off-by: Kevin May <kevin.may@arm.com>
Change-Id: I33c1c9f93976708c9315f71290d42cff53b8c075
set_source_files_properties(src/armnnDeserializer/test/SchemaSerialize.s PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp")
endif()
+ if(BUILD_GATORD_MOCK)
+ list(APPEND unittest_sources
+ tests/profiling/gatordmock/tests/GatordMockTests.cpp
+ )
+ endif()
+
foreach(lib ${armnnUnitTestLibraries})
message("Adding object library dependency to UnitTests: ${lib}")
list(APPEND unittest_sources $<TARGET_OBJECTS:${lib}>)
target_link_libraries(UnitTests armnnTfParser)
endif()
+ if(BUILD_GATORD_MOCK)
+ target_link_libraries(UnitTests gatordMockService)
+ endif()
+
if(BUILD_TF_LITE_PARSER)
target_include_directories(UnitTests SYSTEM PRIVATE "${TF_LITE_SCHEMA_INCLUDE_PATH}")
target_include_directories(UnitTests SYSTEM PRIVATE "${FLATBUFFERS_INCLUDE_PATH}")
if(BUILD_GATORD_MOCK)
set(gatord_mock_sources)
list(APPEND gatord_mock_sources
- tests/profiling/GatordMockMain.cpp
- tests/profiling/CommandLineProcessor.hpp
- tests/profiling/CommandLineProcessor.cpp
- tests/profiling/GatordMockService.hpp
- tests/profiling/GatordMockService.cpp
+ tests/profiling/gatordmock/CommandFileParser.hpp
+ tests/profiling/gatordmock/CommandFileParser.cpp
+ tests/profiling/gatordmock/CommandLineProcessor.hpp
+ tests/profiling/gatordmock/CommandLineProcessor.cpp
+ tests/profiling/gatordmock/GatordMockService.hpp
+ tests/profiling/gatordmock/GatordMockService.cpp
+ tests/profiling/gatordmock/MockUtils.hpp
+ tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.cpp
+ tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.hpp
)
- include_directories( ${Boost_INCLUDE_DIRS} )
+ include_directories( ${Boost_INCLUDE_DIRS} src/profiling)
- add_executable_ex(GartordMock ${gatord_mock_sources})
+ add_library_ex(gatordMockService STATIC ${gatord_mock_sources})
+ add_executable_ex(GartordMock tests/profiling/gatordmock/GatordMockMain.cpp)
+
+ if(Threads_FOUND AND (NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL Android)))
+ target_link_libraries(GartordMock pthread)
+ endif()
+
+ target_link_libraries(GartordMock armnn gatordMockService)
target_link_libraries(GartordMock
- ${Boost_PROGRAM_OPTIONS_LIBRARY}
+ ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SYSTEM_LIBRARY}
)
-endif()
+endif()
\ No newline at end of file
option(TF_LITE_GENERATED_PATH "Tensorflow lite generated C++ schema location" OFF)
option(FLATBUFFERS_ROOT "Location where the flatbuffers 'include' and 'lib' folders to be found" Off)
option(DYNAMIC_BACKEND_PATHS "Colon seperated list of paths where to load the dynamic backends from" "")
-option(BUILD_GATORD_MOCK "Build the Gatord simulator for external profiling testing." OFF)
+option(BUILD_GATORD_MOCK "Build the Gatord simulator for external profiling testing." ON)
include(SelectLibraryConfigurations)
#include "SocketProfilingConnection.hpp"
+#include <cerrno>
#include <fcntl.h>
+#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
-#include <cerrno>
#include <string>
namespace armnn
m_Socket[0].fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (m_Socket[0].fd == -1)
{
- throw RuntimeException(std::string("Socket construction failed: ") + strerror(errno));
+ throw armnn::RuntimeException(std::string("Socket construction failed: ") + strerror(errno));
}
// Connect to the named unix domain socket.
if (0 != connect(m_Socket[0].fd, reinterpret_cast<const sockaddr*>(&server), sizeof(sockaddr_un)))
{
close(m_Socket[0].fd);
- throw RuntimeException(std::string("Cannot connect to stream socket: ") + strerror(errno));
+ throw armnn::RuntimeException(std::string("Cannot connect to stream socket: ") + strerror(errno));
}
// Our socket will only be interested in polling reads.
if (0 != fcntl(m_Socket[0].fd, F_SETFL, currentFlags | O_NONBLOCK))
{
close(m_Socket[0].fd);
- throw RuntimeException(std::string("Failed to set socket as non blocking: ") + strerror(errno));
+ throw armnn::RuntimeException(std::string("Failed to set socket as non blocking: ") + strerror(errno));
}
}
{
if (close(m_Socket[0].fd) != 0)
{
- throw RuntimeException(std::string("Cannot close stream socket: ") + strerror(errno));
+ throw armnn::RuntimeException(std::string("Cannot close stream socket: ") + strerror(errno));
}
memset(m_Socket, 0, sizeof(m_Socket));
Packet SocketProfilingConnection::ReadPacket(uint32_t timeout)
{
- // Poll for data on the socket or until timeout occurs
- int pollResult = poll(m_Socket, 1, static_cast<int>(timeout));
-
- switch (pollResult)
+ // Is there currently at least a headers worth of data waiting to be read?
+ int bytes_available;
+ ioctl(m_Socket[0].fd, FIONREAD, &bytes_available);
+ if (bytes_available >= 8)
{
- case -1: // Error
- throw RuntimeException(std::string("Read failure from socket: ") + strerror(errno));
+ // Yes there is. Read it:
+ return ReceivePacket();
+ }
+ else
+ {
+ // Poll for data on the socket or until timeout occurs
+ int pollResult = poll(m_Socket, 1, static_cast<int>(timeout));
+
+ switch (pollResult)
+ {
+ case -1: // Error
+ throw armnn::RuntimeException(std::string("Read failure from socket: ") + strerror(errno));
- case 0: // Timeout
- throw RuntimeException("Timeout while reading from socket");
+ case 0: // Timeout
+ throw TimeoutException("Timeout while reading from socket");
- default: // Normal poll return but it could still contain an error signal
+ default: // Normal poll return but it could still contain an error signal
- // Check if the socket reported an error
- if (m_Socket[0].revents & (POLLNVAL | POLLERR | POLLHUP))
- {
- throw Exception(std::string("Socket 0 reported an error: ") + strerror(errno));
- }
+ // Check if the socket reported an error
+ if (m_Socket[0].revents & (POLLNVAL | POLLERR | POLLHUP))
+ {
+ throw armnn::RuntimeException(std::string("Socket reported an error: ") + strerror(errno));
+ }
- // Check if there is data to read
- if (!(m_Socket[0].revents & (POLLIN)))
- {
- // No data to read from the socket. Silently ignore and continue
- return Packet();
- }
+ // Check if there is data to read
+ if (!(m_Socket[0].revents & (POLLIN)))
+ {
+ // This is a very odd case. The file descriptor was woken up but no data was written.
+ throw armnn::RuntimeException("Poll resulted in : no data to read");
+ }
- // There is data to read, read the header first
- char header[8] = {};
- if (8 != recv(m_Socket[0].fd, &header, sizeof(header), 0))
- {
- // What do we do here if there's not a valid 8 byte header to read?
- throw RuntimeException("The received packet did not contains a valid MIPE header");
+ return ReceivePacket();
}
+ }
+}
- // stream_metadata_identifier is the first 4 bytes
- uint32_t metadataIdentifier = 0;
- std::memcpy(&metadataIdentifier, header, sizeof(metadataIdentifier));
+Packet SocketProfilingConnection::ReceivePacket()
+{
+ char header[8] = {};
+ if (8 != recv(m_Socket[0].fd, &header, sizeof(header), 0))
+ {
+ // What do we do here if there's not a valid 8 byte header to read?
+ throw armnn::RuntimeException("The received packet did not contains a valid MIPE header");
+ }
+ // stream_metadata_identifier is the first 4 bytes
+ uint32_t metadataIdentifier = 0;
+ std::memcpy(&metadataIdentifier, header, sizeof(metadataIdentifier));
- // data_length is the next 4 bytes
- uint32_t dataLength = 0;
- std::memcpy(&dataLength, header + 4u, sizeof(dataLength));
+ // data_length is the next 4 bytes
+ uint32_t dataLength = 0;
+ std::memcpy(&dataLength, header + 4u, sizeof(dataLength));
std::unique_ptr<unsigned char[]> packetData;
- if (dataLength > 0)
- {
+ if (dataLength > 0)
+ {
packetData = std::make_unique<unsigned char[]>(dataLength);
- }
-
ssize_t receivedLength = recv(m_Socket[0].fd, packetData.get(), dataLength, 0);
if (receivedLength < 0)
{
if (dataLength != static_cast<uint32_t>(receivedLength))
{
// What do we do here if we can't read in a full packet?
- throw RuntimeException("Invalid MIPE packet");
+ throw armnn::RuntimeException("Invalid MIPE packet");
}
-
- return Packet(metadataIdentifier, dataLength, packetData);
}
+
+ return Packet(metadataIdentifier, dataLength, packetData);
}
} // namespace profiling
Packet ReadPacket(uint32_t timeout) final;
private:
+
+ // Read a full packet from the socket.
+ Packet ReceivePacket();
+
// To indicate we want to use an abstract UDS ensure the first character of the address is 0.
const char* m_GatorNamespace = "\0gatord_namespace";
struct pollfd m_Socket[1]{};
+++ /dev/null
-//
-// Copyright © 2019 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-#pragma once
-
-#include <string>
-
-namespace armnn
-{
-
-namespace gatordmock
-{
-
-// Parses the command line to extract:
-//
-//
-
-/**
- * Use Boost program options to process the command line.
- * -h or --help to print the options.
- * -n or --namespace to specify the UDS namespace that the server will be listening on.
- */
-class CommandLineProcessor
-{
-public:
- bool ProcessCommandLine(int argc, char *argv[]);
-
- std::string GetUdsNamespace() { return m_UdsNamespace; }
-
-private:
- std::string m_UdsNamespace;
-};
-
-} // namespace gatordmock
-
-} // namespace armnn
+++ /dev/null
-//
-// Copyright © 2019 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-
-#include "CommandLineProcessor.hpp"
-#include "GatordMockService.hpp"
-
-int main(int argc, char *argv[])
-{
- armnn::gatordmock::CommandLineProcessor cmdline;
- if (!cmdline.ProcessCommandLine(argc, argv))
- {
- return EXIT_FAILURE;
- }
- armnn::gatordmock::GatordMockService mockService;
- if (!mockService.OpenListeningSocket(cmdline.GetUdsNamespace()))
- {
- return EXIT_FAILURE;
- }
- int clientFd = mockService.BlockForOneClient();
- if (-1 == clientFd)
- {
- return EXIT_FAILURE;
- }
- return EXIT_SUCCESS;
-}
+++ /dev/null
-//
-// Copyright © 2019 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-
-#include "GatordMockService.hpp"
-
-#include <cerrno>
-#include <fcntl.h>
-#include <iostream>
-#include <string>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-namespace armnn
-{
-
-namespace gatordmock
-{
-
-
-bool GatordMockService::OpenListeningSocket(std::string udsNamespace)
-{
- m_ListeningSocket = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
- if (-1 == m_ListeningSocket)
- {
- std::cerr << ": Socket construction failed: " << strerror(errno) << std::endl;
- return false;
- }
-
- sockaddr_un udsAddress;
- memset(&udsAddress, 0, sizeof(sockaddr_un));
- // We've set the first element of sun_path to be 0, skip over it and copy the namespace after it.
- memcpy(udsAddress.sun_path + 1, udsNamespace.c_str(), strlen(udsNamespace.c_str()));
- udsAddress.sun_family = AF_UNIX;
-
- // Bind the socket to the UDS namespace.
- if (-1 == bind(m_ListeningSocket, reinterpret_cast<const sockaddr *>(&udsAddress), sizeof(sockaddr_un)))
- {
- std::cerr << ": Binding on socket failed: " << strerror(errno) << std::endl;
- return false;
- }
- // Listen for 1 connection.
- if (-1 == listen(m_ListeningSocket, 1))
- {
- std::cerr << ": Listen call on socket failed: " << strerror(errno) << std::endl;
- return false;
- }
- std::cout << "Bound to UDS namespace: \\0" << udsNamespace << std::endl;
- return true;
-}
-
-int GatordMockService::BlockForOneClient()
-{
- std::cout << "Waiting for client connection." << std::endl;
-
- int accepted = accept4(m_ListeningSocket, nullptr, nullptr, SOCK_CLOEXEC);
- if (-1 == accepted)
- {
- std::cerr << ": Failure when waiting for a client connection: " << strerror(errno) << std::endl;
- return -1;
- }
-
- std::cout << "Client connection established." << std::endl;
- return accepted;
-}
-
-
-} // namespace gatordmock
-
-} // namespace armnn
+++ /dev/null
-//
-// Copyright © 2019 Arm Ltd. All rights reserved.
-// SPDX-License-Identifier: MIT
-//
-
-#pragma once
-
-#include <string>
-
-namespace armnn
-{
-
-namespace gatordmock
-{
-
-
-/**
- * A class that implements a Mock Gatord server. It will listen on a specified Unix domain socket (UDS)
- * namespace for client connections.
- */
-class GatordMockService
-{
-public:
-
- /**
- * Establish the Unix domain socket and set it to listen for connections.
- *
- * @param udsNamespace the namespace (socket address) associated with the listener.
- * @return true only if the socket has been correctly setup.
- */
- bool OpenListeningSocket(std::string udsNamespace);
-
- /**
- * Block waiting to accept one client to connect to the UDS.
- *
- * @return the file descriptor of the client connection.
- */
- int BlockForOneClient();
-
-private:
-
- int m_ListeningSocket;
-};
-
-
-} // namespace gatordmock
-
-} // namespace armnn
-
-
--- /dev/null
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "CommandFileParser.hpp"
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <iterator>
+
+namespace armnn
+{
+
+namespace gatordmock
+{
+
+void CommandFileParser::ParseFile(std::string CommandFile, GatordMockService& mockService)
+{
+ std::ifstream infile(CommandFile);
+ std::string line;
+
+ std::cout << "Parsing command file: " << CommandFile << std::endl;
+
+ while (std::getline(infile, line))
+ {
+ std::istringstream iss(line);
+
+ std::vector<std::string> tokens;
+
+ std::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(),
+ std::back_inserter(tokens));
+
+ std::string command = tokens[0];
+
+ if (command == "SET")
+ {
+ // Expected format for the SET command
+ //
+ // SET 500000 1 2 5 10
+ //
+ // This breaks down to:
+ // SET command
+ // 500000 polling period in micro seconds
+ // 1 2 5 10 counter list
+
+ uint period = static_cast<uint>(std::stoul(tokens[1]));
+
+ std::vector<uint16_t> counters;
+
+ std::transform(tokens.begin() + 2, tokens.end(), std::back_inserter(counters),
+ [](const std::string& str) { return static_cast<uint16_t>(std::stoul(str)); });
+
+ mockService.SendPeriodicCounterSelectionList(period, counters);
+ }
+ else if (command == "WAIT")
+ {
+ // Expected format for the SET command
+ //
+ // WAIT 11000000
+ //
+ // This breaks down to:
+ // WAIT command
+ // 11000000 timeout period in micro seconds
+
+ uint timeout = static_cast<uint>(std::stoul(tokens[1]));
+
+ mockService.WaitCommand(timeout);
+ }
+ }
+}
+
+} // namespace gatordmock
+
+} // namespace armnn
--- /dev/null
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <string>
+#include "GatordMockService.hpp"
+
+namespace armnn
+{
+
+namespace gatordmock
+{
+
+/// This class parses a command file for the GatordMockService. The file contains one command per line.
+/// Valid commands are: SET and WAIT.
+///
+/// SET: Will construct and send a PeriodicCounterSelection packet to enable a set of counters.
+/// WAIT: Will pause for a set period of time to allow for data to be received.
+class CommandFileParser
+{
+public:
+ void ParseFile(std::string CommandFile, GatordMockService& mockService);
+};
+
+} // namespace gatordmock
+} // namespace armnn
+
+
#include "CommandLineProcessor.hpp"
#include <boost/program_options.hpp>
-
#include <iostream>
namespace armnn
bool CommandLineProcessor::ProcessCommandLine(int argc, char *argv[])
{
namespace po = boost::program_options;
-
po::options_description desc("Options");
try
{
desc.add_options()
("help,h", "Display help messages")
+ ("file,f", po::value<std::string>(&m_File),
+ "The path to the file that contains instructions for the mock gatord")
("namespace,n", po::value<std::string>(&m_UdsNamespace)->default_value("gatord_namespace"),
"The Unix domain socket namespace this server will bind to.\n"
- "This will always be prepended with \\0 to use the abstract namespace");
+ "This will always be prepended with \\0 to use the abstract namespace")
+ ("echo,e", po::bool_switch(&m_Echo)->default_value(false),
+ "Echo packets sent and received to stdout. Disabled by default.\n");
}
catch (const std::exception& e)
{
std::cout << desc << std::endl;
return false;
}
-
+ // Currently the file parameter is mandatory.
+ if (!vm.count("file"))
+ {
+ std::cout << std::endl << "*** Expected --file or -f parameter." << std::endl;
+ std::cout << std::endl;
+ std::cout << desc << std::endl;
+ return false;
+ }
po::notify(vm);
}
catch (const po::error& e)
--- /dev/null
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+#pragma once
+
+#include <string>
+
+namespace armnn
+{
+
+namespace gatordmock
+{
+
+
+/// Use Boost program options to process the command line.
+/// -h or --help to print the options.
+/// -n or --namespace to specify the UDS namespace that the server will be listening on.
+/// -e or --echo print all sent and received packets to stdout.
+/// -f or --file The path to the file that contains instructions for the mock gatord.
+class CommandLineProcessor
+{
+public:
+ bool ProcessCommandLine(int argc, char *argv[]);
+ bool IsEchoEnabled() { return m_Echo; }
+
+ std::string GetUdsNamespace() { return m_UdsNamespace; }
+ std::string GetCommandFile() { return m_File; }
+
+private:
+ std::string m_UdsNamespace;
+ std::string m_File;
+
+ bool m_Echo;
+};
+
+} // namespace gatordmock
+
+} // namespace armnn
--- /dev/null
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "CommandFileParser.hpp"
+#include "CommandLineProcessor.hpp"
+#include "GatordMockService.hpp"
+#include "MockUtils.hpp"
+#include "PeriodicCounterCaptureCommandHandler.hpp"
+
+#include <string>
+
+int main(int argc, char *argv[])
+{
+ // Process command line arguments
+ armnn::gatordmock::CommandLineProcessor cmdLine;
+ if (!cmdLine.ProcessCommandLine(argc, argv))
+ {
+ return EXIT_FAILURE;
+ }
+
+ // Initialise functors and register into the CommandHandlerRegistry
+ uint32_t version = 1;
+
+ // Create headers
+ uint32_t counterCaptureCommandHeader = armnn::gatordmock::ConstructHeader(1,0,0);
+
+ // Create the Command Handler Registry
+ armnn::profiling::CommandHandlerRegistry registry;
+
+ // Update with derived functors
+ armnn::gatordmock::PeriodicCounterCaptureCommandHandler counterCaptureCommandHandler(counterCaptureCommandHeader,
+ version,
+ cmdLine.IsEchoEnabled());
+
+ // Register different derived functors
+ registry.RegisterFunctor(&counterCaptureCommandHandler);
+
+ armnn::gatordmock::GatordMockService mockService(registry, cmdLine.IsEchoEnabled());
+
+ if (!mockService.OpenListeningSocket(cmdLine.GetUdsNamespace()))
+ {
+ return EXIT_FAILURE;
+ }
+
+ // Wait for a single connection.
+ if (-1 == mockService.BlockForOneClient())
+ {
+ return EXIT_FAILURE;
+ }
+
+ // Send receive the strweam metadata and send connection ack.
+ if (!mockService.WaitForStreamMetaData())
+ {
+ return EXIT_FAILURE;
+ }
+ mockService.SendConnectionAck();
+
+ // Prepare to receive data.
+ mockService.LaunchReceivingThread();
+
+ // Process the SET and WAIT command from the file.
+ armnn::gatordmock::CommandFileParser commandLineParser;
+ commandLineParser.ParseFile(cmdLine.GetCommandFile(), mockService);
+
+ // Once we've finished processing the file wait for the receiving thread to close.
+ mockService.WaitForReceivingThread();
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+#include "GatordMockService.hpp"
+
+#include "../../src/profiling/CommandHandlerRegistry.hpp"
+
+#include <cerrno>
+#include <fcntl.h>
+#include <iostream>
+#include <poll.h>
+#include <string>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+
+namespace armnn
+{
+
+namespace gatordmock
+{
+
+bool GatordMockService::OpenListeningSocket(std::string udsNamespace)
+{
+ m_ListeningSocket = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (-1 == m_ListeningSocket)
+ {
+ std::cerr << ": Socket construction failed: " << strerror(errno) << std::endl;
+ return false;
+ }
+
+ sockaddr_un udsAddress;
+ memset(&udsAddress, 0, sizeof(sockaddr_un));
+ // We've set the first element of sun_path to be 0, skip over it and copy the namespace after it.
+ memcpy(udsAddress.sun_path + 1, udsNamespace.c_str(), strlen(udsNamespace.c_str()));
+ udsAddress.sun_family = AF_UNIX;
+
+ // Bind the socket to the UDS namespace.
+ if (-1 == bind(m_ListeningSocket, reinterpret_cast<const sockaddr *>(&udsAddress), sizeof(sockaddr_un)))
+ {
+ std::cerr << ": Binding on socket failed: " << strerror(errno) << std::endl;
+ return false;
+ }
+ // Listen for 1 connection.
+ if (-1 == listen(m_ListeningSocket, 1))
+ {
+ std::cerr << ": Listen call on socket failed: " << strerror(errno) << std::endl;
+ return false;
+ }
+ if (m_EchoPackets)
+ {
+ std::cout << "Bound to UDS namespace: \\0" << udsNamespace << std::endl;
+ }
+ return true;
+}
+
+int GatordMockService::BlockForOneClient()
+{
+ if (m_EchoPackets)
+ {
+ std::cout << "Waiting for client connection." << std::endl;
+ }
+ m_ClientConnection = accept4(m_ListeningSocket, nullptr, nullptr, SOCK_CLOEXEC);
+ if (-1 == m_ClientConnection)
+ {
+ std::cerr << ": Failure when waiting for a client connection: " << strerror(errno) << std::endl;
+ return -1;
+ }
+
+ if (m_EchoPackets)
+ {
+ std::cout << "Client connection established." << std::endl;
+ }
+ return m_ClientConnection;
+}
+
+bool GatordMockService::WaitForStreamMetaData()
+{
+ if (m_EchoPackets)
+ {
+ std::cout << "Waiting for stream meta data..." << std::endl;
+ }
+ // The start of the stream metadata is 2x32bit words, 0 and packet length.
+ u_char header[8];
+ if (!ReadFromSocket(header, 8))
+ {
+ return false;
+ }
+ EchoPacket(PacketDirection::Received, header, 8);
+ // The first word, stream_metadata_identifer, should always be 0.
+ if (ToUint32(&header[0], TargetEndianness::BeWire) != 0)
+ {
+ std::cerr << ": Protocol error. The stream_metadata_identifer was not 0." << std::endl;
+ return false;
+ }
+
+ u_char pipeMagic[4];
+ if (!ReadFromSocket(pipeMagic, 4))
+ {
+ return false;
+ }
+ EchoPacket(PacketDirection::Received, pipeMagic, 4);
+
+ // Before we interpret the length we need to read the pipe_magic word to determine endianness.
+ if (ToUint32(&pipeMagic[0], TargetEndianness::BeWire) == PIPE_MAGIC)
+ {
+ m_Endianness = TargetEndianness::BeWire;
+ }
+ else if (ToUint32(&pipeMagic[0], TargetEndianness::LeWire) == PIPE_MAGIC)
+ {
+ m_Endianness = TargetEndianness::LeWire;
+ }
+ else
+ {
+ std::cerr << ": Protocol read error. Unable to read PIPE_MAGIC value." << std::endl;
+ return false;
+ }
+ // Now we know the endianness we can get the length from the header.
+ // Remember we already read the pipe magic 4 bytes.
+ uint32_t metaDataLength = ToUint32(&header[4], m_Endianness) - 4;
+ // Read the entire packet.
+ u_char packetData[metaDataLength];
+ if (metaDataLength != read(m_ClientConnection, &packetData, metaDataLength))
+ {
+ std::cerr << ": Protocol read error. Data length mismatch." << std::endl;
+ return false;
+ }
+ EchoPacket(PacketDirection::Received, packetData, metaDataLength);
+ m_StreamMetaDataVersion = ToUint32(&packetData[0], m_Endianness);
+ m_StreamMetaDataMaxDataLen = ToUint32(&packetData[4], m_Endianness);
+ m_StreamMetaDataPid = ToUint32(&packetData[8], m_Endianness);
+
+ return true;
+}
+
+void GatordMockService::SendConnectionAck()
+{
+ if (m_EchoPackets)
+ {
+ std::cout << "Sending connection acknowledgement." << std::endl;
+ }
+ // The connection ack packet is an empty data packet with packetId == 1.
+ SendPacket(0, 1, nullptr, 0);
+}
+
+bool GatordMockService::LaunchReceivingThread()
+{
+ if (m_EchoPackets)
+ {
+ std::cout << "Launching receiving thread." << std::endl;
+ }
+ // At this point we want to make the socket non blocking.
+ const int currentFlags = fcntl(m_ClientConnection, F_GETFL);
+ if (0 != fcntl(m_ClientConnection, F_SETFL, currentFlags | O_NONBLOCK))
+ {
+ close(m_ClientConnection);
+ std::cerr << "Failed to set socket as non blocking: " << strerror(errno) << std::endl;
+ return false;
+ }
+ m_ListeningThread = std::thread(&GatordMockService::ReceiveLoop, this, std::ref(*this));
+ return true;
+}
+
+void GatordMockService::WaitForReceivingThread()
+{
+ m_CloseReceivingThread.store(true);
+ // Check that the receiving thread is running
+ if (m_ListeningThread.joinable())
+ {
+ // Wait for the receiving thread to complete operations
+ m_ListeningThread.join();
+ }
+}
+
+
+void GatordMockService::SendPeriodicCounterSelectionList(uint period, std::vector<uint16_t> counters)
+{
+ //get the datalength in bytes
+ uint32_t datalength = static_cast<uint32_t>(4 + counters.size() * 2);
+
+ u_char data[datalength];
+
+ *data = static_cast<u_char>(period >> 24);
+ *(data + 1) = static_cast<u_char>(period >> 16 & 0xFF);
+ *(data + 2) = static_cast<u_char>(period >> 8 & 0xFF);
+ *(data + 3) = static_cast<u_char>(period & 0xFF);
+
+ for (unsigned long i = 0; i < counters.size(); ++i)
+ {
+ *(data + 4 + i * 2) = static_cast<u_char>(counters[i] >> 8);
+ *(data + 5 + i * 2) = static_cast<u_char>(counters[i] & 0xFF);
+ }
+
+ // create packet send packet
+ SendPacket(0, 4, data, datalength);
+}
+
+void GatordMockService::WaitCommand(uint timeout)
+{
+ std::this_thread::sleep_for(std::chrono::microseconds(timeout));
+
+ if (m_EchoPackets)
+ {
+ std::cout << std::dec << "Wait command with timeout of " << timeout << " microseconds completed. " << std::endl;
+ }
+}
+
+void GatordMockService::ReceiveLoop(GatordMockService& mockService)
+{
+ m_CloseReceivingThread.store(false);
+ while (!m_CloseReceivingThread.load())
+ {
+ try
+ {
+ armnn::profiling::Packet packet = mockService.WaitForPacket(500);
+ }
+ catch(armnn::TimeoutException)
+ {
+ // In this case we ignore timeouts and and keep trying to receive.
+ }
+ }
+}
+
+armnn::profiling::Packet GatordMockService::WaitForPacket(uint32_t timeoutMs)
+{
+ // Is there currently more than a headers worth of data waiting to be read?
+ int bytes_available;
+ ioctl(m_ClientConnection, FIONREAD, &bytes_available);
+ if (bytes_available > 8)
+ {
+ // Yes there is. Read it:
+ return ReceivePacket();
+ }
+ else
+ {
+ // No there's not. Poll for more data.
+ struct pollfd pollingFd[1]{};
+ pollingFd[0].fd = m_ClientConnection;
+ int pollResult = poll(pollingFd, 1, static_cast<int>(timeoutMs));
+
+ switch (pollResult)
+ {
+ // Error
+ case -1:
+ throw armnn::RuntimeException(std::string("File descriptor reported an error during polling: ") +
+ strerror(errno));
+
+ // Timeout
+ case 0:
+ throw armnn::TimeoutException("Timeout while waiting to receive packet.");
+
+ // Normal poll return. It could still contain an error signal
+ default:
+
+ // Check if the socket reported an error
+ if (pollingFd[0].revents & (POLLNVAL | POLLERR | POLLHUP))
+ {
+ std::cout << "Error while polling receiving socket." << std::endl;
+ throw armnn::RuntimeException(std::string("File descriptor reported an error during polling: ") +
+ strerror(errno));
+ }
+
+ // Check if there is data to read
+ if (!(pollingFd[0].revents & (POLLIN)))
+ {
+ // This is a corner case. The socket as been woken up but not with any data.
+ // We'll throw a timeout exception to loop around again.
+ throw armnn::TimeoutException("File descriptor was polled but no data was available to receive.");
+ }
+ return ReceivePacket();
+ }
+ }
+}
+
+
+armnn::profiling::Packet GatordMockService::ReceivePacket()
+{
+ uint32_t header[2];
+ if (!ReadHeader(header))
+ {
+ return armnn::profiling::Packet();
+ }
+ // Read data_length bytes from the socket.
+ std::unique_ptr<unsigned char[]> uniquePacketData = std::make_unique<unsigned char[]>(header[1]);
+ unsigned char *packetData = reinterpret_cast<unsigned char *>(uniquePacketData.get());
+
+ if (!ReadFromSocket(packetData, header[1]))
+ {
+ return armnn::profiling::Packet();
+ }
+
+ // Construct received packet
+ armnn::profiling::Packet packetRx = armnn::profiling::Packet(header[0], header[1], uniquePacketData);
+
+ // Pass packet into the handler registry
+ if (packetRx.GetHeader()!= 0)
+ {
+ m_PacketsReceivedCount.operator++(std::memory_order::memory_order_release);
+ m_HandlerRegistry.GetFunctor(header[0],1)->operator()(packetRx);
+ }
+
+ EchoPacket(PacketDirection::Received, packetData, sizeof(packetData));
+ return packetRx;
+}
+
+bool GatordMockService::SendPacket(uint32_t packetFamily, uint32_t packetId, const u_char* data, uint32_t dataLength)
+{
+ // Construct a packet from the id and data given and send it to the client.
+ // Encode the header.
+ uint32_t header[2];
+ header[0] = packetFamily << 26 | packetId << 16;
+ header[1] = dataLength;
+ // Add the header to the packet.
+ u_char packet[8 + dataLength ];
+ InsertU32(header[0], packet, m_Endianness);
+ InsertU32(header[1], packet + 4, m_Endianness);
+ // And the rest of the data if there is any.
+ if (dataLength > 0)
+ {
+ memcpy((packet + 8), data, dataLength);
+ }
+ EchoPacket(PacketDirection::Sending, packet, sizeof(packet));
+ if (-1 == write(m_ClientConnection, packet, sizeof(packet)))
+ {
+ std::cerr << ": Failure when writing to client socket: " << strerror(errno) << std::endl;
+ return false;
+ }
+ return true;
+}
+
+bool GatordMockService::ReadHeader(uint32_t headerAsWords[2])
+{
+ // The herader will always be 2x32bit words.
+ u_char header[8];
+ if (!ReadFromSocket(header, 8))
+ {
+ return false;
+ }
+ headerAsWords[0] = ToUint32(&header[0], m_Endianness);
+ headerAsWords[1] = ToUint32(&header[4], m_Endianness);
+ return true;
+}
+
+bool GatordMockService::ReadFromSocket(u_char* packetData, uint32_t expectedLength)
+{
+ // This is a blocking read until either expectedLength has been received or an error is detected.
+ ssize_t totalBytesRead = 0;
+ while (totalBytesRead < expectedLength)
+ {
+ ssize_t bytesRead = recv(m_ClientConnection, packetData, expectedLength, 0);
+ if (bytesRead < 0)
+ {
+ std::cerr << ": Failure when reading from client socket: " << strerror(errno) << std::endl;
+ return false;
+ }
+ if (bytesRead == 0)
+ {
+ std::cerr << ": EOF while reading from client socket." << std::endl;
+ return false;
+ }
+ totalBytesRead += bytesRead;
+ }
+ return true;
+};
+
+void GatordMockService::EchoPacket(PacketDirection direction, u_char* packet, size_t lengthInBytes)
+{
+ // If enabled print the contents of the data packet to the console.
+ if (m_EchoPackets)
+ {
+ if (direction == PacketDirection::Sending)
+ {
+ std::cout << "Sending " << std::dec << lengthInBytes << " bytes : ";
+ } else
+ {
+ std::cout << "Received " << std::dec << lengthInBytes << " bytes : ";
+ }
+ for (unsigned int i = 0; i < lengthInBytes; i++)
+ {
+ if ((i % 10) == 0)
+ {
+ std::cout << std::endl;
+ }
+ std::cout << std::hex << "0x" << static_cast<unsigned int>(packet[i]) << " ";
+ }
+ std::cout << std::endl;
+ }
+}
+
+uint32_t GatordMockService::ToUint32(u_char* data, TargetEndianness endianness)
+{
+ // Extract the first 4 bytes starting at data and push them into a 32bit integer based on the
+ // specified endianness.
+ if (endianness == TargetEndianness::BeWire)
+ {
+ return static_cast<uint32_t>(data[0]) << 24 | static_cast<uint32_t>(data[1]) << 16 |
+ static_cast<uint32_t>(data[2]) << 8 | static_cast<uint32_t>(data[3]);
+ }
+ else
+ {
+ return static_cast<uint32_t>(data[3]) << 24 | static_cast<uint32_t>(data[2]) << 16 |
+ static_cast<uint32_t>(data[1]) << 8 | static_cast<uint32_t>(data[0]);
+ }
+}
+
+void GatordMockService::InsertU32(uint32_t value, u_char* data, TargetEndianness endianness)
+{
+ // Take the bytes of a 32bit integer and copy them into char array starting at data considering
+ // the endianness value.
+ if (endianness == TargetEndianness::BeWire)
+ {
+ *data = static_cast<u_char>((value >> 24) & 0xFF);
+ *(data + 1) = static_cast<u_char>((value >> 16) & 0xFF);
+ *(data + 2) = static_cast<u_char>((value >> 8) & 0xFF);
+ *(data + 3) = static_cast<u_char>(value & 0xFF);
+ }
+ else
+ {
+ *(data + 3) = static_cast<u_char>((value >> 24) & 0xFF);
+ *(data + 2) = static_cast<u_char>((value >> 16) & 0xFF);
+ *(data + 1) = static_cast<u_char>((value >> 8) & 0xFF);
+ *data = static_cast<u_char>(value & 0xFF);
+ }
+}
+
+} // namespace gatordmock
+
+} // namespace armnn
--- /dev/null
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <string>
+#include <thread>
+#include <atomic>
+
+#include "../../src/profiling/CommandHandlerRegistry.hpp"
+#include "../../src/profiling/Packet.hpp"
+
+namespace armnn
+{
+
+namespace gatordmock
+{
+
+enum class TargetEndianness
+{
+ BeWire, LeWire
+};
+
+enum class PacketDirection
+{
+ Sending, Received
+};
+
+/// A class that implements a Mock Gatord server. It will listen on a specified Unix domain socket (UDS)
+/// namespace for client connections. It will then allow opertaions to manage coutners while receiving counter data.
+class GatordMockService
+{
+public:
+
+
+ /// @param registry reference to a command handler registry.
+ /// @param echoPackets if true the raw packets will be printed to stdout.
+ GatordMockService(armnn::profiling::CommandHandlerRegistry& registry, bool echoPackets)
+ : m_HandlerRegistry(registry)
+ , m_EchoPackets(echoPackets)
+ {
+ m_PacketsReceivedCount.store(0, std::memory_order_relaxed);
+ }
+
+ ~GatordMockService()
+ {
+ // We have set SOCK_CLOEXEC on these sockets but we'll close them to be good citizens.
+ close(m_ClientConnection);
+ close(m_ListeningSocket);
+ }
+
+ /// Establish the Unix domain socket and set it to listen for connections.
+ /// @param udsNamespace the namespace (socket address) associated with the listener.
+ /// @return true only if the socket has been correctly setup.
+ bool OpenListeningSocket(std::string udsNamespace);
+
+ /// Block waiting to accept one client to connect to the UDS.
+ /// @return the file descriptor of the client connection.
+ int BlockForOneClient();
+
+ /// Once the connection is open wait to receive the stream meta data packet from the client. Reading this
+ /// packet differs from others as we need to determine endianness.
+ /// @return true only if a valid stream met data packet has been received.
+ bool WaitForStreamMetaData();
+
+ /// Send a connection acknowledged packet back to the client.
+ void SendConnectionAck();
+
+ /// Start the thread that will receive all packets and print them nicely to stdout.
+ bool LaunchReceivingThread();
+
+ /// Return the total number of periodic counter capture packets received since the receive thread started.
+ /// @return number of periodic counter capture packets received.
+ uint32_t GetPacketsReceivedCount()
+ {
+ return m_PacketsReceivedCount.load(std::memory_order_acquire);
+ }
+
+ /// This is a placeholder method to prevent main exiting. It can be removed once the
+ /// command handling code is added.
+ void WaitForReceivingThread();
+
+ /// Send the counter list to ArmNN.
+ void SendPeriodicCounterSelectionList(uint period, std::vector<uint16_t> counters);
+
+ /// Execute the WAIT command from the comamnd file.
+ void WaitCommand(uint timeout);
+
+ uint32_t GetStreamMetadataVersion()
+ {
+ return m_StreamMetaDataVersion;
+ }
+
+ uint32_t GetStreamMetadataMaxDataLen()
+ {
+ return m_StreamMetaDataMaxDataLen;
+ }
+
+ uint32_t GetStreamMetadataPid()
+ {
+ return m_StreamMetaDataPid;
+ }
+
+private:
+
+ void ReceiveLoop(GatordMockService& mockService);
+
+ /// Block on the client connection until a complete packet has been received. This is a placeholder function to
+ /// enable early testing of the tool.
+ /// @return true if a valid packet has been received.
+ armnn::profiling::Packet WaitForPacket(uint32_t timeoutMs);
+
+ armnn::profiling::Packet ReceivePacket();
+
+ bool SendPacket(uint32_t packetFamily, uint32_t packetId, const u_char* data, uint32_t dataLength);
+
+ void EchoPacket(PacketDirection direction, u_char* packet, size_t lengthInBytes);
+
+ bool ReadHeader(uint32_t headerAsWords[2]);
+
+ bool ReadFromSocket(u_char* packetData, uint32_t expectedLength);
+
+ uint32_t ToUint32(u_char* data, TargetEndianness endianness);
+
+ void InsertU32(uint32_t value, u_char* data, TargetEndianness endianness);
+
+ static const uint32_t PIPE_MAGIC = 0x45495434;
+
+ std::atomic<uint32_t> m_PacketsReceivedCount;
+ TargetEndianness m_Endianness;
+ uint32_t m_StreamMetaDataVersion;
+ uint32_t m_StreamMetaDataMaxDataLen;
+ uint32_t m_StreamMetaDataPid;
+
+ armnn::profiling::CommandHandlerRegistry& m_HandlerRegistry;
+
+ bool m_EchoPackets;
+ int m_ListeningSocket;
+ int m_ClientConnection;
+ std::thread m_ListeningThread;
+ std::atomic<bool> m_CloseReceivingThread;
+};
+} // namespace gatordmock
+
+} // namespace armnn
+
+
--- /dev/null
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <EncodeVersion.hpp>
+
+namespace armnn
+{
+
+namespace gatordmock
+{
+
+
+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);
+}
+
+} // gatordmock
+
+} // armnn
--- /dev/null
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include <iostream>
+
+#include "../../../src/profiling/ProfilingUtils.hpp"
+#include "PeriodicCounterCaptureCommandHandler.hpp"
+
+#include <boost/numeric/conversion/cast.hpp>
+
+namespace armnn
+{
+
+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;
+ std::vector<uint32_t> counterValues;
+
+ uint32_t sizeOfUint64 = numeric_cast<uint32_t>(sizeof(uint64_t));
+ uint32_t sizeOfUint32 = numeric_cast<uint32_t>(sizeof(uint32_t));
+ uint32_t sizeOfUint16 = numeric_cast<uint32_t>(sizeof(uint16_t));
+
+ uint32_t offset = 0;
+
+ if (packet.GetLength() >= 8)
+ {
+ offset = 0;
+
+ uint64_t timestamp = profiling::ReadUint64(reinterpret_cast<const unsigned char*>(packet.GetData()), offset);
+
+ if (m_FirstTimestamp == 0) // detect the first timestamp we receive.
+ {
+ m_FirstTimestamp = timestamp;
+ }
+ else
+ {
+ m_SecondTimestamp = timestamp;
+ m_CurrentPeriodValue = m_SecondTimestamp - m_FirstTimestamp;
+ m_FirstTimestamp = m_SecondTimestamp;
+ }
+
+ // Length minus timestamp and header divided by the length of an indexPair
+ unsigned int counters = (packet.GetLength() - 8) / 6;
+
+ if (counters > 0)
+ {
+ counterIds.reserve(counters);
+ counterValues.reserve(counters);
+ // Move offset over timestamp area
+ offset += sizeOfUint64;
+ for (unsigned int pos = 0; pos < counters; ++pos)
+ {
+ counterIds.emplace_back(
+ profiling::ReadUint16(reinterpret_cast<const unsigned char*>(packet.GetData()), offset));
+ offset += sizeOfUint16;
+
+ counterValues.emplace_back(
+ profiling::ReadUint32(reinterpret_cast<const unsigned char*>(packet.GetData()), offset));
+ offset += sizeOfUint32;
+ }
+ }
+
+ m_CounterCaptureValues.m_Timestamp = timestamp;
+ m_CounterCaptureValues.m_Uids = counterIds;
+ m_CounterCaptureValues.m_Values = counterValues;
+ }
+}
+
+void PeriodicCounterCaptureCommandHandler::operator()(const profiling::Packet& packet)
+{
+ ParseData(packet);
+ if (m_EchoPackets)
+ {
+ std::string header, body, uidString, valueString;
+
+ for (uint16_t uid : m_CounterCaptureValues.m_Uids)
+ {
+ uidString.append(std::to_string(uid));
+ uidString.append(", ");
+ }
+
+ for (uint32_t val : m_CounterCaptureValues.m_Values)
+ {
+ valueString.append(std::to_string(val));
+ valueString.append(", ");
+ }
+
+ body.append(gatordmock::CentreAlignFormatting(std::to_string(m_CounterCaptureValues.m_Timestamp), 10));
+ body.append(" | ");
+ body.append(gatordmock::CentreAlignFormatting(std::to_string(m_CurrentPeriodValue), 13));
+ body.append(" | ");
+ body.append(gatordmock::CentreAlignFormatting(uidString, 10));
+ body.append(" | ");
+ body.append(gatordmock::CentreAlignFormatting(valueString, 10));
+ body.append("\n");
+
+ if (!m_HeaderPrinted)
+ {
+ header.append(gatordmock::CentreAlignFormatting(" Timestamp", 11));
+ header.append(" | ");
+ header.append(gatordmock::CentreAlignFormatting("Period (us)", 13));
+ header.append(" | ");
+ header.append(gatordmock::CentreAlignFormatting("UID's", static_cast<int>(uidString.size())));
+ header.append(" | ");
+ header.append(gatordmock::CentreAlignFormatting("Values", 10));
+ header.append("\n");
+
+ std::cout << header;
+ m_HeaderPrinted = true;
+ }
+
+ std::cout << std::string(body.size(), '-') << "\n";
+
+ std::cout << body;
+ }
+}
+
+} // namespace gatordmock
+
+} // namespace armnn
\ No newline at end of file
--- /dev/null
+//
+// Copyright © 2019 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "../../armnn/src/profiling/Packet.hpp"
+#include "../../armnn/src/profiling/CommandHandlerFunctor.hpp"
+
+#include <vector>
+
+namespace armnn
+{
+
+namespace gatordmock
+{
+
+struct CounterCaptureValues
+{
+ uint64_t m_Timestamp;
+ std::vector<uint16_t> m_Uids;
+ std::vector<uint32_t> m_Values;
+};
+
+class PeriodicCounterCaptureCommandHandler : public profiling::CommandHandlerFunctor
+{
+
+public:
+ PeriodicCounterCaptureCommandHandler(uint32_t packetId,
+ uint32_t version,
+ bool echoPackets)
+ : CommandHandlerFunctor(packetId, version)
+ , m_EchoPackets(echoPackets)
+ {}
+
+ void operator()(const armnn::profiling::Packet& packet) override;
+
+ CounterCaptureValues m_CounterCaptureValues;
+
+ uint64_t m_CurrentPeriodValue = 0;
+
+private:
+ void ParseData(const armnn::profiling::Packet& packet);
+
+ uint64_t m_FirstTimestamp = 0, m_SecondTimestamp = 0;
+
+ bool m_HeaderPrinted = false;
+ bool m_EchoPackets;
+};
+
+} // namespace gatordmock
+
+} // namespace armnn
\ No newline at end of file
--- /dev/null
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "../MockUtils.hpp"
+#include "../PeriodicCounterCaptureCommandHandler.hpp"
+
+#include "../../src/profiling/CommandHandlerRegistry.hpp"
+#include "../GatordMockService.hpp"
+
+#include "../../src/profiling/ProfilingService.hpp"
+#include "../../src/profiling/test/SendCounterPacketTests.hpp"
+
+#include <boost/cast.hpp>
+#include <boost/test/test_tools.hpp>
+#include <boost/test/unit_test_suite.hpp>
+
+BOOST_AUTO_TEST_SUITE(GatordMockTests)
+
+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))
+
+BOOST_AUTO_TEST_CASE(CounterCaptureHandlingTest)
+{
+ using boost::numeric_cast;
+
+ // Initialise functors and register into the CommandHandlerRegistry
+ uint32_t headerWord1 = gatordmock::ConstructHeader(1, 0, 0);
+
+ // Create the Command Handler Registry
+ profiling::CommandHandlerRegistry registry;
+
+ // Data with timestamp, counter idx & counter values
+ std::vector<std::pair<uint16_t, uint32_t>> indexValuePairs;
+ indexValuePairs.reserve(5);
+ indexValuePairs.emplace_back(std::make_pair<uint16_t, uint32_t>(0, 100));
+ indexValuePairs.emplace_back(std::make_pair<uint16_t, uint32_t>(1, 200));
+ indexValuePairs.emplace_back(std::make_pair<uint16_t, uint32_t>(2, 300));
+ indexValuePairs.emplace_back(std::make_pair<uint16_t, uint32_t>(3, 400));
+ indexValuePairs.emplace_back(std::make_pair<uint16_t, uint32_t>(4, 500));
+
+ // ((uint16_t (2 bytes) + uint32_t (4 bytes)) * 5) + word1 + word2
+ uint32_t dataLength = 38;
+
+ // Simulate two different packets incoming 500 ms apart
+ uint64_t time = static_cast<uint64_t>(
+ std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now().time_since_epoch())
+ .count());
+
+ sleep_for(5000us);
+
+ uint64_t time2 = static_cast<uint64_t>(
+ std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now().time_since_epoch())
+ .count());
+
+ // UniqueData required for Packet class
+ std::unique_ptr<unsigned char[]> uniqueData1 = std::make_unique<unsigned char[]>(dataLength);
+ unsigned char* data1 = reinterpret_cast<unsigned char*>(uniqueData1.get());
+
+ std::unique_ptr<unsigned char[]> uniqueData2 = std::make_unique<unsigned char[]>(dataLength);
+ unsigned char* data2 = reinterpret_cast<unsigned char*>(uniqueData2.get());
+
+ uint32_t sizeOfUint64 = numeric_cast<uint32_t>(sizeof(uint64_t));
+ uint32_t sizeOfUint32 = numeric_cast<uint32_t>(sizeof(uint32_t));
+ uint32_t sizeOfUint16 = numeric_cast<uint32_t>(sizeof(uint16_t));
+ // Offset index to point to mem address
+ uint32_t offset = 0;
+
+ profiling::WriteUint64(data1, offset, time);
+ offset += sizeOfUint64;
+ for (const auto& pair : indexValuePairs)
+ {
+ profiling::WriteUint16(data1, offset, pair.first);
+ offset += sizeOfUint16;
+ profiling::WriteUint32(data1, offset, pair.second);
+ offset += sizeOfUint32;
+ }
+
+ offset = 0;
+
+ profiling::WriteUint64(data2, offset, time2);
+ offset += sizeOfUint64;
+ for (const auto& pair : indexValuePairs)
+ {
+ profiling::WriteUint16(data2, offset, pair.first);
+ offset += sizeOfUint16;
+ profiling::WriteUint32(data2, offset, pair.second);
+ offset += sizeOfUint32;
+ }
+
+ // 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(headerWord1, version, false);
+
+ // Simulate two separate packets coming in to calculate period
+ commandHandler(packet1);
+ commandHandler(packet2);
+
+ BOOST_ASSERT(4500 < commandHandler.m_CurrentPeriodValue && 5500 > commandHandler.m_CurrentPeriodValue);
+
+ for (size_t i = 0; i < commandHandler.m_CounterCaptureValues.m_Uids.size(); ++i)
+ {
+ BOOST_ASSERT(commandHandler.m_CounterCaptureValues.m_Uids[i] == i);
+ }
+}
+
+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 counterCaptureHeader = gatordmock::ConstructHeader(1, 0);
+ uint32_t version = 1;
+
+ // Create the Command Handler Registry
+ profiling::CommandHandlerRegistry registry;
+
+ // Update with derived functors
+ gatordmock::PeriodicCounterCaptureCommandHandler counterCaptureCommandHandler(counterCaptureHeader, version, false);
+ // Register different derived functors
+ registry.RegisterFunctor(&counterCaptureCommandHandler);
+
+ // Setup the mock service to bind to the UDS.
+ std::string udsNamespace = "gatord_namespace";
+ gatordmock::GatordMockService mockService(registry, false);
+ mockService.OpenListeningSocket(udsNamespace);
+
+ // Enable the profiling service.
+ armnn::Runtime::CreationOptions::ExternalProfilingOptions options;
+ options.m_EnableProfiling = true;
+ profiling::ProfilingService& profilingService = profiling::ProfilingService::Instance();
+ profilingService.ResetExternalProfilingOptions(options, true);
+
+ // Bring the profiling service to the "WaitingForAck" state
+ BOOST_CHECK(profilingService.GetCurrentState() == profiling::ProfilingState::Uninitialised);
+ profilingService.Update();
+ BOOST_CHECK(profilingService.GetCurrentState() == profiling::ProfilingState::NotConnected);
+ profilingService.Update();
+
+ // Connect the profiling service to the mock Gatord.
+ int clientFd = mockService.BlockForOneClient();
+ if (-1 == clientFd)
+ {
+ 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);
+ profilingService.Update();
+ // Read the stream metadata on the mock side.
+ if (!mockService.WaitForStreamMetaData())
+ {
+ BOOST_FAIL("Failed to receive StreamMetaData");
+ }
+ // 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.
+
+ options.m_EnableProfiling = false;
+ profilingService.ResetExternalProfilingOptions(options, true);
+}
+
+BOOST_AUTO_TEST_SUITE_END()