Using new RAII epoll instance wrapper in GDB server and Connection class

This commit is contained in:
Nav
2022-03-28 01:04:14 +01:00
parent 2b55f8f5ea
commit b339bfe016
6 changed files with 77 additions and 114 deletions

View File

@@ -38,19 +38,7 @@ namespace Bloom::DebugServers::Gdb
fcntl(this->socketFileDescriptor, F_GETFL, 0) | O_NONBLOCK fcntl(this->socketFileDescriptor, F_GETFL, 0) | O_NONBLOCK
); );
// Create event FD this->epollInstance.addEntry(this->socketFileDescriptor, static_cast<std::uint16_t>(EpollEvent::READ_READY));
this->eventFileDescriptor = ::epoll_create(2);
struct epoll_event event = {};
event.events = EPOLLIN;
event.data.fd = this->socketFileDescriptor;
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, this->socketFileDescriptor, &event) != 0) {
throw Exception(
"Failed to create event FD for GDB client connection - could not add client connection "
"socket FD to epoll FD"
);
}
this->enableReadInterrupts(); this->enableReadInterrupts();
} }
@@ -157,7 +145,11 @@ namespace Bloom::DebugServers::Gdb
} while (this->readSingleByte(false).value_or(0) != '+'); } while (this->readSingleByte(false).value_or(0) != '+');
} }
std::vector<unsigned char> Connection::read(size_t bytes, bool interruptible, std::optional<int> msTimeout) { std::vector<unsigned char> Connection::read(
size_t bytes,
bool interruptible,
std::optional<std::chrono::milliseconds> timeout
) {
auto output = std::vector<unsigned char>(); auto output = std::vector<unsigned char>();
constexpr size_t bufferSize = 1024; constexpr size_t bufferSize = 1024;
std::array<unsigned char, bufferSize> buffer = {}; std::array<unsigned char, bufferSize> buffer = {};
@@ -177,25 +169,16 @@ namespace Bloom::DebugServers::Gdb
this->disableReadInterrupts(); this->disableReadInterrupts();
} }
std::array<struct epoll_event, 1> events = {}; const auto eventFileDescriptor = this->epollInstance.waitForEvent(timeout);
int eventCount = ::epoll_wait( if (
this->eventFileDescriptor, !eventFileDescriptor.has_value()
events.data(), || eventFileDescriptor.value() == this->interruptEventNotifier.getFileDescriptor()
1, ) {
msTimeout.value_or(-1)
);
if (eventCount > 0) {
for (size_t i = 0; i < eventCount; i++) {
auto fileDescriptor = events.at(i).data.fd;
if (fileDescriptor == this->interruptEventNotifier.getFileDescriptor()) {
// Interrupted // Interrupted
this->interruptEventNotifier.clear(); this->interruptEventNotifier.clear();
throw DebugServerInterrupted(); throw DebugServerInterrupted();
} }
}
size_t bytesToRead = (bytes > bufferSize || bytes == 0) ? bufferSize : bytes; size_t bytesToRead = (bytes > bufferSize || bytes == 0) ? bufferSize : bytes;
while ( while (
@@ -209,21 +192,19 @@ namespace Bloom::DebugServers::Gdb
break; break;
} }
bytesToRead = bytesToRead = ((bytes - output.size()) > bufferSize || bytes == 0) ? bufferSize : (bytes - output.size());
((bytes - output.size()) > bufferSize || bytes == 0) ? bufferSize : (bytes - output.size());
} }
if (output.empty()) { if (output.empty()) {
// EOF means the client has disconnected // EOF means the client has disconnected
throw ClientDisconnected(); throw ClientDisconnected();
} }
}
return output; return output;
} }
std::optional<unsigned char> Connection::readSingleByte(bool interruptible) { std::optional<unsigned char> Connection::readSingleByte(bool interruptible) {
auto bytes = this->read(1, interruptible, 300); auto bytes = this->read(1, interruptible, std::chrono::milliseconds(300));
if (!bytes.empty()) { if (!bytes.empty()) {
return bytes.front(); return bytes.front();
@@ -247,27 +228,16 @@ namespace Bloom::DebugServers::Gdb
} }
void Connection::disableReadInterrupts() { void Connection::disableReadInterrupts() {
if (::epoll_ctl( this->epollInstance.removeEntry(this->interruptEventNotifier.getFileDescriptor());
this->eventFileDescriptor,
EPOLL_CTL_DEL,
this->interruptEventNotifier.getFileDescriptor(),
NULL) != 0
) {
throw Exception("Failed to disable GDB client connection read interrupts - epoll_ctl failed");
}
this->readInterruptEnabled = false; this->readInterruptEnabled = false;
} }
void Connection::enableReadInterrupts() { void Connection::enableReadInterrupts() {
auto interruptFileDescriptor = this->interruptEventNotifier.getFileDescriptor(); this->epollInstance.addEntry(
struct epoll_event event = {}; this->interruptEventNotifier.getFileDescriptor(),
event.events = EPOLLIN; static_cast<std::uint16_t>(EpollEvent::READ_READY)
event.data.fd = interruptFileDescriptor; );
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, interruptFileDescriptor, &event) != 0) {
throw Exception("Failed to enable GDB client connection read interrupts - epoll_ctl failed");
}
this->readInterruptEnabled = true; this->readInterruptEnabled = true;
} }

