Moved GDB command packet handling to individual CommandPacket classes

This commit is contained in:
Nav
2022-03-24 19:17:41 +00:00
parent df5a904a43
commit 2b3a6fd27f
31 changed files with 582 additions and 583 deletions

View File

@@ -129,16 +129,14 @@ add_executable(Bloom
src/DebugServers/GdbRsp/GdbDebugServerConfig.cpp src/DebugServers/GdbRsp/GdbDebugServerConfig.cpp
src/DebugServers/GdbRsp/Connection.cpp src/DebugServers/GdbRsp/Connection.cpp
src/DebugServers/GdbRsp/DebugSession.cpp src/DebugServers/GdbRsp/DebugSession.cpp
src/DebugServers/GdbRsp/CommandPackets/CommandPacket.cpp
src/DebugServers/GdbRsp/CommandPackets/CommandPacketFactory.cpp src/DebugServers/GdbRsp/CommandPackets/CommandPacketFactory.cpp
src/DebugServers/GdbRsp/CommandPackets/CommandPacket.cpp
src/DebugServers/GdbRsp/CommandPackets/SupportedFeaturesQuery.cpp src/DebugServers/GdbRsp/CommandPackets/SupportedFeaturesQuery.cpp
src/DebugServers/GdbRsp/CommandPackets/ReadRegisters.cpp src/DebugServers/GdbRsp/CommandPackets/ReadRegisters.cpp
src/DebugServers/GdbRsp/CommandPackets/WriteRegister.cpp src/DebugServers/GdbRsp/CommandPackets/WriteRegister.cpp
src/DebugServers/GdbRsp/CommandPackets/ContinueExecution.cpp src/DebugServers/GdbRsp/CommandPackets/ContinueExecution.cpp
src/DebugServers/GdbRsp/CommandPackets/StepExecution.cpp src/DebugServers/GdbRsp/CommandPackets/StepExecution.cpp
src/DebugServers/GdbRsp/CommandPackets/InterruptExecution.cpp src/DebugServers/GdbRsp/CommandPackets/InterruptExecution.cpp
src/DebugServers/GdbRsp/CommandPackets/ReadMemory.cpp
src/DebugServers/GdbRsp/CommandPackets/WriteMemory.cpp
src/DebugServers/GdbRsp/CommandPackets/SetBreakpoint.cpp src/DebugServers/GdbRsp/CommandPackets/SetBreakpoint.cpp
src/DebugServers/GdbRsp/CommandPackets/RemoveBreakpoint.cpp src/DebugServers/GdbRsp/CommandPackets/RemoveBreakpoint.cpp
src/DebugServers/GdbRsp/ResponsePackets/SupportedFeaturesResponse.cpp src/DebugServers/GdbRsp/ResponsePackets/SupportedFeaturesResponse.cpp
@@ -146,6 +144,10 @@ add_executable(Bloom
# AVR GDB Server # AVR GDB Server
src/DebugServers/GdbRsp/AvrGdb/AvrGdbRsp.cpp src/DebugServers/GdbRsp/AvrGdb/AvrGdbRsp.cpp
src/DebugServers/GdbRsp/AvrGdb/TargetDescriptor.cpp src/DebugServers/GdbRsp/AvrGdb/TargetDescriptor.cpp
src/DebugServers/GdbRsp/AvrGdb/CommandPackets/AbstractMemoryAccessPacket.cpp
src/DebugServers/GdbRsp/AvrGdb/CommandPackets/ReadMemory.cpp
src/DebugServers/GdbRsp/AvrGdb/CommandPackets/WriteMemory.cpp
# Insight # Insight
src/Insight/Insight.cpp src/Insight/Insight.cpp
src/Insight/InsightWorker/InsightWorker.cpp src/Insight/InsightWorker/InsightWorker.cpp

View File

@@ -0,0 +1,17 @@
#include "AbstractMemoryAccessPacket.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/TargetStopped.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServers/GdbRsp/Signal.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets
{
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
}

View File

@@ -0,0 +1,54 @@
#pragma once
#include <cstdint>
#include <optional>
#include "src/DebugServers/GdbRsp/CommandPackets/CommandPacket.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace Bloom::DebugServers::Gdb::AvrGdb::CommandPackets
{
/**
* The ReadMemory class implements a structure for "m" packets. Upon receiving these packets, the server is
* expected to read memory from the target and send it the client.
*/
class AbstractMemoryAccessPacket: public Bloom::DebugServers::Gdb::CommandPackets::CommandPacket
{
public:
explicit AbstractMemoryAccessPacket(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {};
protected:
/**
* The mask used by the AVR GDB client to encode the memory type into memory addresses.
*/
static constexpr std::uint32_t AVR_GDB_MEMORY_ADDRESS_MASK = 0xFE0000U;
/**
* avr-gdb uses the most significant 15 bits in memory addresses to indicate the type of memory being
* addressed.
*
* @param address
* @return
*/
Targets::TargetMemoryType getMemoryTypeFromGdbAddress(std::uint32_t address) {
if ((address & AbstractMemoryAccessPacket::AVR_GDB_MEMORY_ADDRESS_MASK) != 0U) {
return Targets::TargetMemoryType::RAM;
}
return Targets::TargetMemoryType::FLASH;
}
/**
* Strips the most significant 15 bits from a GDB memory address.
*
* @param address
* @return
*/
std::uint32_t removeMemoryTypeIndicatorFromGdbAddress(std::uint32_t address) {
return (address & AbstractMemoryAccessPacket::AVR_GDB_MEMORY_ADDRESS_MASK) != 0U
? (address & ~(AbstractMemoryAccessPacket::AVR_GDB_MEMORY_ADDRESS_MASK))
: address;
}
};
}

View File

@@ -0,0 +1,70 @@
#include "ReadMemory.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/ResponsePacket.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServers::Gdb::AvrGdb::CommandPackets
{
using ResponsePackets::ErrorResponsePacket;
using ResponsePackets::ResponsePacket;
using Exceptions::Exception;
void ReadMemory::init() {
if (this->data.size() < 4) {
throw Exception("Invalid packet length");
}
auto packetString = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 1),
static_cast<int>(this->data.size() - 1)
);
/*
* The read memory ('m') packet consists of two segments, an address and a number of bytes to read.
* These are separated by a comma character.
*/
auto packetSegments = packetString.split(",");
if (packetSegments.size() != 2) {
throw Exception(
"Unexpected number of segments in packet data: " + std::to_string(packetSegments.size())
);
}
bool conversionStatus = false;
this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse start address from read memory packet data");
}
this->bytes = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse read length from read memory packet data");
}
}
void ReadMemory::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Handling ReadMemory packet");
try {
auto memoryType = this->getMemoryTypeFromGdbAddress(this->startAddress);
auto startAddress = this->removeMemoryTypeIndicatorFromGdbAddress(this->startAddress);
auto memoryBuffer = targetControllerConsole.readMemory(memoryType, startAddress, this->bytes);
auto hexMemoryBuffer = Packet::dataToHex(memoryBuffer);
debugSession.connection.writePacket(
ResponsePacket(std::vector<unsigned char>(hexMemoryBuffer.begin(), hexMemoryBuffer.end()))
);
} catch (const Exception& exception) {
Logger::error("Failed to read memory from target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
}

View File

@@ -3,15 +3,15 @@
#include <cstdint> #include <cstdint>
#include <optional> #include <optional>
#include "CommandPacket.hpp" #include "AbstractMemoryAccessPacket.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets namespace Bloom::DebugServers::Gdb::AvrGdb::CommandPackets
{ {
/** /**
* The ReadMemory class implements a structure for "m" packets. Upon receiving these packets, the server is * The ReadMemory class implements a structure for "m" packets. Upon receiving these packets, the server is
* expected to read memory from the target and send it the client. * expected to read memory from the target and send it the client.
*/ */
class ReadMemory: public CommandPacket class ReadMemory: public AbstractMemoryAccessPacket
{ {
public: public:
/** /**
@@ -28,13 +28,13 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
*/ */
std::uint32_t bytes = 0; std::uint32_t bytes = 0;
explicit ReadMemory(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) { explicit ReadMemory(const std::vector<unsigned char>& rawPacket): AbstractMemoryAccessPacket(rawPacket) {
init(); init();
}; };
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
private: protected:
void init(); void init();
}; };
} }

View File

@@ -1,14 +1,17 @@
#include "WriteMemory.hpp" #include "WriteMemory.hpp"
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" #include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/OkResponsePacket.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets #include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServers::Gdb::AvrGdb::CommandPackets
{ {
using namespace Bloom::Exceptions; using ResponsePackets::ErrorResponsePacket;
using ResponsePackets::OkResponsePacket;
void WriteMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { using namespace Bloom::Exceptions;
gdbRspDebugServer.handleGdbPacket(*this);
}
void WriteMemory::init() { void WriteMemory::init() {
if (this->data.size() < 4) { if (this->data.size() < 4) {
@@ -57,4 +60,27 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
throw Exception("Buffer size does not match length value given in write memory packet"); throw Exception("Buffer size does not match length value given in write memory packet");
} }
} }
void WriteMemory::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Handling WriteMemory packet");
try {
const auto memoryType = this->getMemoryTypeFromGdbAddress(this->startAddress);
if (memoryType == Targets::TargetMemoryType::FLASH) {
throw Exception(
"GDB client requested a flash memory write - This is not currently supported by Bloom."
);
}
const auto startAddress = this->removeMemoryTypeIndicatorFromGdbAddress(this->startAddress);
targetControllerConsole.writeMemory(memoryType, startAddress, this->buffer);
debugSession.connection.writePacket(OkResponsePacket());
} catch (const Exception& exception) {
Logger::error("Failed to write memory to target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
} }

View File

@@ -3,16 +3,15 @@
#include <cstdint> #include <cstdint>
#include <optional> #include <optional>
#include "CommandPacket.hpp" #include "AbstractMemoryAccessPacket.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets namespace Bloom::DebugServers::Gdb::AvrGdb::CommandPackets
{ {
/** /**
* The WriteMemory class implements the structure for "M" packets. Upon receiving this packet, the server is * The WriteMemory class implements the structure for "M" packets. Upon receiving this packet, the server is
* expected to write data to the target's memory, at the specified start address. * expected to write data to the target's memory, at the specified start address.
*/ */
class WriteMemory: public CommandPacket class WriteMemory: public AbstractMemoryAccessPacket
{ {
public: public:
/** /**
@@ -23,11 +22,11 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
Targets::TargetMemoryBuffer buffer; Targets::TargetMemoryBuffer buffer;
explicit WriteMemory(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) { explicit WriteMemory(const std::vector<unsigned char>& rawPacket): AbstractMemoryAccessPacket(rawPacket) {
init(); init();
}; };
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
private: private:
void init(); void init();

View File

@@ -1,10 +1,45 @@
#include "CommandPacket.hpp" #include "CommandPacket.hpp"
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" #include "src/DebugServers/GdbRsp/ResponsePackets/ResponsePacket.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/TargetStopped.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServers/GdbRsp/Signal.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets namespace Bloom::DebugServers::Gdb::CommandPackets
{ {
void CommandPacket::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { using ResponsePackets::ResponsePacket;
gdbRspDebugServer.handleGdbPacket(*this); using ResponsePackets::OkResponsePacket;
using ResponsePackets::TargetStopped;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
void CommandPacket::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
auto packetData = this->getData();
auto packetString = std::string(packetData.begin(), packetData.end());
if (packetString[0] == '?') {
// Status report
debugSession.connection.writePacket(TargetStopped(Signal::TRAP));
} else if (packetString[0] == 'D') {
// Detach packet - there's not really anything we need to do here, so just respond with an OK
debugSession.connection.writePacket(OkResponsePacket());
} else if (packetString.find("qAttached") == 0) {
Logger::debug("Handling qAttached");
debugSession.connection.writePacket(ResponsePacket({1}));
} else {
Logger::debug("Unknown GDB RSP packet: " + packetString + " - returning empty response");
// Respond with an empty packet
debugSession.connection.writePacket(ResponsePacket({0}));
}
} }
} }

View File

@@ -4,10 +4,9 @@
#include <memory> #include <memory>
#include "src/DebugServers/GdbRsp/Packet.hpp" #include "src/DebugServers/GdbRsp/Packet.hpp"
#include "src/DebugServers/GdbRsp/DebugSession.hpp"
namespace Bloom::DebugServers::Gdb { #include "src/TargetController/TargetControllerConsole.hpp"
class GdbRspDebugServer;
}
namespace Bloom::DebugServers::Gdb::CommandPackets namespace Bloom::DebugServers::Gdb::CommandPackets
{ {
@@ -40,12 +39,13 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
explicit CommandPacket(const std::vector<unsigned char>& rawPacket): Packet(rawPacket) {} explicit CommandPacket(const std::vector<unsigned char>& rawPacket): Packet(rawPacket) {}
/** /**
* Double dispatches the packet to the appropriate overload of handleGdbPacket(), within the passed instance of * Should handle the command for the current active debug session.
* GdbRspDebugServer. If there is no overload defined for a specific CommandPacket-inherited type, the
* generic GdbRspDebugServer::handleGdbPacket(CommandPacket&) is used.
* *
* @param gdbRspDebugServer * @param debugSession
* The current active debug session.
*
* @param targetControllerConsole
*/ */
virtual void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer); virtual void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole);
}; };
} }

View File

@@ -119,13 +119,13 @@ namespace Bloom::DebugServers::Gdb
return std::make_unique<CommandPackets::StepExecution>(rawPacket); return std::make_unique<CommandPackets::StepExecution>(rawPacket);
} }
if (rawPacketString[1] == 'm') { // if (rawPacketString[1] == 'm') {
return std::make_unique<CommandPackets::ReadMemory>(rawPacket); // return std::make_unique<CommandPackets::ReadMemory>(rawPacket);
} // }
//
if (rawPacketString[1] == 'M') { // if (rawPacketString[1] == 'M') {
return std::make_unique<CommandPackets::WriteMemory>(rawPacket); // return std::make_unique<CommandPackets::WriteMemory>(rawPacket);
} // }
if (rawPacketString[1] == 'Z') { if (rawPacketString[1] == 'Z') {
return std::make_unique<CommandPackets::SetBreakpoint>(rawPacket); return std::make_unique<CommandPackets::SetBreakpoint>(rawPacket);

View File

@@ -9,8 +9,6 @@
#include "SupportedFeaturesQuery.hpp" #include "SupportedFeaturesQuery.hpp"
#include "ReadRegisters.hpp" #include "ReadRegisters.hpp"
#include "WriteRegister.hpp" #include "WriteRegister.hpp"
#include "ReadMemory.hpp"
#include "WriteMemory.hpp"
#include "StepExecution.hpp" #include "StepExecution.hpp"
#include "ContinueExecution.hpp" #include "ContinueExecution.hpp"
#include "SetBreakpoint.hpp" #include "SetBreakpoint.hpp"

View File

@@ -1,14 +1,14 @@
#include "ContinueExecution.hpp" #include "ContinueExecution.hpp"
#include <cstdint> #include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" #include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets namespace Bloom::DebugServers::Gdb::CommandPackets
{ {
void ContinueExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { using ResponsePackets::ErrorResponsePacket;
gdbRspDebugServer.handleGdbPacket(*this); using Exceptions::Exception;
}
void ContinueExecution::init() { void ContinueExecution::init() {
if (this->data.size() > 1) { if (this->data.size() > 1) {
@@ -17,4 +17,17 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
); );
} }
} }
void ContinueExecution::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Handling ContinueExecution packet");
try {
targetControllerConsole.continueTargetExecution(this->fromProgramCounter);
debugSession.waitingForBreak = true;
} catch (const Exception& exception) {
Logger::error("Failed to continue execution on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
} }

View File

@@ -28,7 +28,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
init(); init();
} }
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
private: private:
void init(); void init();

