diff options
author | Colm Donelan <Colm.Donelan@arm.com> | 2019-10-11 13:09:49 +0100 |
---|---|---|
committer | Colm Donelan <Colm.Donelan@arm.com> | 2019-10-11 13:12:37 +0100 |
commit | a21620d32a8a0a8d527c061e2a22d51009d75877 (patch) | |
tree | b08ffee4cddb1bb3b1d206c67ea80bc2093d7bf5 /tests/profiling/gatordmock/GatordMockService.cpp | |
parent | 67ef2a52c3cdcc37538d77711bbcea2f0e5655e5 (diff) | |
download | armnn-a21620d32a8a0a8d527c061e2a22d51009d75877.tar.gz |
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 <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
Diffstat (limited to 'tests/profiling/gatordmock/GatordMockService.cpp')
-rw-r--r-- | tests/profiling/gatordmock/GatordMockService.cpp | 431 |
1 files changed, 431 insertions, 0 deletions
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 <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 |