Using new RAII epoll instance wrapper in GDB server and Connection class
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user