View File

@@ -1,10 +1,29 @@
#include "InterruptExecution.hpp" #include "InterruptExecution.hpp"
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" #include "src/DebugServers/GdbRsp/ResponsePackets/TargetStopped.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServers/GdbRsp/Signal.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets namespace Bloom::DebugServers::Gdb::CommandPackets
{ {
void InterruptExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { using ResponsePackets::TargetStopped;
gdbRspDebugServer.handleGdbPacket(*this); using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
void InterruptExecution::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Handling InterruptExecution packet");
try {
targetControllerConsole.stopTargetExecution();
debugSession.connection.writePacket(TargetStopped(Signal::INTERRUPTED));
debugSession.waitingForBreak = false;
} catch (const Exception& exception) {
Logger::error("Failed to interrupt execution - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
} }
} }

View File

@@ -17,6 +17,6 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
public: public:
explicit InterruptExecution(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {} explicit InterruptExecution(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {}
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
}; };
} }

View File

@@ -1,48 +0,0 @@
#include "ReadMemory.hpp"
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
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");
}
auto packetString = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 1),
static_cast<int>(this->data.size() - 1)
);
/*
* The read memory ('m') packet consists of two segments, an address and a number of bytes to read.
* These are separated by a comma character.
*/
auto packetSegments = packetString.split(",");
if (packetSegments.size() != 2) {
throw Exception(
"Unexpected number of segments in packet data: " + std::to_string(packetSegments.size())
);
}
bool conversionStatus = false;
this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse start address from read memory packet data");
}
this->bytes = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse read length from read memory packet data");
}
}
}