View File

@@ -8,8 +8,10 @@
#include <array> #include <array>
#include <sys/socket.h> #include <sys/socket.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <chrono>
#include "src/Helpers/EventNotifier.hpp" #include "src/Helpers/EventNotifier.hpp"
#include "src/Helpers/EpollInstance.hpp"
#include "src/DebugServers/GdbRsp/Packet.hpp" #include "src/DebugServers/GdbRsp/Packet.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/ResponsePacket.hpp" #include "src/DebugServers/GdbRsp/ResponsePackets/ResponsePacket.hpp"
@@ -22,12 +24,21 @@ namespace Bloom::DebugServers::Gdb
class Connection class Connection
{ {
public: public:
Connection() = delete;
explicit Connection(EventNotifier& interruptEventNotifier) explicit Connection(EventNotifier& interruptEventNotifier)
: interruptEventNotifier(interruptEventNotifier) : interruptEventNotifier(interruptEventNotifier)
{}; {};
Connection() = delete;
Connection(const Connection&) = delete;
Connection(Connection&& other) noexcept
: interruptEventNotifier(other.interruptEventNotifier)
, socketFileDescriptor(other.socketFileDescriptor)
, epollInstance(std::move(other.epollInstance))
, readInterruptEnabled(other.readInterruptEnabled)
{
other.socketFileDescriptor = -1;
};
/** /**
* Accepts a connection on serverSocketFileDescriptor. * Accepts a connection on serverSocketFileDescriptor.
* *
@@ -75,7 +86,8 @@ namespace Bloom::DebugServers::Gdb
private: private:
int socketFileDescriptor = -1; int socketFileDescriptor = -1;
int eventFileDescriptor = -1;
EpollInstance epollInstance = EpollInstance();
struct sockaddr_in socketAddress = {}; struct sockaddr_in socketAddress = {};
int maxPacketSize = 1024; int maxPacketSize = 1024;
@@ -98,7 +110,7 @@ namespace Bloom::DebugServers::Gdb
* the read (via means of this->interruptEventNotifier). This flag has no effect if this->readInterruptEnabled * the read (via means of this->interruptEventNotifier). This flag has no effect if this->readInterruptEnabled
* is false. * is false.
* *
* @param msTimeout * @param timeout
* The timeout in milliseconds. If not supplied, no timeout will be applied. * The timeout in milliseconds. If not supplied, no timeout will be applied.
* *
* @return * @return
@@ -106,7 +118,7 @@ namespace Bloom::DebugServers::Gdb
std::vector<unsigned char> read( std::vector<unsigned char> read(
std::size_t bytes = 0, std::size_t bytes = 0,
bool interruptible = true, bool interruptible = true,
std::optional<int> msTimeout = std::nullopt std::optional<std::chrono::milliseconds> timeout = std::nullopt
); );
/** /**

View File

@@ -4,8 +4,8 @@
namespace Bloom::DebugServers::Gdb namespace Bloom::DebugServers::Gdb
{ {
DebugSession::DebugSession(const Connection& connection, const TargetDescriptor& targetDescriptor) DebugSession::DebugSession(Connection&& connection, const TargetDescriptor& targetDescriptor)
: connection(connection) : connection(std::move(connection))
, targetDescriptor(targetDescriptor) , targetDescriptor(targetDescriptor)
{} {}

View File

@@ -20,7 +20,7 @@ namespace Bloom::DebugServers::Gdb
*/ */
bool waitingForBreak = false; bool waitingForBreak = false;
DebugSession(const Connection& connection, const TargetDescriptor& targetDescriptor); DebugSession(Connection&& connection, const TargetDescriptor& targetDescriptor);
void terminate(); void terminate();
}; };

View File

