Tidied structure of all classes within the entire code base
Also some other small bits of tidying
This commit is contained in:
@@ -25,14 +25,12 @@ namespace Bloom::DebugServers::Gdb
|
||||
*/
|
||||
class AvrGdbRsp: public GdbRspDebugServer
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* The mask used by the AVR GDB client to encode the memory type into memory addresses.
|
||||
* See AvrGdbRsp::getMemoryTypeFromGdbAddress() for more.
|
||||
*/
|
||||
unsigned int gdbInternalMemoryMask = 0xFE0000u;
|
||||
public:
|
||||
explicit AvrGdbRsp(EventManager& eventManager): GdbRspDebugServer(eventManager) {};
|
||||
|
||||
BiMap<GdbRegisterNumber, Targets::TargetRegisterDescriptor> registerNumberToDescriptorMapping = {};
|
||||
std::string getName() const override {
|
||||
return "AVR GDB Remote Serial Protocol Debug Server";
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
@@ -80,11 +78,13 @@ namespace Bloom::DebugServers::Gdb
|
||||
|
||||
void init() override;
|
||||
|
||||
public:
|
||||
explicit AvrGdbRsp(EventManager& eventManager): GdbRspDebugServer(eventManager) {};
|
||||
private:
|
||||
/**
|
||||
* The mask used by the AVR GDB client to encode the memory type into memory addresses.
|
||||
* See AvrGdbRsp::getMemoryTypeFromGdbAddress() for more.
|
||||
*/
|
||||
unsigned int gdbInternalMemoryMask = 0xFE0000u;
|
||||
|
||||
std::string getName() const override {
|
||||
return "AVR GDB Remote Serial Protocol Debug Server";
|
||||
}
|
||||
BiMap<GdbRegisterNumber, Targets::TargetRegisterDescriptor> registerNumberToDescriptorMapping = {};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,51 +7,6 @@
|
||||
using namespace Bloom::DebugServers::Gdb;
|
||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||
|
||||
std::unique_ptr<CommandPacket> CommandPacketFactory::create(std::vector<unsigned char> rawPacket) {
|
||||
if (rawPacket.size() == 5 && rawPacket[1] == 0x03) {
|
||||
// This is an interrupt request - create a fake packet for it
|
||||
return std::make_unique<CommandPackets::InterruptExecution>(rawPacket);
|
||||
}
|
||||
|
||||
auto rawPacketString = std::string(rawPacket.begin(), rawPacket.end());
|
||||
|
||||
if (rawPacketString.size() >= 2) {
|
||||
/*
|
||||
* First byte of the raw packet will be 0x24 ('$'), so find() should return 1, not 0, when
|
||||
* looking for a command identifier string.
|
||||
*/
|
||||
if (rawPacketString.find("qSupported") == 1) {
|
||||
return std::make_unique<CommandPackets::SupportedFeaturesQuery>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'g' || rawPacketString[1] == 'p') {
|
||||
return std::make_unique<CommandPackets::ReadRegisters>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'P') {
|
||||
return std::make_unique<CommandPackets::WriteRegister>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'c') {
|
||||
return std::make_unique<CommandPackets::ContinueExecution>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 's') {
|
||||
return std::make_unique<CommandPackets::StepExecution>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'm') {
|
||||
return std::make_unique<CommandPackets::ReadMemory>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'M') {
|
||||
return std::make_unique<CommandPackets::WriteMemory>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'Z') {
|
||||
return std::make_unique<CommandPackets::SetBreakpoint>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'z') {
|
||||
return std::make_unique<CommandPackets::RemoveBreakpoint>(rawPacket);
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_unique<CommandPacket>(rawPacket);
|
||||
}
|
||||
|
||||
std::vector<std::vector<unsigned char>> CommandPacketFactory::extractRawPackets(std::vector<unsigned char> buffer) {
|
||||
std::vector<std::vector<unsigned char>> output;
|
||||
|
||||
@@ -127,3 +82,48 @@ std::vector<std::vector<unsigned char>> CommandPacketFactory::extractRawPackets(
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::unique_ptr<CommandPacket> CommandPacketFactory::create(std::vector<unsigned char> rawPacket) {
|
||||
if (rawPacket.size() == 5 && rawPacket[1] == 0x03) {
|
||||
// This is an interrupt request - create a fake packet for it
|
||||
return std::make_unique<CommandPackets::InterruptExecution>(rawPacket);
|
||||
}
|
||||
|
||||
auto rawPacketString = std::string(rawPacket.begin(), rawPacket.end());
|
||||
|
||||
if (rawPacketString.size() >= 2) {
|
||||
/*
|
||||
* First byte of the raw packet will be 0x24 ('$'), so find() should return 1, not 0, when
|
||||
* looking for a command identifier string.
|
||||
*/
|
||||
if (rawPacketString.find("qSupported") == 1) {
|
||||
return std::make_unique<CommandPackets::SupportedFeaturesQuery>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'g' || rawPacketString[1] == 'p') {
|
||||
return std::make_unique<CommandPackets::ReadRegisters>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'P') {
|
||||
return std::make_unique<CommandPackets::WriteRegister>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'c') {
|
||||
return std::make_unique<CommandPackets::ContinueExecution>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 's') {
|
||||
return std::make_unique<CommandPackets::StepExecution>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'm') {
|
||||
return std::make_unique<CommandPackets::ReadMemory>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'M') {
|
||||
return std::make_unique<CommandPackets::WriteMemory>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'Z') {
|
||||
return std::make_unique<CommandPackets::SetBreakpoint>(rawPacket);
|
||||
|
||||
} else if (rawPacketString[1] == 'z') {
|
||||
return std::make_unique<CommandPackets::RemoveBreakpoint>(rawPacket);
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_unique<CommandPacket>(rawPacket);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
|
||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||
|
||||
void ContinueExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
void ContinueExecution::init() {
|
||||
if (this->data.size() > 1) {
|
||||
this->fromProgramCounter = static_cast<std::uint32_t>(
|
||||
@@ -13,7 +17,3 @@ void ContinueExecution::init() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void ContinueExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
@@ -15,9 +15,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
*/
|
||||
class ContinueExecution: public CommandPacket
|
||||
{
|
||||
private:
|
||||
void init();
|
||||
|
||||
public:
|
||||
/**
|
||||
* The "c" packet can contain an address which defines the point from which the execution should be resumed on
|
||||
@@ -32,5 +29,8 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
}
|
||||
|
||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||
|
||||
private:
|
||||
void init();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||
using namespace Bloom::Exceptions;
|
||||
|
||||
void ReadMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
void ReadMemory::init() {
|
||||
if (this->data.size() < 4) {
|
||||
throw Exception("Invalid packet length");
|
||||
@@ -38,7 +42,3 @@ void ReadMemory::init() {
|
||||
throw Exception("Failed to parse read length from read memory packet data");
|
||||
}
|
||||
}
|
||||
|
||||
void ReadMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
@@ -13,9 +13,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
*/
|
||||
class ReadMemory: public CommandPacket
|
||||
{
|
||||
private:
|
||||
void init();
|
||||
|
||||
public:
|
||||
/**
|
||||
* The startAddress sent from the GDB client may include additional bits used to indicate the memory type.
|
||||
@@ -36,5 +33,8 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
};
|
||||
|
||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||
|
||||
private:
|
||||
void init();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||
|
||||
void ReadRegisters::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
void ReadRegisters::init() {
|
||||
if (this->data.size() >= 2 && this->data.front() == 'p') {
|
||||
// This command packet is requesting a specific register
|
||||
this->registerNumber = static_cast<size_t>(std::stoi(std::string(this->data.begin() + 1, this->data.end())));
|
||||
}
|
||||
}
|
||||
|
||||
void ReadRegisters::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
@@ -13,9 +13,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
*/
|
||||
class ReadRegisters: public CommandPacket
|
||||
{
|
||||
private:
|
||||
void init();
|
||||
|
||||
public:
|
||||
/**
|
||||
* "p" packets include a register number to indicate which register is requested for reading. When this is set,
|
||||
@@ -31,5 +28,8 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
};
|
||||
|
||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||
|
||||
private:
|
||||
void init();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||
using namespace Bloom::Exceptions;
|
||||
|
||||
void RemoveBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
void RemoveBreakpoint::init() {
|
||||
if (data.size() < 6) {
|
||||
throw Exception("Unexpected RemoveBreakpoint packet size");
|
||||
@@ -33,7 +37,3 @@ void RemoveBreakpoint::init() {
|
||||
throw Exception("Failed to convert address hex value from RemoveBreakpoint packet.");
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
@@ -20,9 +20,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
*/
|
||||
class RemoveBreakpoint: public CommandPacket
|
||||
{
|
||||
private:
|
||||
void init();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Breakpoint type (Software or Hardware)
|
||||
@@ -39,5 +36,8 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
};
|
||||
|
||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||
|
||||
private:
|
||||
void init();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||
using namespace Bloom::Exceptions;
|
||||
|
||||
void SetBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
void SetBreakpoint::init() {
|
||||
if (data.size() < 6) {
|
||||
throw Exception("Unexpected SetBreakpoint packet size");
|
||||
@@ -34,7 +38,3 @@ void SetBreakpoint::init() {
|
||||
throw Exception("Failed to convert address hex value from SetBreakpoint packet.");
|
||||
}
|
||||
}
|
||||
|
||||
void SetBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
@@ -20,9 +20,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
*/
|
||||
class SetBreakpoint: public CommandPacket
|
||||
{
|
||||
private:
|
||||
void init();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Breakpoint type (Software or Hardware)
|
||||
@@ -39,5 +36,8 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
};
|
||||
|
||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||
|
||||
private:
|
||||
void init();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||
|
||||
void StepExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
void StepExecution::init() {
|
||||
if (this->data.size() > 1) {
|
||||
this->fromProgramCounter = static_cast<std::uint32_t>(
|
||||
@@ -12,7 +16,3 @@ void StepExecution::init() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void StepExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
@@ -12,9 +12,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
*/
|
||||
class StepExecution: public CommandPacket
|
||||
{
|
||||
private:
|
||||
void init();
|
||||
|
||||
public:
|
||||
/**
|
||||
* The address from which to begin the step.
|
||||
@@ -26,5 +23,8 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
};
|
||||
|
||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||
|
||||
private:
|
||||
void init();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
|
||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||
|
||||
void SupportedFeaturesQuery::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
void SupportedFeaturesQuery::init() {
|
||||
/*
|
||||
* For qSupported packets, supported and unsupported GDB features are reported in the packet
|
||||
@@ -37,7 +41,3 @@ void SupportedFeaturesQuery::init() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SupportedFeaturesQuery::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
@@ -21,11 +21,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
*/
|
||||
class SupportedFeaturesQuery: public CommandPacket
|
||||
{
|
||||
private:
|
||||
std::set<Feature> supportedFeatures;
|
||||
|
||||
void init();
|
||||
|
||||
public:
|
||||
explicit SupportedFeaturesQuery(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {
|
||||
this->init();
|
||||
@@ -40,5 +35,10 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
}
|
||||
|
||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||
|
||||
private:
|
||||
std::set<Feature> supportedFeatures;
|
||||
|
||||
void init();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||
using namespace Bloom::Exceptions;
|
||||
|
||||
void WriteMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
void WriteMemory::init() {
|
||||
if (this->data.size() < 4) {
|
||||
throw Exception("Invalid packet length");
|
||||
@@ -47,7 +51,3 @@ void WriteMemory::init() {
|
||||
throw Exception("Buffer size does not match length value given in write memory packet");
|
||||
}
|
||||
}
|
||||
|
||||
void WriteMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
@@ -14,9 +14,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
*/
|
||||
class WriteMemory: public CommandPacket
|
||||
{
|
||||
private:
|
||||
void init();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Like with the ReadMemory command packet, the start address carries additional bits that indicate
|
||||
@@ -31,5 +28,8 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
};
|
||||
|
||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||
|
||||
private:
|
||||
void init();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
using namespace Bloom::DebugServers::Gdb::CommandPackets;
|
||||
using namespace Bloom::Exceptions;
|
||||
|
||||
void WriteRegister::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
void WriteRegister::init() {
|
||||
// The P packet updates a single register
|
||||
auto packet = std::string(this->data.begin(), this->data.end());
|
||||
@@ -23,7 +27,3 @@ void WriteRegister::init() {
|
||||
this->registerValue = Packet::hexToData(packetSegments.back().toStdString());
|
||||
std::reverse(this->registerValue.begin(), this->registerValue.end());
|
||||
}
|
||||
|
||||
void WriteRegister::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
|
||||
gdbRspDebugServer.handleGdbPacket(*this);
|
||||
}
|
||||
|
||||
@@ -13,9 +13,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
*/
|
||||
class WriteRegister: public CommandPacket
|
||||
{
|
||||
private:
|
||||
void init();
|
||||
|
||||
public:
|
||||
int registerNumber = 0;
|
||||
std::vector<unsigned char> registerValue;
|
||||
@@ -25,5 +22,8 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
|
||||
};
|
||||
|
||||
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override;
|
||||
|
||||
private:
|
||||
void init();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "CommandPackets/CommandPacketFactory.hpp"
|
||||
|
||||
#include "Exceptions/ClientDisconnected.hpp"
|
||||
#include "Exceptions/ClientCommunicationError.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
#include "src/Exceptions/DebugServerInterrupted.hpp"
|
||||
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
using namespace Bloom::DebugServers::Gdb;
|
||||
@@ -51,32 +53,6 @@ void Connection::accept(int serverSocketFileDescriptor) {
|
||||
this->enableReadInterrupts();
|
||||
}
|
||||
|
||||
void Connection::disableReadInterrupts() {
|
||||
if (::epoll_ctl(
|
||||
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;
|
||||
}
|
||||
|
||||
void Connection::enableReadInterrupts() {
|
||||
auto interruptFileDescriptor = this->interruptEventNotifier->getFileDescriptor();
|
||||
struct epoll_event event = {};
|
||||
event.events = EPOLLIN;
|
||||
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;
|
||||
}
|
||||
|
||||
void Connection::close() noexcept {
|
||||
if (this->socketFileDescriptor > 0) {
|
||||
::close(this->socketFileDescriptor);
|
||||
@@ -84,18 +60,28 @@ void Connection::close() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::write(const std::vector<unsigned char>& buffer) {
|
||||
Logger::debug("Writing packet: " + std::string(buffer.begin(), buffer.end()));
|
||||
if (::write(this->socketFileDescriptor, buffer.data(), buffer.size()) == -1) {
|
||||
if (errno == EPIPE || errno == ECONNRESET) {
|
||||
// Connection was closed
|
||||
throw ClientDisconnected();
|
||||
std::vector<std::unique_ptr<CommandPacket>> Connection::readPackets() {
|
||||
auto buffer = this->read();
|
||||
Logger::debug("GDB client data received (" + std::to_string(buffer.size()) + " bytes): " + std::string(buffer.begin(), buffer.end()));
|
||||
|
||||
} else {
|
||||
throw ClientCommunicationError("Failed to write " + std::to_string(buffer.size())
|
||||
+ " bytes to GDP client socket - error no: " + std::to_string(errno));
|
||||
auto rawPackets = CommandPacketFactory::extractRawPackets(buffer);
|
||||
std::vector<std::unique_ptr<CommandPacket>> output;
|
||||
|
||||
for (const auto& rawPacket : rawPackets) {
|
||||
try {
|
||||
output.push_back(CommandPacketFactory::create(rawPacket));
|
||||
this->write({'+'});
|
||||
|
||||
} catch (const ClientDisconnected& exception) {
|
||||
throw exception;
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to parse GDB packet - " + exception.getMessage());
|
||||
this->write({'-'});
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void Connection::writePacket(const ResponsePacket& packet) {
|
||||
@@ -185,26 +171,42 @@ std::optional<unsigned char> Connection::readSingleByte(bool interruptible) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<CommandPacket>> Connection::readPackets() {
|
||||
auto buffer = this->read();
|
||||
Logger::debug("GDB client data received (" + std::to_string(buffer.size()) + " bytes): " + std::string(buffer.begin(), buffer.end()));
|
||||
void Connection::write(const std::vector<unsigned char>& buffer) {
|
||||
Logger::debug("Writing packet: " + std::string(buffer.begin(), buffer.end()));
|
||||
if (::write(this->socketFileDescriptor, buffer.data(), buffer.size()) == -1) {
|
||||
if (errno == EPIPE || errno == ECONNRESET) {
|
||||
// Connection was closed
|
||||
throw ClientDisconnected();
|
||||
|
||||
auto rawPackets = CommandPacketFactory::extractRawPackets(buffer);
|
||||
std::vector<std::unique_ptr<CommandPacket>> output;
|
||||
|
||||
for (const auto& rawPacket : rawPackets) {
|
||||
try {
|
||||
output.push_back(CommandPacketFactory::create(rawPacket));
|
||||
this->write({'+'});
|
||||
|
||||
} catch (const ClientDisconnected& exception) {
|
||||
throw exception;
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to parse GDB packet - " + exception.getMessage());
|
||||
this->write({'-'});
|
||||
} else {
|
||||
throw ClientCommunicationError("Failed to write " + std::to_string(buffer.size())
|
||||
+ " bytes to GDP client socket - error no: " + std::to_string(errno));
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void Connection::disableReadInterrupts() {
|
||||
if (::epoll_ctl(
|
||||
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;
|
||||
}
|
||||
|
||||
void Connection::enableReadInterrupts() {
|
||||
auto interruptFileDescriptor = this->interruptEventNotifier->getFileDescriptor();
|
||||
struct epoll_event event = {};
|
||||
event.events = EPOLLIN;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -22,59 +22,6 @@ namespace Bloom::DebugServers::Gdb
|
||||
*/
|
||||
class Connection
|
||||
{
|
||||
private:
|
||||
int socketFileDescriptor = -1;
|
||||
int eventFileDescriptor = -1;
|
||||
|
||||
struct sockaddr_in socketAddress = {};
|
||||
int maxPacketSize = 1024;
|
||||
|
||||
/**
|
||||
* The interruptEventNotifier allows us to interrupt blocking IO calls on the GDB debug server.
|
||||
* Under the hood, this is just a wrapper for a Linux event notifier. See the EventNotifier class for more.
|
||||
*/
|
||||
std::shared_ptr<EventNotifier> interruptEventNotifier = nullptr;
|
||||
bool readInterruptEnabled = false;
|
||||
|
||||
/**
|
||||
* Reads data from the client into a raw buffer.
|
||||
*
|
||||
* @param bytes
|
||||
* Number of bytes to read.
|
||||
*
|
||||
* @param interruptible
|
||||
* If this flag is set to false, no other component within Bloom will be able to gracefully interrupt
|
||||
* the read (via means of this->interruptEventNotifier). This flag has no effect if this->readInterruptEnabled
|
||||
* is false.
|
||||
*
|
||||
* @param msTimeout
|
||||
* The timeout in milliseconds. If not supplied, no timeout will be applied.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
std::vector<unsigned char> read(std::size_t bytes = 0, bool interruptible = true, std::optional<int> msTimeout = std::nullopt);
|
||||
|
||||
/**
|
||||
* Does the same as Connection::read(), but only reads a single byte.
|
||||
*
|
||||
* @param interruptible
|
||||
* See Connection::read().
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
std::optional<unsigned char> readSingleByte(bool interruptible = true);
|
||||
|
||||
/**
|
||||
* Writes data from a raw buffer to the client connection.
|
||||
*
|
||||
* @param buffer
|
||||
*/
|
||||
void write(const std::vector<unsigned char>& buffer);
|
||||
|
||||
void disableReadInterrupts();
|
||||
|
||||
void enableReadInterrupts();
|
||||
|
||||
public:
|
||||
/**
|
||||
* When the GDB client is waiting for the target to reach a breakpoint, this is set to true so we know when to
|
||||
@@ -131,5 +78,58 @@ namespace Bloom::DebugServers::Gdb
|
||||
[[nodiscard]] int getMaxPacketSize() const {
|
||||
return this->maxPacketSize;
|
||||
}
|
||||
|
||||
private:
|
||||
int socketFileDescriptor = -1;
|
||||
int eventFileDescriptor = -1;
|
||||
|
||||
struct sockaddr_in socketAddress = {};
|
||||
int maxPacketSize = 1024;
|
||||
|
||||
/**
|
||||
* The interruptEventNotifier allows us to interrupt blocking IO calls on the GDB debug server.
|
||||
* Under the hood, this is just a wrapper for a Linux event notifier. See the EventNotifier class for more.
|
||||
*/
|
||||
std::shared_ptr<EventNotifier> interruptEventNotifier = nullptr;
|
||||
bool readInterruptEnabled = false;
|
||||
|
||||
/**
|
||||
* Reads data from the client into a raw buffer.
|
||||
*
|
||||
* @param bytes
|
||||
* Number of bytes to read.
|
||||
*
|
||||
* @param interruptible
|
||||
* If this flag is set to false, no other component within Bloom will be able to gracefully interrupt
|
||||
* the read (via means of this->interruptEventNotifier). This flag has no effect if this->readInterruptEnabled
|
||||
* is false.
|
||||
*
|
||||
* @param msTimeout
|
||||
* The timeout in milliseconds. If not supplied, no timeout will be applied.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
std::vector<unsigned char> read(std::size_t bytes = 0, bool interruptible = true, std::optional<int> msTimeout = std::nullopt);
|
||||
|
||||
/**
|
||||
* Does the same as Connection::read(), but only reads a single byte.
|
||||
*
|
||||
* @param interruptible
|
||||
* See Connection::read().
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
std::optional<unsigned char> readSingleByte(bool interruptible = true);
|
||||
|
||||
/**
|
||||
* Writes data from a raw buffer to the client connection.
|
||||
*
|
||||
* @param buffer
|
||||
*/
|
||||
void write(const std::vector<unsigned char>& buffer);
|
||||
|
||||
void disableReadInterrupts();
|
||||
|
||||
void enableReadInterrupts();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,92 @@ namespace Bloom::DebugServers::Gdb
|
||||
*/
|
||||
class GdbRspDebugServer: public DebugServer
|
||||
{
|
||||
public:
|
||||
explicit GdbRspDebugServer(EventManager& eventManager): DebugServer(eventManager) {};
|
||||
|
||||
std::string getName() const override {
|
||||
return "GDB Remote Serial Protocol DebugServer";
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles any other GDB command packet that has not been promoted to a more specific type.
|
||||
* This would be packets like "?" and "qAttached".
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::CommandPacket& packet);
|
||||
|
||||
/**
|
||||
* Handles the supported features query ("qSupported") command packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::SupportedFeaturesQuery& packet);
|
||||
|
||||
/**
|
||||
* Handles the read registers ("g" and "p") command packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::ReadRegisters& packet);
|
||||
|
||||
/**
|
||||
* Handles the write general register ("P") command packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::WriteRegister& packet);
|
||||
|
||||
/**
|
||||
* Handles the continue execution ("c") command packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::ContinueExecution& packet);
|
||||
|
||||
/**
|
||||
* Handles the step execution ("s") packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::StepExecution& packet);
|
||||
|
||||
/**
|
||||
* Handles the read memory ("m") command packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::ReadMemory& packet);
|
||||
|
||||
/**
|
||||
* Handles the write memory ("M") command packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::WriteMemory& packet);
|
||||
|
||||
/**
|
||||
* Handles the set breakpoint ("Z") command packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::SetBreakpoint& packet);
|
||||
|
||||
/**
|
||||
* Handles the remove breakpoint ("z") command packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::RemoveBreakpoint& packet);
|
||||
|
||||
/**
|
||||
* Handles the interrupt command packet.
|
||||
* Will attempt to halt execution on the target. Should respond with a "stop reply" packet, or an error code.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::InterruptExecution& packet);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The port number for the GDB server to listen on.
|
||||
@@ -156,19 +242,12 @@ namespace Bloom::DebugServers::Gdb
|
||||
|
||||
if (!mapping.contains(number)) {
|
||||
throw Exceptions::Exception("Unknown register from GDB - register number ("
|
||||
+ std::to_string(number) + ") not mapped to any register descriptor.");
|
||||
+ std::to_string(number) + ") not mapped to any register descriptor.");
|
||||
}
|
||||
|
||||
return mapping.valueAt(number).value();
|
||||
}
|
||||
|
||||
public:
|
||||
explicit GdbRspDebugServer(EventManager& eventManager): DebugServer(eventManager) {};
|
||||
|
||||
std::string getName() const override {
|
||||
return "GDB Remote Serial Protocol DebugServer";
|
||||
};
|
||||
|
||||
void onTargetControllerStateReported(const Events::TargetControllerStateReported& event);
|
||||
|
||||
/**
|
||||
@@ -176,84 +255,5 @@ namespace Bloom::DebugServers::Gdb
|
||||
* a "stop reply" packet to the client once the target execution stops.
|
||||
*/
|
||||
void onTargetExecutionStopped(const Events::TargetExecutionStopped&);
|
||||
|
||||
/**
|
||||
* Handles any other GDB command packet that has not been promoted to a more specific type.
|
||||
* This would be packets like "?" and "qAttached".
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::CommandPacket& packet);
|
||||
|
||||
/**
|
||||
* Handles the supported features query ("qSupported") command packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::SupportedFeaturesQuery& packet);
|
||||
|
||||
/**
|
||||
* Handles the read registers ("g" and "p") command packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::ReadRegisters& packet);
|
||||
|
||||
/**
|
||||
* Handles the write general register ("P") command packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::WriteRegister& packet);
|
||||
|
||||
/**
|
||||
* Handles the continue execution ("c") command packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::ContinueExecution& packet);
|
||||
|
||||
/**
|
||||
* Handles the step execution ("s") packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::StepExecution& packet);
|
||||
|
||||
/**
|
||||
* Handles the read memory ("m") command packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::ReadMemory& packet);
|
||||
|
||||
/**
|
||||
* Handles the write memory ("M") command packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::WriteMemory& packet);
|
||||
|
||||
/**
|
||||
* Handles the set breakpoint ("Z") command packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::SetBreakpoint& packet);
|
||||
|
||||
/**
|
||||
* Handles the remove breakpoint ("z") command packet.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::RemoveBreakpoint& packet);
|
||||
|
||||
/**
|
||||
* Handles the interrupt command packet.
|
||||
* Will attempt to halt execution on the target. Should respond with a "stop reply" packet, or an error code.
|
||||
*
|
||||
* @param packet
|
||||
*/
|
||||
virtual void handleGdbPacket(CommandPackets::InterruptExecution& packet);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,21 +16,12 @@ namespace Bloom::DebugServers::Gdb
|
||||
*/
|
||||
class Packet
|
||||
{
|
||||
protected:
|
||||
std::vector<unsigned char> data;
|
||||
|
||||
void init(const std::vector<unsigned char>& rawPacket) {
|
||||
this->data.insert(
|
||||
this->data.begin(),
|
||||
rawPacket.begin() + 1,
|
||||
rawPacket.end() - 3
|
||||
);
|
||||
}
|
||||
public:
|
||||
Packet() = default;
|
||||
explicit Packet(const std::vector<unsigned char>& rawPacket) {
|
||||
this->init(rawPacket);
|
||||
}
|
||||
virtual ~Packet() = default;
|
||||
|
||||
[[nodiscard]] virtual std::vector<unsigned char> getData() const {
|
||||
return this->data;
|
||||
@@ -118,6 +109,15 @@ namespace Bloom::DebugServers::Gdb
|
||||
return output;
|
||||
}
|
||||
|
||||
virtual ~Packet() = default;
|
||||
protected:
|
||||
std::vector<unsigned char> data;
|
||||
|
||||
void init(const std::vector<unsigned char>& rawPacket) {
|
||||
this->data.insert(
|
||||
this->data.begin(),
|
||||
rawPacket.begin() + 1,
|
||||
rawPacket.end() - 3
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "ResponsePacket.hpp"
|
||||
|
||||
namespace Bloom::DebugServers::Gdb {
|
||||
|
||||
@@ -13,14 +13,14 @@ namespace Bloom::DebugServers::Gdb::ResponsePackets
|
||||
*/
|
||||
class SupportedFeaturesResponse: public ResponsePacket
|
||||
{
|
||||
protected:
|
||||
std::set<std::pair<Feature, std::optional<std::string>>> supportedFeatures;
|
||||
|
||||
public:
|
||||
SupportedFeaturesResponse() = default;
|
||||
explicit SupportedFeaturesResponse(std::set<std::pair<Feature, std::optional<std::string>>> supportedFeatures)
|
||||
: supportedFeatures(std::move(supportedFeatures)) {};
|
||||
|
||||
[[nodiscard]] std::vector<unsigned char> getData() const override;
|
||||
|
||||
protected:
|
||||
std::set<std::pair<Feature, std::optional<std::string>>> supportedFeatures;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user