View File

@@ -1,12 +1,22 @@
#include "ReadRegisters.hpp" #include "ReadRegisters.hpp"
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" #include "src/DebugServers/GdbRsp/ResponsePackets/TargetStopped.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetRegister.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets namespace Bloom::DebugServers::Gdb::CommandPackets
{ {
void ReadRegisters::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { using Targets::TargetRegister;
gdbRspDebugServer.handleGdbPacket(*this); using Targets::TargetRegisterDescriptors;
}
using ResponsePackets::ResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
void ReadRegisters::init() { void ReadRegisters::init() {
if (this->data.size() >= 2 && this->data.front() == 'p') { if (this->data.size() >= 2 && this->data.front() == 'p') {
@@ -16,4 +26,69 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
); );
} }
} }
void ReadRegisters::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Handling ReadRegisters packet");
try {
const auto& targetDescriptor = debugSession.targetDescriptor;
auto descriptors = TargetRegisterDescriptors();
if (this->registerNumber.has_value()) {
Logger::debug("Reading register number: " + std::to_string(this->registerNumber.value()));
descriptors.insert(targetDescriptor.getTargetRegisterDescriptorFromNumber(this->registerNumber.value()));
} else {
// Read all target registers mapped to a GDB register
for (const auto& descriptor : targetDescriptor.getRegisterNumberToDescriptorMapping().getMap()) {
descriptors.insert(targetDescriptor.getTargetRegisterDescriptorFromNumber(descriptor.second.number));
}
}
auto registerSet = targetControllerConsole.readRegisters(descriptors);
/*
* Sort each register by their respective GDB register number - this will leave us with a collection of
* registers in the order expected by the GDB client.
*/
std::sort(
registerSet.begin(),
registerSet.end(),
[this](const TargetRegister& registerA, const TargetRegister& registerB) {
return targetDescriptor.getRegisterNumberFromTargetRegisterDescriptor(registerA.descriptor) <
targetDescriptor.getRegisterNumberFromTargetRegisterDescriptor(registerB.descriptor);
}
);
/*
* Finally, reverse the register values (as they're all currently in MSB, but GDB expects them in LSB), ensure
* that each register value size matches the size in the associated GDB register descriptor, implode the
* values, convert to hexadecimal form and send to the GDB client.
*/
auto registers = std::vector<unsigned char>();
for (auto& reg : registerSet) {
std::reverse(reg.value.begin(), reg.value.end());
const auto gdbRegisterNumber = targetDescriptor.getRegisterNumberFromTargetRegisterDescriptor(
reg.descriptor
).value();
const auto& gdbRegisterDescriptor = targetDescriptor.getRegisterDescriptorFromNumber(gdbRegisterNumber);
if (reg.value.size() < gdbRegisterDescriptor.size) {
reg.value.insert(reg.value.end(), (gdbRegisterDescriptor.size - reg.value.size()), 0x00);
}
registers.insert(registers.end(), reg.value.begin(), reg.value.end());
}
auto responseRegisters = Packet::dataToHex(registers);
debugSession.connection.writePacket(
ResponsePacket(std::vector<unsigned char>(responseRegisters.begin(), responseRegisters.end()))
);
} catch (const Exception& exception) {
Logger::error("Failed to read general registers - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
} }