@@ -92,22 +92,15 @@ namespace Bloom::DebugServers::Gdb
this->serverSocketFileDescriptor = socketFileDescriptor; this->serverSocketFileDescriptor = socketFileDescriptor;
this->eventFileDescriptor = ::epoll_create(2); this->epollInstance.addEntry(
struct epoll_event event = {}; this->serverSocketFileDescriptor,
event.events = EPOLLIN; static_cast<std::uint16_t>(EpollEvent::READ_READY)
event.data.fd = this->serverSocketFileDescriptor; );
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, this->serverSocketFileDescriptor, &event) != 0) { this->epollInstance.addEntry(
throw Exception("Failed epoll_ctl server socket"); this->interruptEventNotifier->getFileDescriptor(),
} static_cast<std::uint16_t>(EpollEvent::READ_READY)
);
const auto interruptFileDescriptor = this->interruptEventNotifier->getFileDescriptor();
event.events = EPOLLIN;
event.data.fd = interruptFileDescriptor;
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, interruptFileDescriptor, &event) != 0) {
throw Exception("Failed epoll_ctl interrupt event fd");
}
Logger::info("GDB RSP address: " + this->debugServerConfig->listeningAddress); Logger::info("GDB RSP address: " + this->debugServerConfig->listeningAddress);
Logger::info("GDB RSP port: " + std::to_string(this->debugServerConfig->listeningPortNumber)); Logger::info("GDB RSP port: " + std::to_string(this->debugServerConfig->listeningPortNumber));
@@ -145,7 +138,7 @@ namespace Bloom::DebugServers::Gdb
Logger::info("Accepted GDP RSP connection from " + connection->getIpAddress()); Logger::info("Accepted GDP RSP connection from " + connection->getIpAddress());
this->activeDebugSession.emplace( this->activeDebugSession.emplace(
DebugSession(connection.value(), this->getGdbTargetDescriptor()) DebugSession(std::move(connection.value()), this->getGdbTargetDescriptor())
); );
EventManager::triggerEvent(std::make_shared<Events::DebugSessionStarted>()); EventManager::triggerEvent(std::make_shared<Events::DebugSessionStarted>());
@@ -203,30 +196,17 @@ namespace Bloom::DebugServers::Gdb
throw Exception("Failed to listen on server socket"); throw Exception("Failed to listen on server socket");
} }
constexpr int maxEvents = 5; const auto eventFileDescriptor = this->epollInstance.waitForEvent();
std::array<struct epoll_event, maxEvents> events = {};
int eventCount = ::epoll_wait(
this->eventFileDescriptor,
events.data(),
maxEvents,
-1
);
if (eventCount > 0) { if (
for (size_t i = 0; i < eventCount; i++) { !eventFileDescriptor.has_value()
auto fileDescriptor = events.at(i).data.fd; || eventFileDescriptor.value() == this->interruptEventNotifier->getFileDescriptor()
) {
if (fileDescriptor == this->interruptEventNotifier->getFileDescriptor()) {
// Interrupted
this->interruptEventNotifier->clear(); this->interruptEventNotifier->clear();
return std::nullopt; return std::nullopt;
} }
}
return Connection(*(this->interruptEventNotifier)); return std::make_optional<Connection>(*(this->interruptEventNotifier));
}
return std::nullopt;
} }
std::unique_ptr<CommandPacket> GdbRspDebugServer::waitForCommandPacket() { std::unique_ptr<CommandPacket> GdbRspDebugServer::waitForCommandPacket() {

View File

@@ -11,6 +11,7 @@
#include "GdbDebugServerConfig.hpp" #include "GdbDebugServerConfig.hpp"
#include "src/EventManager/EventListener.hpp" #include "src/EventManager/EventListener.hpp"
#include "src/Helpers/EpollInstance.hpp"
#include "src/TargetController/TargetControllerConsole.hpp" #include "src/TargetController/TargetControllerConsole.hpp"
#include "Connection.hpp" #include "Connection.hpp"
@@ -88,15 +89,15 @@ namespace Bloom::DebugServers::Gdb
int serverSocketFileDescriptor = -1; int serverSocketFileDescriptor = -1;
/** /**
* We don't listen on the this->serverSocketFileDescriptor directly. Instead, we add it to an epoll set, along * We don't listen on the this->serverSocketFileDescriptor directly. Instead, we use an EpollInstance to
* with the this->interruptEventNotifier FD. This allows us to interrupt any blocking socket IO calls when * monitor both this->serverSocketFileDescriptor and this->interruptEventNotifier. This allows us to interrupt
* we have other things to do. * any blocking socket IO calls when EventNotifier::notify() is called on this->interruptEventNotifier.
* *
* See GdbRspDebugServer::init() * See GdbRspDebugServer::init()
* See DebugServer::interruptEventNotifier * See DebugServer::interruptEventNotifier
* See EventNotifier * See EventNotifier
*/ */
int eventFileDescriptor = -1; EpollInstance epollInstance = EpollInstance();
/** /**
* SO_REUSEADDR option value for listening socket. * SO_REUSEADDR option value for listening socket.