Tidied structure of all classes within the entire code base
Also some other small bits of tidying
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
#include "Exceptions/ClientDisconnected.hpp"
|
||||
#include "Exceptions/ClientNotSupported.hpp"
|
||||
@@ -10,7 +11,6 @@
|
||||
#include "Exceptions/DebugSessionAborted.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
#include "src/Exceptions/InvalidConfig.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
using namespace Bloom::DebugServers::Gdb;
|
||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||
@@ -25,191 +25,6 @@ using Bloom::Targets::TargetRegisterDescriptor;
|
||||
using Bloom::Targets::TargetRegisterDescriptors;
|
||||
using Bloom::Targets::TargetBreakpoint;
|
||||
|
||||
void GdbRspDebugServer::init() {
|
||||
auto ipAddress = this->debugServerConfig.jsonObject.find("ipAddress")->toString().toStdString();
|
||||
auto configPortJsonValue = this->debugServerConfig.jsonObject.find("port");
|
||||
auto configPortValue = configPortJsonValue->isString()
|
||||
? static_cast<std::uint16_t>(configPortJsonValue->toString().toInt(nullptr, 10))
|
||||
: static_cast<std::uint16_t>(configPortJsonValue->toInt());
|
||||
|
||||
if (!ipAddress.empty()) {
|
||||
this->listeningAddress = ipAddress;
|
||||
}
|
||||
|
||||
if (configPortValue > 0) {
|
||||
this->listeningPortNumber = configPortValue;
|
||||
}
|
||||
|
||||
this->socketAddress.sin_family = AF_INET;
|
||||
this->socketAddress.sin_port = htons(this->listeningPortNumber);
|
||||
|
||||
if (::inet_pton(AF_INET, this->listeningAddress.c_str(), &(this->socketAddress.sin_addr)) == 0) {
|
||||
// Invalid IP address
|
||||
throw InvalidConfig("Invalid IP address provided in config file: (\"" + this->listeningAddress + "\")");
|
||||
}
|
||||
|
||||
int socketFileDescriptor;
|
||||
|
||||
if ((socketFileDescriptor = ::socket(AF_INET, SOCK_STREAM, 0)) == 0) {
|
||||
throw Exception("Failed to create socket file descriptor.");
|
||||
}
|
||||
|
||||
if (::setsockopt(
|
||||
socketFileDescriptor,
|
||||
SOL_SOCKET,
|
||||
SO_REUSEADDR,
|
||||
&(this->enableReuseAddressSocketOption),
|
||||
sizeof(this->enableReuseAddressSocketOption)
|
||||
) < 0
|
||||
) {
|
||||
Logger::error("Failed to set socket SO_REUSEADDR option.");
|
||||
}
|
||||
|
||||
if (::bind(
|
||||
socketFileDescriptor,
|
||||
reinterpret_cast<const sockaddr*>(&(this->socketAddress)),
|
||||
sizeof(this->socketAddress)
|
||||
) < 0
|
||||
) {
|
||||
throw Exception("Failed to bind address. The selected port number ("
|
||||
+ std::to_string(this->listeningPortNumber) + ") may be in use.");
|
||||
}
|
||||
|
||||
this->serverSocketFileDescriptor = socketFileDescriptor;
|
||||
|
||||
this->eventFileDescriptor = ::epoll_create(2);
|
||||
struct epoll_event event = {};
|
||||
event.events = EPOLLIN;
|
||||
event.data.fd = this->serverSocketFileDescriptor;
|
||||
|
||||
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, this->serverSocketFileDescriptor, &event) != 0) {
|
||||
throw Exception("Failed epoll_ctl server socket");
|
||||
}
|
||||
|
||||
if (this->interruptEventNotifier != nullptr) {
|
||||
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->listeningAddress);
|
||||
Logger::info("GDB RSP port: " + std::to_string(this->listeningPortNumber));
|
||||
|
||||
this->eventListener->registerCallbackForEventType<Events::TargetControllerStateReported>(
|
||||
std::bind(&GdbRspDebugServer::onTargetControllerStateReported, this, std::placeholders::_1)
|
||||
);
|
||||
|
||||
this->eventListener->registerCallbackForEventType<Events::TargetExecutionStopped>(
|
||||
std::bind(&GdbRspDebugServer::onTargetExecutionStopped, this, std::placeholders::_1)
|
||||
);
|
||||
}
|
||||
|
||||
void GdbRspDebugServer::close() {
|
||||
this->closeClientConnection();
|
||||
|
||||
if (this->serverSocketFileDescriptor > 0) {
|
||||
::close(this->serverSocketFileDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
void GdbRspDebugServer::serve() {
|
||||
try {
|
||||
if (!this->clientConnection.has_value()) {
|
||||
Logger::info("Waiting for GDB RSP connection");
|
||||
|
||||
do {
|
||||
this->waitForConnection();
|
||||
|
||||
} while (!this->clientConnection.has_value());
|
||||
|
||||
this->clientConnection->accept(this->serverSocketFileDescriptor);
|
||||
Logger::info("Accepted GDP RSP connection from " + this->clientConnection->getIpAddress());
|
||||
this->eventManager.triggerEvent(std::make_shared<Events::DebugSessionStarted>());
|
||||
|
||||
/*
|
||||
* Before proceeding with a new debug session, we must ensure that the TargetController is able to
|
||||
* service it.
|
||||
*/
|
||||
if (!this->targetControllerConsole.isTargetControllerInService()) {
|
||||
this->closeClientConnection();
|
||||
throw DebugSessionAborted("TargetController not in service");
|
||||
}
|
||||
}
|
||||
|
||||
auto packets = this->clientConnection->readPackets();
|
||||
|
||||
// Only process the last packet - any others will likely be duplicates from an impatient client
|
||||
if (!packets.empty()) {
|
||||
// Double-dispatch to appropriate handler
|
||||
packets.back()->dispatchToHandler(*this);
|
||||
}
|
||||
|
||||
} catch (const ClientDisconnected&) {
|
||||
Logger::info("GDB RSP client disconnected");
|
||||
this->closeClientConnection();
|
||||
return;
|
||||
|
||||
} catch (const ClientCommunicationError& exception) {
|
||||
Logger::error("GDB RSP client communication error - " + exception.getMessage() + " - closing connection");
|
||||
this->closeClientConnection();
|
||||
return;
|
||||
|
||||
} catch (const ClientNotSupported& exception) {
|
||||
Logger::error("Invalid GDB RSP client - " + exception.getMessage() + " - closing connection");
|
||||
this->closeClientConnection();
|
||||
return;
|
||||
|
||||
} catch (const DebugSessionAborted& exception) {
|
||||
Logger::warning("GDB debug session aborted - " + exception.getMessage());
|
||||
this->closeClientConnection();
|
||||
return;
|
||||
|
||||
} catch (const DebugServerInterrupted&) {
|
||||
// Server was interrupted
|
||||
Logger::debug("GDB RSP interrupted");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void GdbRspDebugServer::waitForConnection() {
|
||||
if (::listen(this->serverSocketFileDescriptor, 3) != 0) {
|
||||
throw Exception("Failed to listen on server socket");
|
||||
}
|
||||
|
||||
std::array<struct epoll_event, 5> events = {};
|
||||
int eventCount = ::epoll_wait(
|
||||
this->eventFileDescriptor,
|
||||
events.data(),
|
||||
5,
|
||||
-1
|
||||
);
|
||||
|
||||
if (eventCount > 0) {
|
||||
for (size_t i = 0; i < eventCount; i++) {
|
||||
auto fileDescriptor = events[i].data.fd;
|
||||
|
||||
if (fileDescriptor == this->interruptEventNotifier->getFileDescriptor()) {
|
||||
// Interrupted
|
||||
this->interruptEventNotifier->clear();
|
||||
throw DebugServerInterrupted();
|
||||
}
|
||||
}
|
||||
|
||||
this->clientConnection = Connection(this->interruptEventNotifier);
|
||||
}
|
||||
}
|
||||
|
||||
void GdbRspDebugServer::onTargetControllerStateReported(const Events::TargetControllerStateReported& event) {
|
||||
if (event.state == TargetControllerState::SUSPENDED && this->clientConnection.has_value()) {
|
||||
Logger::warning("Terminating debug session - TargetController suspended unexpectedly");
|
||||
this->closeClientConnection();
|
||||
}
|
||||
}
|
||||
|
||||
void GdbRspDebugServer::handleGdbPacket(CommandPacket& packet) {
|
||||
auto packetData = packet.getData();
|
||||
auto packetString = std::string(packetData.begin(), packetData.end());
|
||||
@@ -234,13 +49,6 @@ void GdbRspDebugServer::handleGdbPacket(CommandPacket& packet) {
|
||||
}
|
||||
}
|
||||
|
||||
void GdbRspDebugServer::onTargetExecutionStopped(const Events::TargetExecutionStopped&) {
|
||||
if (this->clientConnection.has_value() && this->clientConnection->waitingForBreak) {
|
||||
this->clientConnection->writePacket(TargetStopped(Signal::TRAP));
|
||||
this->clientConnection->waitingForBreak = false;
|
||||
}
|
||||
}
|
||||
|
||||
void GdbRspDebugServer::handleGdbPacket(CommandPackets::SupportedFeaturesQuery& packet) {
|
||||
Logger::debug("Handling QuerySupport packet");
|
||||
|
||||
@@ -455,3 +263,195 @@ void GdbRspDebugServer::handleGdbPacket(CommandPackets::InterruptExecution& pack
|
||||
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
|
||||
}
|
||||
}
|
||||
|
||||
void GdbRspDebugServer::init() {
|
||||
auto ipAddress = this->debugServerConfig.jsonObject.find("ipAddress")->toString().toStdString();
|
||||
auto configPortJsonValue = this->debugServerConfig.jsonObject.find("port");
|
||||
auto configPortValue = configPortJsonValue->isString()
|
||||
? static_cast<std::uint16_t>(configPortJsonValue->toString().toInt(nullptr, 10))
|
||||
: static_cast<std::uint16_t>(configPortJsonValue->toInt());
|
||||
|
||||
if (!ipAddress.empty()) {
|
||||
this->listeningAddress = ipAddress;
|
||||
}
|
||||
|
||||
if (configPortValue > 0) {
|
||||
this->listeningPortNumber = configPortValue;
|
||||
}
|
||||
|
||||
this->socketAddress.sin_family = AF_INET;
|
||||
this->socketAddress.sin_port = htons(this->listeningPortNumber);
|
||||
|
||||
if (::inet_pton(AF_INET, this->listeningAddress.c_str(), &(this->socketAddress.sin_addr)) == 0) {
|
||||
// Invalid IP address
|
||||
throw InvalidConfig("Invalid IP address provided in config file: (\"" + this->listeningAddress + "\")");
|
||||
}
|
||||
|
||||
int socketFileDescriptor;
|
||||
|
||||
if ((socketFileDescriptor = ::socket(AF_INET, SOCK_STREAM, 0)) == 0) {
|
||||
throw Exception("Failed to create socket file descriptor.");
|
||||
}
|
||||
|
||||
if (::setsockopt(
|
||||
socketFileDescriptor,
|
||||
SOL_SOCKET,
|
||||
SO_REUSEADDR,
|
||||
&(this->enableReuseAddressSocketOption),
|
||||
sizeof(this->enableReuseAddressSocketOption)
|
||||
) < 0
|
||||
) {
|
||||
Logger::error("Failed to set socket SO_REUSEADDR option.");
|
||||
}
|
||||
|
||||
if (::bind(
|
||||
socketFileDescriptor,
|
||||
reinterpret_cast<const sockaddr*>(&(this->socketAddress)),
|
||||
sizeof(this->socketAddress)
|
||||
) < 0
|
||||
) {
|
||||
throw Exception("Failed to bind address. The selected port number ("
|
||||
+ std::to_string(this->listeningPortNumber) + ") may be in use.");
|
||||
}
|
||||
|
||||
this->serverSocketFileDescriptor = socketFileDescriptor;
|
||||
|
||||
this->eventFileDescriptor = ::epoll_create(2);
|
||||
struct epoll_event event = {};
|
||||
event.events = EPOLLIN;
|
||||
event.data.fd = this->serverSocketFileDescriptor;
|
||||
|
||||
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, this->serverSocketFileDescriptor, &event) != 0) {
|
||||
throw Exception("Failed epoll_ctl server socket");
|
||||
}
|
||||
|
||||
if (this->interruptEventNotifier != nullptr) {
|
||||
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->listeningAddress);
|
||||
Logger::info("GDB RSP port: " + std::to_string(this->listeningPortNumber));
|
||||
|
||||
this->eventListener->registerCallbackForEventType<Events::TargetControllerStateReported>(
|
||||
std::bind(&GdbRspDebugServer::onTargetControllerStateReported, this, std::placeholders::_1)
|
||||
);
|
||||
|
||||
this->eventListener->registerCallbackForEventType<Events::TargetExecutionStopped>(
|
||||
std::bind(&GdbRspDebugServer::onTargetExecutionStopped, this, std::placeholders::_1)
|
||||
);
|
||||
}
|
||||
|
||||
void GdbRspDebugServer::close() {
|
||||
this->closeClientConnection();
|
||||
|
||||
if (this->serverSocketFileDescriptor > 0) {
|
||||
::close(this->serverSocketFileDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
void GdbRspDebugServer::serve() {
|
||||
try {
|
||||
if (!this->clientConnection.has_value()) {
|
||||
Logger::info("Waiting for GDB RSP connection");
|
||||
|
||||
do {
|
||||
this->waitForConnection();
|
||||
|
||||
} while (!this->clientConnection.has_value());
|
||||
|
||||
this->clientConnection->accept(this->serverSocketFileDescriptor);
|
||||
Logger::info("Accepted GDP RSP connection from " + this->clientConnection->getIpAddress());
|
||||
this->eventManager.triggerEvent(std::make_shared<Events::DebugSessionStarted>());
|
||||
|
||||
/*
|
||||
* Before proceeding with a new debug session, we must ensure that the TargetController is able to
|
||||
* service it.
|
||||
*/
|
||||
if (!this->targetControllerConsole.isTargetControllerInService()) {
|
||||
this->closeClientConnection();
|
||||
throw DebugSessionAborted("TargetController not in service");
|
||||
}
|
||||
}
|
||||
|
||||
auto packets = this->clientConnection->readPackets();
|
||||
|
||||
// Only process the last packet - any others will likely be duplicates from an impatient client
|
||||
if (!packets.empty()) {
|
||||
// Double-dispatch to appropriate handler
|
||||
packets.back()->dispatchToHandler(*this);
|
||||
}
|
||||
|
||||
} catch (const ClientDisconnected&) {
|
||||
Logger::info("GDB RSP client disconnected");
|
||||
this->closeClientConnection();
|
||||
return;
|
||||
|
||||
} catch (const ClientCommunicationError& exception) {
|
||||
Logger::error("GDB RSP client communication error - " + exception.getMessage() + " - closing connection");
|
||||
this->closeClientConnection();
|
||||
return;
|
||||
|
||||
} catch (const ClientNotSupported& exception) {
|
||||
Logger::error("Invalid GDB RSP client - " + exception.getMessage() + " - closing connection");
|
||||
this->closeClientConnection();
|
||||
return;
|
||||
|
||||
} catch (const DebugSessionAborted& exception) {
|
||||
Logger::warning("GDB debug session aborted - " + exception.getMessage());
|
||||
this->closeClientConnection();
|
||||
return;
|
||||
|
||||
} catch (const DebugServerInterrupted&) {
|
||||
// Server was interrupted
|
||||
Logger::debug("GDB RSP interrupted");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void GdbRspDebugServer::waitForConnection() {
|
||||
if (::listen(this->serverSocketFileDescriptor, 3) != 0) {
|
||||
throw Exception("Failed to listen on server socket");
|
||||
}
|
||||
|
||||
std::array<struct epoll_event, 5> events = {};
|
||||
int eventCount = ::epoll_wait(
|
||||
this->eventFileDescriptor,
|
||||
events.data(),
|
||||
5,
|
||||
-1
|
||||
);
|
||||
|
||||
if (eventCount > 0) {
|
||||
for (size_t i = 0; i < eventCount; i++) {
|
||||
auto fileDescriptor = events[i].data.fd;
|
||||
|
||||
if (fileDescriptor == this->interruptEventNotifier->getFileDescriptor()) {
|
||||
// Interrupted
|
||||
this->interruptEventNotifier->clear();
|
||||
throw DebugServerInterrupted();
|
||||
}
|
||||
}
|
||||
|
||||
this->clientConnection = Connection(this->interruptEventNotifier);
|
||||
}
|
||||
}
|
||||
|
||||
void GdbRspDebugServer::onTargetControllerStateReported(const Events::TargetControllerStateReported& event) {
|
||||
if (event.state == TargetControllerState::SUSPENDED && this->clientConnection.has_value()) {
|
||||
Logger::warning("Terminating debug session - TargetController suspended unexpectedly");
|
||||
this->closeClientConnection();
|
||||
}
|
||||
}
|
||||
|
||||
void GdbRspDebugServer::onTargetExecutionStopped(const Events::TargetExecutionStopped&) {
|
||||
if (this->clientConnection.has_value() && this->clientConnection->waitingForBreak) {
|
||||
this->clientConnection->writePacket(TargetStopped(Signal::TRAP));
|
||||
this->clientConnection->waitingForBreak = false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user