From a21620d32a8a0a8d527c061e2a22d51009d75877 Mon Sep 17 00:00:00 2001 From: Colm Donelan Date: Fri, 11 Oct 2019 13:09:49 +0100 Subject: IVGCVSW-3721 Add support for startup sequence (Mock Gatord service). * 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 Signed-off-by: Keith Davis Signed-off-by: Mike Kelly Signed-off-by: Finn Williams Signed-off-by: Kevin May Change-Id: I33c1c9f93976708c9315f71290d42cff53b8c075 --- tests/profiling/CommandLineProcessor.cpp | 63 --- tests/profiling/CommandLineProcessor.hpp | 37 -- tests/profiling/GatordMockMain.cpp | 27 -- tests/profiling/GatordMockService.cpp | 71 ---- tests/profiling/GatordMockService.hpp | 50 --- tests/profiling/gatordmock/CommandFileParser.cpp | 76 ++++ tests/profiling/gatordmock/CommandFileParser.hpp | 31 ++ .../profiling/gatordmock/CommandLineProcessor.cpp | 72 ++++ .../profiling/gatordmock/CommandLineProcessor.hpp | 39 ++ tests/profiling/gatordmock/GatordMockMain.cpp | 71 ++++ tests/profiling/gatordmock/GatordMockService.cpp | 431 +++++++++++++++++++++ tests/profiling/gatordmock/GatordMockService.hpp | 149 +++++++ tests/profiling/gatordmock/MockUtils.hpp | 35 ++ .../PeriodicCounterCaptureCommandHandler.cpp | 147 +++++++ .../PeriodicCounterCaptureCommandHandler.hpp | 54 +++ .../profiling/gatordmock/tests/GatordMockTests.cpp | 178 +++++++++ 16 files changed, 1283 insertions(+), 248 deletions(-) delete mode 100644 tests/profiling/CommandLineProcessor.cpp delete mode 100644 tests/profiling/CommandLineProcessor.hpp delete mode 100644 tests/profiling/GatordMockMain.cpp delete mode 100644 tests/profiling/GatordMockService.cpp delete mode 100644 tests/profiling/GatordMockService.hpp create mode 100644 tests/profiling/gatordmock/CommandFileParser.cpp create mode 100644 tests/profiling/gatordmock/CommandFileParser.hpp create mode 100644 tests/profiling/gatordmock/CommandLineProcessor.cpp create mode 100644 tests/profiling/gatordmock/CommandLineProcessor.hpp create mode 100644 tests/profiling/gatordmock/GatordMockMain.cpp create mode 100644 tests/profiling/gatordmock/GatordMockService.cpp create mode 100644 tests/profiling/gatordmock/GatordMockService.hpp create mode 100644 tests/profiling/gatordmock/MockUtils.hpp create mode 100644 tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.cpp create mode 100644 tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.hpp create mode 100644 tests/profiling/gatordmock/tests/GatordMockTests.cpp (limited to 'tests') diff --git a/tests/profiling/CommandLineProcessor.cpp b/tests/profiling/CommandLineProcessor.cpp deleted file mode 100644 index 1b431c0436..0000000000 --- a/tests/profiling/CommandLineProcessor.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright © 2019 Arm Ltd. All rights reserved. -// SPDX-License-Identifier: MIT -// - -#include "CommandLineProcessor.hpp" - -#include - -#include - -namespace armnn -{ -namespace gatordmock -{ - -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") - ("namespace,n", po::value(&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"); - } - catch (const std::exception& e) - { - std::cerr << "Fatal internal error: [" << e.what() << "]" << std::endl; - return false; - } - - po::variables_map vm; - try - { - po::store(po::parse_command_line(argc, argv, desc), vm); - - if (vm.count("help")) - { - std::cout << "Simulate a Gatord server to interact with ArmNN external profiling." << std::endl; - std::cout << std::endl; - std::cout << desc << std::endl; - return false; - } - - po::notify(vm); - } - catch (const po::error& e) - { - std::cerr << e.what() << std::endl << std::endl; - std::cerr << desc << std::endl; - return false; - } - - return true; -} - -} // namespace gatordmock - -} // namespace armnn \ No newline at end of file diff --git a/tests/profiling/CommandLineProcessor.hpp b/tests/profiling/CommandLineProcessor.hpp deleted file mode 100644 index a7e43ada12..0000000000 --- a/tests/profiling/CommandLineProcessor.hpp +++ /dev/null @@ -1,37 +0,0 @@ -// -// Copyright © 2019 Arm Ltd. All rights reserved. -// SPDX-License-Identifier: MIT -// -#pragma once - -#include - -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 diff --git a/tests/profiling/GatordMockMain.cpp b/tests/profiling/GatordMockMain.cpp deleted file mode 100644 index b8edeb2b2d..0000000000 --- a/tests/profiling/GatordMockMain.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// -// 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; -} diff --git a/tests/profiling/GatordMockService.cpp b/tests/profiling/GatordMockService.cpp deleted file mode 100644 index c774ab0b45..0000000000 --- a/tests/profiling/GatordMockService.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copyright © 2019 Arm Ltd. All rights reserved. -// SPDX-License-Identifier: MIT -// - -#include "GatordMockService.hpp" - -#include -#include -#include -#include -#include -#include - -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(&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 diff --git a/tests/profiling/GatordMockService.hpp b/tests/profiling/GatordMockService.hpp deleted file mode 100644 index c19e710d69..0000000000 --- a/tests/profiling/GatordMockService.hpp +++ /dev/null @@ -1,50 +0,0 @@ -// -// Copyright © 2019 Arm Ltd. All rights reserved. -// SPDX-License-Identifier: MIT -// - -#pragma once - -#include - -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 - - diff --git a/tests/profiling/gatordmock/CommandFileParser.cpp b/tests/profiling/gatordmock/CommandFileParser.cpp new file mode 100644 index 0000000000..e86763b55f --- /dev/null +++ b/tests/profiling/gatordmock/CommandFileParser.cpp @@ -0,0 +1,76 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "CommandFileParser.hpp" + +#include +#include +#include +#include + +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 tokens; + + std::copy(std::istream_iterator(iss), std::istream_iterator(), + 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(std::stoul(tokens[1])); + + std::vector counters; + + std::transform(tokens.begin() + 2, tokens.end(), std::back_inserter(counters), + [](const std::string& str) { return static_cast(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(std::stoul(tokens[1])); + + mockService.WaitCommand(timeout); + } + } +} + +} // namespace gatordmock + +} // namespace armnn diff --git a/tests/profiling/gatordmock/CommandFileParser.hpp b/tests/profiling/gatordmock/CommandFileParser.hpp new file mode 100644 index 0000000000..e95395d206 --- /dev/null +++ b/tests/profiling/gatordmock/CommandFileParser.hpp @@ -0,0 +1,31 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include +#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 + + diff --git a/tests/profiling/gatordmock/CommandLineProcessor.cpp b/tests/profiling/gatordmock/CommandLineProcessor.cpp new file mode 100644 index 0000000000..55b51137bc --- /dev/null +++ b/tests/profiling/gatordmock/CommandLineProcessor.cpp @@ -0,0 +1,72 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "CommandLineProcessor.hpp" + +#include +#include + +namespace armnn +{ +namespace gatordmock +{ + +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(&m_File), + "The path to the file that contains instructions for the mock gatord") + ("namespace,n", po::value(&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") + ("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::cerr << "Fatal internal error: [" << e.what() << "]" << std::endl; + return false; + } + + po::variables_map vm; + try + { + po::store(po::parse_command_line(argc, argv, desc), vm); + + if (vm.count("help")) + { + std::cout << "Simulate a Gatord server to interact with ArmNN external profiling." << std::endl; + std::cout << std::endl; + 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) + { + std::cerr << e.what() << std::endl << std::endl; + std::cerr << desc << std::endl; + return false; + } + + return true; +} + +} // namespace gatordmock + +} // namespace armnn \ No newline at end of file diff --git a/tests/profiling/gatordmock/CommandLineProcessor.hpp b/tests/profiling/gatordmock/CommandLineProcessor.hpp new file mode 100644 index 0000000000..0eed23ace8 --- /dev/null +++ b/tests/profiling/gatordmock/CommandLineProcessor.hpp @@ -0,0 +1,39 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// +#pragma once + +#include + +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 diff --git a/tests/profiling/gatordmock/GatordMockMain.cpp b/tests/profiling/gatordmock/GatordMockMain.cpp new file mode 100644 index 0000000000..500b016669 --- /dev/null +++ b/tests/profiling/gatordmock/GatordMockMain.cpp @@ -0,0 +1,71 @@ +// +// 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 + +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; +} diff --git a/tests/profiling/gatordmock/GatordMockService.cpp b/tests/profiling/gatordmock/GatordMockService.cpp new file mode 100644 index 0000000000..f4146c2e38 --- /dev/null +++ b/tests/profiling/gatordmock/GatordMockService.cpp @@ -0,0 +1,431 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// +#include "GatordMockService.hpp" + +#include "../../src/profiling/CommandHandlerRegistry.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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(&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 counters) +{ + //get the datalength in bytes + uint32_t datalength = static_cast(4 + counters.size() * 2); + + u_char data[datalength]; + + *data = static_cast(period >> 24); + *(data + 1) = static_cast(period >> 16 & 0xFF); + *(data + 2) = static_cast(period >> 8 & 0xFF); + *(data + 3) = static_cast(period & 0xFF); + + for (unsigned long i = 0; i < counters.size(); ++i) + { + *(data + 4 + i * 2) = static_cast(counters[i] >> 8); + *(data + 5 + i * 2) = static_cast(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(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 uniquePacketData = std::make_unique(header[1]); + unsigned char *packetData = reinterpret_cast(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(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(data[0]) << 24 | static_cast(data[1]) << 16 | + static_cast(data[2]) << 8 | static_cast(data[3]); + } + else + { + return static_cast(data[3]) << 24 | static_cast(data[2]) << 16 | + static_cast(data[1]) << 8 | static_cast(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((value >> 24) & 0xFF); + *(data + 1) = static_cast((value >> 16) & 0xFF); + *(data + 2) = static_cast((value >> 8) & 0xFF); + *(data + 3) = static_cast(value & 0xFF); + } + else + { + *(data + 3) = static_cast((value >> 24) & 0xFF); + *(data + 2) = static_cast((value >> 16) & 0xFF); + *(data + 1) = static_cast((value >> 8) & 0xFF); + *data = static_cast(value & 0xFF); + } +} + +} // namespace gatordmock + +} // namespace armnn diff --git a/tests/profiling/gatordmock/GatordMockService.hpp b/tests/profiling/gatordmock/GatordMockService.hpp new file mode 100644 index 0000000000..1aba2e8a09 --- /dev/null +++ b/tests/profiling/gatordmock/GatordMockService.hpp @@ -0,0 +1,149 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include +#include +#include + +#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 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 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 m_CloseReceivingThread; +}; +} // namespace gatordmock + +} // namespace armnn + + diff --git a/tests/profiling/gatordmock/MockUtils.hpp b/tests/profiling/gatordmock/MockUtils.hpp new file mode 100644 index 0000000000..93fc408037 --- /dev/null +++ b/tests/profiling/gatordmock/MockUtils.hpp @@ -0,0 +1,35 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include + +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 diff --git a/tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.cpp b/tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.cpp new file mode 100644 index 0000000000..5a70f68805 --- /dev/null +++ b/tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.cpp @@ -0,0 +1,147 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include + +#include "../../../src/profiling/ProfilingUtils.hpp" +#include "PeriodicCounterCaptureCommandHandler.hpp" + +#include + +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(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 counterIds; + std::vector counterValues; + + uint32_t sizeOfUint64 = numeric_cast(sizeof(uint64_t)); + uint32_t sizeOfUint32 = numeric_cast(sizeof(uint32_t)); + uint32_t sizeOfUint16 = numeric_cast(sizeof(uint16_t)); + + uint32_t offset = 0; + + if (packet.GetLength() >= 8) + { + offset = 0; + + uint64_t timestamp = profiling::ReadUint64(reinterpret_cast(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(packet.GetData()), offset)); + offset += sizeOfUint16; + + counterValues.emplace_back( + profiling::ReadUint32(reinterpret_cast(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(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 diff --git a/tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.hpp b/tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.hpp new file mode 100644 index 0000000000..7f46d8e102 --- /dev/null +++ b/tests/profiling/gatordmock/PeriodicCounterCaptureCommandHandler.hpp @@ -0,0 +1,54 @@ +// +// 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 + +namespace armnn +{ + +namespace gatordmock +{ + +struct CounterCaptureValues +{ + uint64_t m_Timestamp; + std::vector m_Uids; + std::vector 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 diff --git a/tests/profiling/gatordmock/tests/GatordMockTests.cpp b/tests/profiling/gatordmock/tests/GatordMockTests.cpp new file mode 100644 index 0000000000..100b7b8a60 --- /dev/null +++ b/tests/profiling/gatordmock/tests/GatordMockTests.cpp @@ -0,0 +1,178 @@ +// +// 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 +#include +#include + +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> indexValuePairs; + indexValuePairs.reserve(5); + indexValuePairs.emplace_back(std::make_pair(0, 100)); + indexValuePairs.emplace_back(std::make_pair(1, 200)); + indexValuePairs.emplace_back(std::make_pair(2, 300)); + indexValuePairs.emplace_back(std::make_pair(3, 400)); + indexValuePairs.emplace_back(std::make_pair(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( + std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) + .count()); + + sleep_for(5000us); + + uint64_t time2 = static_cast( + std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) + .count()); + + // UniqueData required for Packet class + std::unique_ptr uniqueData1 = std::make_unique(dataLength); + unsigned char* data1 = reinterpret_cast(uniqueData1.get()); + + std::unique_ptr uniqueData2 = std::make_unique(dataLength); + unsigned char* data2 = reinterpret_cast(uniqueData2.get()); + + uint32_t sizeOfUint64 = numeric_cast(sizeof(uint64_t)); + uint32_t sizeOfUint32 = numeric_cast(sizeof(uint32_t)); + uint32_t sizeOfUint16 = numeric_cast(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() -- cgit v1.2.1