View File

@@ -29,7 +29,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
init(); init();
}; };
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
private: private:
void init(); void init();

View File

@@ -2,15 +2,22 @@
#include <QtCore/QString> #include <QtCore/QString>
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" #include "src/DebugServers/GdbRsp/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetBreakpoint.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets namespace Bloom::DebugServers::Gdb::CommandPackets
{ {
using namespace Bloom::Exceptions; using Targets::TargetBreakpoint;
void RemoveBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { using ResponsePackets::OkResponsePacket;
gdbRspDebugServer.handleGdbPacket(*this); using ResponsePackets::ErrorResponsePacket;
}
using Exceptions::Exception;
void RemoveBreakpoint::init() { void RemoveBreakpoint::init() {
if (data.size() < 6) { if (data.size() < 6) {
@@ -38,4 +45,20 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
throw Exception("Failed to convert address hex value from RemoveBreakpoint packet."); throw Exception("Failed to convert address hex value from RemoveBreakpoint packet.");
} }
} }
void RemoveBreakpoint::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Removing breakpoint at address " + std::to_string(this->address));
try {
auto breakpoint = TargetBreakpoint();
breakpoint.address = this->address;
targetControllerConsole.removeBreakpoint(breakpoint);
debugSession.connection.writePacket(OkResponsePacket());
} catch (const Exception& exception) {
Logger::error("Failed to remove breakpoint on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
} }

View File

@@ -4,13 +4,8 @@
#include <string> #include <string>
#include <set> #include <set>
#include "../BreakpointType.hpp"
#include "CommandPacket.hpp" #include "CommandPacket.hpp"
#include "src/DebugServers/GdbRsp/BreakpointType.hpp"
namespace Bloom::DebugServers::Gdb
{
enum class Feature: int;
}
namespace Bloom::DebugServers::Gdb::CommandPackets namespace Bloom::DebugServers::Gdb::CommandPackets
{ {
@@ -35,7 +30,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
this->init(); this->init();
}; };
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
private: private:
void init(); void init();

View File

@@ -2,15 +2,22 @@
#include <QtCore/QString> #include <QtCore/QString>
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" #include "src/DebugServers/GdbRsp/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetBreakpoint.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets namespace Bloom::DebugServers::Gdb::CommandPackets
{ {
using namespace Bloom::Exceptions; using Targets::TargetBreakpoint;
void SetBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { using ResponsePackets::OkResponsePacket;
gdbRspDebugServer.handleGdbPacket(*this); using ResponsePackets::ErrorResponsePacket;
}
using Exceptions::Exception;
void SetBreakpoint::init() { void SetBreakpoint::init() {
if (data.size() < 6) { if (data.size() < 6) {
@@ -38,4 +45,20 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
throw Exception("Failed to convert address hex value from SetBreakpoint packet."); throw Exception("Failed to convert address hex value from SetBreakpoint packet.");
} }
} }
void SetBreakpoint::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Handling SetBreakpoint packet");
try {
auto breakpoint = TargetBreakpoint();
breakpoint.address = this->address;
targetControllerConsole.setBreakpoint(breakpoint);
debugSession.connection.writePacket(OkResponsePacket());
} catch (const Exception& exception) {
Logger::error("Failed to set breakpoint on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
} }

View File

@@ -4,13 +4,8 @@
#include <string> #include <string>
#include <set> #include <set>
#include "../BreakpointType.hpp"
#include "CommandPacket.hpp" #include "CommandPacket.hpp"
#include "src/DebugServers/GdbRsp/BreakpointType.hpp"
namespace Bloom::DebugServers::Gdb
{
enum class Feature: int;
}
namespace Bloom::DebugServers::Gdb::CommandPackets namespace Bloom::DebugServers::Gdb::CommandPackets
{ {
@@ -35,7 +30,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
this->init(); this->init();
}; };
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
private: private:
void init(); void init();

View File

@@ -1,14 +1,15 @@
#include "StepExecution.hpp" #include "StepExecution.hpp"
#include <cstdint> #include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" #include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets namespace Bloom::DebugServers::Gdb::CommandPackets
{ {
void StepExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { using ResponsePackets::ErrorResponsePacket;
gdbRspDebugServer.handleGdbPacket(*this);
} using Exceptions::Exception;
void StepExecution::init() { void StepExecution::init() {
if (this->data.size() > 1) { if (this->data.size() > 1) {
@@ -17,4 +18,17 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
); );
} }
} }
void StepExecution::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Handling StepExecution packet");
try {
targetControllerConsole.stepTargetExecution(this->fromProgramCounter);
debugSession.waitingForBreak = true;
} catch (const Exception& exception) {
Logger::error("Failed to step execution on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
} }

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <cstdint>
#include <optional> #include <optional>
#include "CommandPacket.hpp" #include "CommandPacket.hpp"
@@ -22,7 +23,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
init(); init();
}; };
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
private: private:
void init(); void init();

View File

@@ -2,13 +2,22 @@
#include <QtCore/QString> #include <QtCore/QString>
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" #include "src/DebugServers/GdbRsp/Feature.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/SupportedFeaturesResponse.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
#include "src/DebugServers/GdbRsp/Exceptions/ClientNotSupported.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets namespace Bloom::DebugServers::Gdb::CommandPackets
{ {
void SupportedFeaturesQuery::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { using ResponsePackets::SupportedFeaturesResponse;
gdbRspDebugServer.handleGdbPacket(*this); using ResponsePackets::ErrorResponsePacket;
}
using Bloom::Exceptions::Exception;
using Gdb::Exceptions::ClientNotSupported;
void SupportedFeaturesQuery::init() { void SupportedFeaturesQuery::init() {
/* /*
@@ -41,4 +50,23 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
} }
} }
} }
void SupportedFeaturesQuery::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Handling QuerySupport packet");
if (!this->isFeatureSupported(Feature::HARDWARE_BREAKPOINTS)
&& !this->isFeatureSupported(Feature::SOFTWARE_BREAKPOINTS)
) {
// All GDB clients are expected to support breakpoints!
throw ClientNotSupported("GDB client does not support HW or SW breakpoints");
}
// Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom
auto response = SupportedFeaturesResponse({
{Feature::SOFTWARE_BREAKPOINTS, std::nullopt},
{Feature::PACKET_SIZE, std::to_string(debugSession.connection.getMaxPacketSize())},
});
debugSession.connection.writePacket(response);
}
} }

View File

@@ -34,7 +34,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
return this->supportedFeatures; return this->supportedFeatures;
} }
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
private: private:
std::set<Feature> supportedFeatures; std::set<Feature> supportedFeatures;

View File

@@ -1,15 +1,24 @@
#include "WriteRegister.hpp" #include "WriteRegister.hpp"
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp" #include "src/DebugServers/GdbRsp/ResponsePackets/TargetStopped.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServers/GdbRsp/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetRegister.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp" #include "src/Exceptions/Exception.hpp"
namespace Bloom::DebugServers::Gdb::CommandPackets namespace Bloom::DebugServers::Gdb::CommandPackets
{ {
using namespace Bloom::Exceptions; using Targets::TargetRegister;
using Targets::TargetRegisterDescriptors;
void WriteRegister::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) { using ResponsePackets::ResponsePacket;
gdbRspDebugServer.handleGdbPacket(*this); using ResponsePackets::OkResponsePacket;
} using ResponsePackets::ErrorResponsePacket;
using Exceptions::Exception;
void WriteRegister::init() { void WriteRegister::init() {
// The P packet updates a single register // The P packet updates a single register
@@ -28,4 +37,41 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
this->registerValue = Packet::hexToData(packetSegments.back().toStdString()); this->registerValue = Packet::hexToData(packetSegments.back().toStdString());
std::reverse(this->registerValue.begin(), this->registerValue.end()); std::reverse(this->registerValue.begin(), this->registerValue.end());
} }
void WriteRegister::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
Logger::debug("Handling WriteRegister packet");
try {
auto targetRegisterDescriptor = debugSession.targetDescriptor.getTargetRegisterDescriptorFromNumber(this->registerNumber);
const auto valueSize = this->registerValue.size();
if (valueSize > 0 && valueSize > targetRegisterDescriptor.size) {
// Attempt to trim the higher zero-value bytes from the register value, until we reach the correct size.
for (auto i = this->registerValue.size() - 1; i >= targetRegisterDescriptor.size; i--) {
if (this->registerValue.at(i) != 0x00) {
// If we reach a non-zero byte, we cannot trim anymore without changing the data
break;
}
this->registerValue.erase(this->registerValue.begin() + i);
}
if (this->registerValue.size() > targetRegisterDescriptor.size) {
const auto& gdbRegisterDescriptor = debugSession.targetDescriptor.getRegisterDescriptorFromNumber(this->registerNumber);
throw Exception("Cannot set value for " + gdbRegisterDescriptor.name
+ " - value size exceeds register size."
);
}
}
targetControllerConsole.writeRegisters({
TargetRegister(targetRegisterDescriptor, this->registerValue)
});
debugSession.connection.writePacket(OkResponsePacket());
} catch (const Exception& exception) {
Logger::error("Failed to write registers - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket());
}
}
} }

View File

@@ -21,7 +21,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
init(); init();
}; };
void dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) override; void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
private: private:
void init(); void init();

View File

@@ -23,16 +23,9 @@ namespace Bloom::DebugServers::Gdb
class Connection class Connection
{ {
public: public:
/**
* When the GDB client is waiting for the target to reach a breakpoint, this is set to true so we know when to
* notify the client.
*
* @TODO: This is pretty gross. Consider rethinking it.
*/
bool waitingForBreak = false;
explicit Connection(std::shared_ptr<EventNotifier> interruptEventNotifier) explicit Connection(std::shared_ptr<EventNotifier> interruptEventNotifier)
: interruptEventNotifier(std::move(interruptEventNotifier)) {}; : interruptEventNotifier(std::move(interruptEventNotifier))
{};
/** /**
* Accepts a connection on serverSocketFileDescriptor. * Accepts a connection on serverSocketFileDescriptor.

View File

@@ -26,264 +26,6 @@ namespace Bloom::DebugServers::Gdb
using Bloom::Targets::TargetRegisterDescriptors; using Bloom::Targets::TargetRegisterDescriptors;
using Bloom::Targets::TargetBreakpoint; using Bloom::Targets::TargetBreakpoint;
void GdbRspDebugServer::handleGdbPacket(CommandPacket& packet) {
auto packetData = packet.getData();
auto packetString = std::string(packetData.begin(), packetData.end());
if (packetString[0] == '?') {
// Status report
this->clientConnection->writePacket(TargetStopped(Signal::TRAP));
} else if (packetString[0] == 'D') {
// Detach packet - there's not really anything we need to do here, so just respond with an OK
this->clientConnection->writePacket(ResponsePacket({'O', 'K'}));
} else if (packetString.find("qAttached") == 0) {
Logger::debug("Handling qAttached");
this->clientConnection->writePacket(ResponsePacket({1}));
} else {
Logger::debug("Unknown GDB RSP packet: " + packetString + " - returning empty response");
// Respond with an empty packet
this->clientConnection->writePacket(ResponsePacket({0}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::SupportedFeaturesQuery& packet) {
Logger::debug("Handling QuerySupport packet");
if (!packet.isFeatureSupported(Feature::HARDWARE_BREAKPOINTS)
&& !packet.isFeatureSupported(Feature::SOFTWARE_BREAKPOINTS)
) {
// All GDB clients are expected to support breakpoints!
throw ClientNotSupported("GDB client does not support HW or SW breakpoints");
}
// Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom
auto response = ResponsePackets::SupportedFeaturesResponse({
{Feature::SOFTWARE_BREAKPOINTS, std::nullopt},
{Feature::PACKET_SIZE, std::to_string(this->clientConnection->getMaxPacketSize())},
});
this->clientConnection->writePacket(response);
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::ReadRegisters& packet) {
Logger::debug("Handling ReadRegisters packet");
try {
auto descriptors = TargetRegisterDescriptors();
if (packet.registerNumber.has_value()) {
Logger::debug("Reading register number: " + std::to_string(packet.registerNumber.value()));
descriptors.insert(this->getTargetRegisterDescriptorFromNumber(packet.registerNumber.value()));
} else {
// Read all target registers mapped to a GDB register
for (const auto& descriptor : this->getRegisterNumberToDescriptorMapping().getMap()) {
descriptors.insert(this->getTargetRegisterDescriptorFromNumber(descriptor.second.number));
}
}
auto registerSet = this->targetControllerConsole.readRegisters(descriptors);
/*
* Sort each register by their respective GDB register number - this will leave us with a collection of
* registers in the order expected by the GDB client.
*/
std::sort(
registerSet.begin(),
registerSet.end(),
[this](const TargetRegister& registerA, const TargetRegister& registerB) {
return this->getRegisterNumberFromTargetRegisterDescriptor(registerA.descriptor) <
this->getRegisterNumberFromTargetRegisterDescriptor(registerB.descriptor);
}
);
/*
* Finally, reverse the register values (as they're all currently in MSB, but GDB expects them in LSB), ensure
* that each register value size matches the size in the associated GDB register descriptor, implode the
* values, convert to hexadecimal form and send to the GDB client.
*/
auto registers = std::vector<unsigned char>();
for (auto& reg : registerSet) {
std::reverse(reg.value.begin(), reg.value.end());
const auto gdbRegisterNumber = this->getRegisterNumberFromTargetRegisterDescriptor(
reg.descriptor
).value();
const auto& gdbRegisterDescriptor = this->getRegisterDescriptorFromNumber(gdbRegisterNumber);
if (reg.value.size() < gdbRegisterDescriptor.size) {
reg.value.insert(reg.value.end(), (gdbRegisterDescriptor.size - reg.value.size()), 0x00);
}
registers.insert(registers.end(), reg.value.begin(), reg.value.end());
}
auto responseRegisters = Packet::dataToHex(registers);
this->clientConnection->writePacket(
ResponsePacket(std::vector<unsigned char>(responseRegisters.begin(), responseRegisters.end()))
);
} catch (const Exception& exception) {
Logger::error("Failed to read general registers - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::WriteRegister& packet) {
Logger::debug("Handling WriteRegister packet");
try {
auto targetRegisterDescriptor = this->getTargetRegisterDescriptorFromNumber(packet.registerNumber);
const auto valueSize = packet.registerValue.size();
if (valueSize > 0 && valueSize > targetRegisterDescriptor.size) {
// Attempt to trim the higher zero-value bytes from the register value, until we reach the correct size.
for (auto i = packet.registerValue.size() - 1; i >= targetRegisterDescriptor.size; i--) {
if (packet.registerValue.at(i) != 0x00) {
// If we reach a non-zero byte, we cannot trim anymore without changing the data
break;
}
packet.registerValue.erase(packet.registerValue.begin() + i);
}
if (packet.registerValue.size() > targetRegisterDescriptor.size) {
const auto& gdbRegisterDescriptor = this->getRegisterDescriptorFromNumber(packet.registerNumber);
throw Exception("Cannot set value for " + gdbRegisterDescriptor.name
+ " - value size exceeds register size."
);
}
}
this->targetControllerConsole.writeRegisters({
TargetRegister(targetRegisterDescriptor, packet.registerValue)
});
this->clientConnection->writePacket(ResponsePacket({'O', 'K'}));
} catch (const Exception& exception) {
Logger::error("Failed to write registers - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::ContinueExecution& packet) {
Logger::debug("Handling ContinueExecution packet");
try {
this->targetControllerConsole.continueTargetExecution(packet.fromProgramCounter);
this->clientConnection->waitingForBreak = true;
} catch (const Exception& exception) {
Logger::error("Failed to continue execution on target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::StepExecution& packet) {
Logger::debug("Handling StepExecution packet");
try {
this->targetControllerConsole.stepTargetExecution(packet.fromProgramCounter);
this->clientConnection->waitingForBreak = true;
} catch (const Exception& exception) {
Logger::error("Failed to step execution on target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::ReadMemory& packet) {
Logger::debug("Handling ReadMemory packet");
try {
auto memoryType = this->getMemoryTypeFromGdbAddress(packet.startAddress);
auto startAddress = this->removeMemoryTypeIndicatorFromGdbAddress(packet.startAddress);
auto memoryBuffer = this->targetControllerConsole.readMemory(memoryType, startAddress, packet.bytes);
auto hexMemoryBuffer = Packet::dataToHex(memoryBuffer);
this->clientConnection->writePacket(
ResponsePacket(std::vector<unsigned char>(hexMemoryBuffer.begin(), hexMemoryBuffer.end()))
);
} catch (const Exception& exception) {
Logger::error("Failed to read memory from target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::WriteMemory& packet) {
Logger::debug("Handling WriteMemory packet");
try {
const auto memoryType = this->getMemoryTypeFromGdbAddress(packet.startAddress);
if (memoryType == Targets::TargetMemoryType::FLASH) {
throw Exception(
"GDB client requested a flash memory write - This is not currently supported by Bloom."
);
}
const auto startAddress = this->removeMemoryTypeIndicatorFromGdbAddress(packet.startAddress);
this->targetControllerConsole.writeMemory(memoryType, startAddress, packet.buffer);
this->clientConnection->writePacket(ResponsePacket({'O', 'K'}));
} catch (const Exception& exception) {
Logger::error("Failed to write memory to target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::SetBreakpoint& packet) {
Logger::debug("Handling SetBreakpoint packet");
try {
auto breakpoint = TargetBreakpoint();
breakpoint.address = packet.address;
this->targetControllerConsole.setBreakpoint(breakpoint);
this->clientConnection->writePacket(ResponsePacket({'O', 'K'}));
} catch (const Exception& exception) {
Logger::error("Failed to set breakpoint on target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::RemoveBreakpoint& packet) {
Logger::debug("Removing breakpoint at address " + std::to_string(packet.address));
try {
auto breakpoint = TargetBreakpoint();
breakpoint.address = packet.address;
this->targetControllerConsole.removeBreakpoint(breakpoint);
this->clientConnection->writePacket(ResponsePacket({'O', 'K'}));
} catch (const Exception& exception) {
Logger::error("Failed to remove breakpoint on target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::InterruptExecution& packet) {
Logger::debug("Handling InterruptExecution packet");
try {
this->targetControllerConsole.stopTargetExecution();
this->clientConnection->writePacket(TargetStopped(Signal::INTERRUPTED));
this->clientConnection->waitingForBreak = false;
} catch (const Exception& exception) {
Logger::error("Failed to interrupt execution - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::init() { void GdbRspDebugServer::init() {
this->debugServerConfig = GdbDebugServerConfig(DebugServer::debugServerConfig); this->debugServerConfig = GdbDebugServerConfig(DebugServer::debugServerConfig);
@@ -364,7 +106,7 @@ namespace Bloom::DebugServers::Gdb
} }
void GdbRspDebugServer::close() { void GdbRspDebugServer::close() {
this->closeClientConnection(); this->terminateActiveDebugSession();
if (this->serverSocketFileDescriptor > 0) { if (this->serverSocketFileDescriptor > 0) {
::close(this->serverSocketFileDescriptor); ::close(this->serverSocketFileDescriptor);
@@ -373,16 +115,20 @@ namespace Bloom::DebugServers::Gdb
void GdbRspDebugServer::serve() { void GdbRspDebugServer::serve() {
try { try {
if (!this->clientConnection.has_value()) { if (!this->activeDebugSession.has_value()) {
Logger::info("Waiting for GDB RSP connection"); Logger::info("Waiting for GDB RSP connection");
do { auto connection = this->waitForConnection();
this->waitForConnection();
} while (!this->clientConnection.has_value()); if (!connection.has_value()) {
// Likely an interrupt
return;
}
this->clientConnection->accept(this->serverSocketFileDescriptor); connection->accept(this->serverSocketFileDescriptor);
Logger::info("Accepted GDP RSP connection from " + this->clientConnection->getIpAddress()); Logger::info("Accepted GDP RSP connection from " + connection->getIpAddress());
this->activeDebugSession = DebugSession(connection.value(), this->getGdbTargetDescriptor());
EventManager::triggerEvent(std::make_shared<Events::DebugSessionStarted>()); EventManager::triggerEvent(std::make_shared<Events::DebugSessionStarted>());
/* /*
@@ -390,39 +136,39 @@ namespace Bloom::DebugServers::Gdb
* service it. * service it.
*/ */
if (!this->targetControllerConsole.isTargetControllerInService()) { if (!this->targetControllerConsole.isTargetControllerInService()) {
this->closeClientConnection(); this->terminateActiveDebugSession();
throw DebugSessionAborted("TargetController not in service"); throw DebugSessionAborted("TargetController not in service");
} }
} }
auto packets = this->clientConnection->readPackets(); // auto packets = this->activeDebugSession->connection.readPackets();
//
// Only process the last packet - any others will likely be duplicates from an impatient client // // Only process the last packet - any others will likely be duplicates from an impatient client
if (!packets.empty()) { // if (!packets.empty()) {
// Double-dispatch to appropriate handler // // Double-dispatch to appropriate handler
packets.back()->dispatchToHandler(*this); // packets.back()->dispatchToHandler(*this);
} // }
} catch (const ClientDisconnected&) { } catch (const ClientDisconnected&) {
Logger::info("GDB RSP client disconnected"); Logger::info("GDB RSP client disconnected");
this->closeClientConnection(); this->terminateActiveDebugSession();
return; return;
} catch (const ClientCommunicationError& exception) { } catch (const ClientCommunicationError& exception) {
Logger::error( Logger::error(
"GDB RSP client communication error - " + exception.getMessage() + " - closing connection" "GDB RSP client communication error - " + exception.getMessage() + " - closing connection"
); );
this->closeClientConnection(); this->terminateActiveDebugSession();
return; return;
} catch (const ClientNotSupported& exception) { } catch (const ClientNotSupported& exception) {
Logger::error("Invalid GDB RSP client - " + exception.getMessage() + " - closing connection"); Logger::error("Invalid GDB RSP client - " + exception.getMessage() + " - closing connection");
this->closeClientConnection(); this->terminateActiveDebugSession();
return; return;
} catch (const DebugSessionAborted& exception) { } catch (const DebugSessionAborted& exception) {
Logger::warning("GDB debug session aborted - " + exception.getMessage()); Logger::warning("GDB debug session aborted - " + exception.getMessage());
this->closeClientConnection(); this->terminateActiveDebugSession();
return; return;
} catch (const DebugServerInterrupted&) { } catch (const DebugServerInterrupted&) {
@@ -432,7 +178,7 @@ namespace Bloom::DebugServers::Gdb
} }
} }
void GdbRspDebugServer::waitForConnection() { std::optional<Connection> GdbRspDebugServer::waitForConnection() {
if (::listen(this->serverSocketFileDescriptor, 3) != 0) { if (::listen(this->serverSocketFileDescriptor, 3) != 0) {
throw Exception("Failed to listen on server socket"); throw Exception("Failed to listen on server socket");
} }
@@ -453,25 +199,36 @@ namespace Bloom::DebugServers::Gdb
if (fileDescriptor == this->interruptEventNotifier->getFileDescriptor()) { if (fileDescriptor == this->interruptEventNotifier->getFileDescriptor()) {
// Interrupted // Interrupted
this->interruptEventNotifier->clear(); this->interruptEventNotifier->clear();
throw DebugServerInterrupted(); return std::nullopt;
} }
} }
this->clientConnection = Connection(this->interruptEventNotifier); return Connection(this->interruptEventNotifier);
}
return std::nullopt;
}
void GdbRspDebugServer::terminateActiveDebugSession() {
if (this->activeDebugSession.has_value()) {
this->activeDebugSession->terminate();
this->activeDebugSession = std::nullopt;
EventManager::triggerEvent(std::make_shared<Events::DebugSessionFinished>());
} }
} }
void GdbRspDebugServer::onTargetControllerStateReported(const Events::TargetControllerStateReported& event) { void GdbRspDebugServer::onTargetControllerStateReported(const Events::TargetControllerStateReported& event) {
if (event.state == TargetControllerState::SUSPENDED && this->clientConnection.has_value()) { if (event.state == TargetControllerState::SUSPENDED && this->activeDebugSession.has_value()) {
Logger::warning("Terminating debug session - TargetController suspended unexpectedly"); Logger::warning("Terminating debug session - TargetController suspended unexpectedly");
this->closeClientConnection(); this->terminateActiveDebugSession();
} }
} }
void GdbRspDebugServer::onTargetExecutionStopped(const Events::TargetExecutionStopped&) { void GdbRspDebugServer::onTargetExecutionStopped(const Events::TargetExecutionStopped&) {
if (this->clientConnection.has_value() && this->clientConnection->waitingForBreak) { if (this->activeDebugSession.has_value() && this->activeDebugSession->waitingForBreak) {
this->clientConnection->writePacket(TargetStopped(Signal::TRAP)); this->activeDebugSession->connection.writePacket(TargetStopped(Signal::TRAP));
this->clientConnection->waitingForBreak = false; this->activeDebugSession->waitingForBreak = false;
} }
} }
} }

View File

@@ -11,6 +11,8 @@
#include "GdbDebugServerConfig.hpp" #include "GdbDebugServerConfig.hpp"
#include "Connection.hpp" #include "Connection.hpp"
#include "TargetDescriptor.hpp"
#include "DebugSession.hpp"
#include "Signal.hpp" #include "Signal.hpp"
#include "RegisterDescriptor.hpp" #include "RegisterDescriptor.hpp"
#include "Feature.hpp" #include "Feature.hpp"
@@ -51,85 +53,6 @@ namespace Bloom::DebugServers::Gdb
return "GDB Remote Serial Protocol DebugServer"; 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: protected:
std::optional<GdbDebugServerConfig> debugServerConfig; std::optional<GdbDebugServerConfig> debugServerConfig;
@@ -159,10 +82,7 @@ namespace Bloom::DebugServers::Gdb
*/ */
int enableReuseAddressSocketOption = 1; int enableReuseAddressSocketOption = 1;
/** std::optional<DebugSession> activeDebugSession;
* The current active GDB client connection, if any.
*/
std::optional<Connection> clientConnection;
/** /**
* Prepares the GDB server for listing on the selected address and port. * Prepares the GDB server for listing on the selected address and port.
@@ -180,72 +100,16 @@ namespace Bloom::DebugServers::Gdb
void serve() override; void serve() override;
/** /**
* Waits for a GDB client to connect on the listening socket. Accepts the connection and * Waits for a GDB client to connect on the listening socket.
* sets this->clientConnection.
*/
void waitForConnection();
void closeClientConnection() {
if (this->clientConnection.has_value()) {
this->clientConnection->close();
this->clientConnection = std::nullopt;
EventManager::triggerEvent(std::make_shared<Events::DebugSessionFinished>());
}
}
/**
* GDB clients encode memory type information (flash, ram, eeprom, etc) in memory addresses. This is typically
* hardcoded in the GDB client source. This method extracts memory type information from a given memory address.
* The specifics of the encoding may vary with targets, which is why this method is virtual. For an example,
* see the implementation of this method in AvrGdbRsp.
* *
* @param address * This function may return an std::nullopt, if the waiting was interrupted by some other event.
* @return
*/ */
virtual Targets::TargetMemoryType getMemoryTypeFromGdbAddress(std::uint32_t address) = 0; std::optional<Connection> waitForConnection();
/** void terminateActiveDebugSession();
* Removes memory type information from memory address.
* See comment for GdbRspDebugServer::getMemoryTypeFromGdbAddress()
*
* @param address
* @return
*/
virtual std::uint32_t removeMemoryTypeIndicatorFromGdbAddress(std::uint32_t address) = 0;
/** virtual const TargetDescriptor& getGdbTargetDescriptor() = 0;
* Should return the mapping of GDB register numbers to GDB register descriptors.
*/
virtual const BiMap<GdbRegisterNumberType, RegisterDescriptor>& getRegisterNumberToDescriptorMapping() = 0;
/**
* Should retrieve the GDB register number, given a target register descriptor. Or std::nullopt if the target
* register descriptor isn't mapped to any GDB register.
*
* @param registerDescriptor
* @return
*/
virtual std::optional<GdbRegisterNumberType> getRegisterNumberFromTargetRegisterDescriptor(
const Targets::TargetRegisterDescriptor& registerDescriptor
) = 0;
/**
* Should retrieve the GDB register descriptor for a given GDB register number.
*
* @param number
* @return
*/
virtual const RegisterDescriptor& getRegisterDescriptorFromNumber(GdbRegisterNumberType number) = 0;
/**
* Should retrieve the mapped target register descriptor for a given GDB register number.
*
* @param number
* @return
*/
virtual const Targets::TargetRegisterDescriptor& getTargetRegisterDescriptorFromNumber(
GdbRegisterNumberType number
) = 0;
void onTargetControllerStateReported(const Events::TargetControllerStateReported& event); void onTargetControllerStateReported(const Events::TargetControllerStateReported& event);