Implemented support for breakpoint caching in the GDB server

This commit is contained in:
Nav
2023-04-01 14:30:33 +01:00
parent 06b6c4460b
commit 3a74906541
13 changed files with 129 additions and 6 deletions

View File

@@ -29,6 +29,13 @@ namespace Bloom::DebugServer::Gdb::AvrGdb
this->gdbTargetDescriptor = TargetDescriptor( this->gdbTargetDescriptor = TargetDescriptor(
this->targetControllerService.getTargetDescriptor() this->targetControllerService.getTargetDescriptor()
); );
if (!this->debugServerConfig.breakpointCachingEnabled) {
Logger::warning(
"Breakpoint caching has been disabled - this could result in excessive wear of the AVR target's"
" flash memory"
);
}
} }
std::unique_ptr<Gdb::CommandPackets::CommandPacket> AvrGdbRsp::resolveCommandPacket( std::unique_ptr<Gdb::CommandPackets::CommandPacket> AvrGdbRsp::resolveCommandPacket(

View File

@@ -36,7 +36,13 @@ namespace Bloom::DebugServer::Gdb::CommandPackets
class CommandPacket: public Packet class CommandPacket: public Packet
{ {
public: public:
explicit CommandPacket(const RawPacket& rawPacket): Packet(rawPacket) {} explicit CommandPacket(const RawPacket& rawPacket)
: Packet(rawPacket)
{}
virtual bool requiresBreakpointFlush() const {
return false;
}
/** /**
* Should handle the command for the current active debug session. * Should handle the command for the current active debug session.

View File

@@ -28,6 +28,10 @@ namespace Bloom::DebugServer::Gdb::CommandPackets
explicit ContinueExecution(const RawPacket& rawPacket); explicit ContinueExecution(const RawPacket& rawPacket);
bool requiresBreakpointFlush() const override {
return true;
}
void handle( void handle(
DebugSession& debugSession, DebugSession& debugSession,
Services::TargetControllerService& targetControllerService Services::TargetControllerService& targetControllerService

View File

@@ -9,6 +9,10 @@ namespace Bloom::DebugServer::Gdb::CommandPackets
public: public:
explicit Detach(const RawPacket& rawPacket); explicit Detach(const RawPacket& rawPacket);
bool requiresBreakpointFlush() const override {
return true;
}
void handle( void handle(
DebugSession& debugSession, DebugSession& debugSession,
Services::TargetControllerService& targetControllerService Services::TargetControllerService& targetControllerService

View File

@@ -51,9 +51,18 @@ namespace Bloom::DebugServer::Gdb::CommandPackets
} }
void RemoveBreakpoint::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) { void RemoveBreakpoint::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
Logger::debug("Removing breakpoint at address " + std::to_string(this->address)); Logger::debug("Handling RemoveBreakpoint packet");
try { try {
if (debugSession.serverConfig.breakpointCachingEnabled) {
debugSession.breakpointAddressesPendingRemoval.insert(this->address);
debugSession.connection.writePacket(OkResponsePacket());
return;
}
Logger::debug("Removing breakpoint at address " + std::to_string(this->address));
targetControllerService.removeBreakpoint(TargetBreakpoint(this->address)); targetControllerService.removeBreakpoint(TargetBreakpoint(this->address));
debugSession.connection.writePacket(OkResponsePacket()); debugSession.connection.writePacket(OkResponsePacket());

View File

@@ -54,7 +54,18 @@ namespace Bloom::DebugServer::Gdb::CommandPackets
Logger::debug("Handling SetBreakpoint packet"); Logger::debug("Handling SetBreakpoint packet");
try { try {
targetControllerService.setBreakpoint(TargetBreakpoint(this->address)); if (
!debugSession.serverConfig.breakpointCachingEnabled
|| !debugSession.breakpointAddresses.contains(this->address)
) {
targetControllerService.setBreakpoint(TargetBreakpoint(this->address));
}
if (debugSession.serverConfig.breakpointCachingEnabled) {
debugSession.breakpointAddresses.insert(this->address);
debugSession.breakpointAddressesPendingRemoval.erase(this->address);
}
debugSession.connection.writePacket(OkResponsePacket()); debugSession.connection.writePacket(OkResponsePacket());
} catch (const Exception& exception) { } catch (const Exception& exception) {

View File

@@ -23,6 +23,10 @@ namespace Bloom::DebugServer::Gdb::CommandPackets
explicit StepExecution(const RawPacket& rawPacket); explicit StepExecution(const RawPacket& rawPacket);
bool requiresBreakpointFlush() const override {
return false;
}
void handle( void handle(
DebugSession& debugSession, DebugSession& debugSession,
Services::TargetControllerService& targetControllerService Services::TargetControllerService& targetControllerService

View File

@@ -7,11 +7,13 @@ namespace Bloom::DebugServer::Gdb
DebugSession::DebugSession( DebugSession::DebugSession(
Connection&& connection, Connection&& connection,
const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures, const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures,
const TargetDescriptor& targetDescriptor const TargetDescriptor& targetDescriptor,
const GdbDebugServerConfig& serverConfig
) )
: connection(std::move(connection)) : connection(std::move(connection))
, supportedFeatures(supportedFeatures) , supportedFeatures(supportedFeatures)
, gdbTargetDescriptor(targetDescriptor) , gdbTargetDescriptor(targetDescriptor)
, serverConfig(serverConfig)
{ {
this->supportedFeatures.insert({ this->supportedFeatures.insert({
Feature::PACKET_SIZE, std::to_string(Connection::ABSOLUTE_MAXIMUM_PACKET_READ_SIZE) Feature::PACKET_SIZE, std::to_string(Connection::ABSOLUTE_MAXIMUM_PACKET_READ_SIZE)

View File

@@ -2,12 +2,16 @@
#include <cstdint> #include <cstdint>
#include <optional> #include <optional>
#include <unordered_set>
#include "TargetDescriptor.hpp" #include "TargetDescriptor.hpp"
#include "GdbDebugServerConfig.hpp"
#include "Connection.hpp" #include "Connection.hpp"
#include "Feature.hpp" #include "Feature.hpp"
#include "ProgrammingSession.hpp" #include "ProgrammingSession.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace Bloom::DebugServer::Gdb namespace Bloom::DebugServer::Gdb
{ {
class DebugSession class DebugSession
@@ -29,6 +33,18 @@ namespace Bloom::DebugServer::Gdb
*/ */
const TargetDescriptor& gdbTargetDescriptor; const TargetDescriptor& gdbTargetDescriptor;
/**
* The current server configuration.
*/
const GdbDebugServerConfig& serverConfig;
/**
* Internal bookkeeping of breakpoints managed by GDB. These will both remain empty if the user has disabled
* breakpoint caching.
*/
std::unordered_set<Targets::TargetMemoryAddress> breakpointAddresses;
std::unordered_set<Targets::TargetMemoryAddress> breakpointAddressesPendingRemoval;
/** /**
* When the GDB client is waiting for the target to halt, this is set to true so we know when to notify the * When the GDB client is waiting for the target to halt, this is set to true so we know when to notify the
* client. * client.
@@ -53,7 +69,8 @@ namespace Bloom::DebugServer::Gdb
DebugSession( DebugSession(
Connection&& connection, Connection&& connection,
const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures, const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures,
const TargetDescriptor& targetDescriptor const TargetDescriptor& targetDescriptor,
const GdbDebugServerConfig& serverConfig
); );
DebugSession(const DebugSession& other) = delete; DebugSession(const DebugSession& other) = delete;

View File

@@ -30,5 +30,17 @@ namespace Bloom::DebugServer::Gdb
); );
} }
} }
if (debugServerConfig.debugServerNode["enableBreakpointCaching"]) {
if (YamlUtilities::isCastable<bool>(debugServerConfig.debugServerNode["enableBreakpointCaching"])) {
this->breakpointCachingEnabled = debugServerConfig.debugServerNode["enableBreakpointCaching"].as<bool>();
} else {
Logger::error(
"Invalid GDB debug server config parameter ('enableBreakpointCaching') provided - value must be "
"castable to a boolean. The parameter will be ignored."
);
}
}
} }
} }

View File

@@ -24,6 +24,19 @@ namespace Bloom::DebugServer::Gdb
*/ */
std::string listeningAddress = "127.0.0.1"; std::string listeningAddress = "127.0.0.1";
/**
* GDB tends to remove all breakpoints when target execution stops, and then installs them again, just before
* resuming target execution. This can result in excessive wearing of the target's program memory, as well as
* a negative impact on performance.
*
* When breakpoint caching is enabled, Bloom's GDB server will perform internal bookkeeping of the breakpoints
* installed and removed via GDB. Then, just before resuming target execution, it will only apply the necessary
* changes to the target, avoiding the excessive wear and IO.
*
* This param is optional, and is enabled by default.
*/
bool breakpointCachingEnabled = true;
explicit GdbDebugServerConfig(const DebugServerConfig& debugServerConfig); explicit GdbDebugServerConfig(const DebugServerConfig& debugServerConfig);
}; };
} }

View File

@@ -154,7 +154,8 @@ namespace Bloom::DebugServer::Gdb
this->activeDebugSession.emplace( this->activeDebugSession.emplace(
std::move(connection), std::move(connection),
this->getSupportedFeatures(), this->getSupportedFeatures(),
this->getGdbTargetDescriptor() this->getGdbTargetDescriptor(),
this->debugServerConfig
); );
/* /*
@@ -183,6 +184,10 @@ namespace Bloom::DebugServer::Gdb
const auto commandPacket = this->waitForCommandPacket(); const auto commandPacket = this->waitForCommandPacket();
if (commandPacket) { if (commandPacket) {
if (this->debugServerConfig.breakpointCachingEnabled && commandPacket->requiresBreakpointFlush()) {
this->flushBreakpointRemovals();
}
commandPacket->handle(this->activeDebugSession.value(), this->targetControllerService); commandPacket->handle(this->activeDebugSession.value(), this->targetControllerService);
} }
@@ -367,4 +372,28 @@ namespace Bloom::DebugServer::Gdb
return; return;
} }
} }
void GdbRspDebugServer::flushBreakpointRemovals() {
Logger::debug(
"Removing " + std::to_string(this->activeDebugSession->breakpointAddressesPendingRemoval.size())
+ " breakpoint(s)"
);
for (const auto& breakpointAddress : this->activeDebugSession->breakpointAddressesPendingRemoval) {
try {
Logger::debug("Removing breakpoint at address " + std::to_string(breakpointAddress));
this->targetControllerService.removeBreakpoint(Targets::TargetBreakpoint(breakpointAddress));
this->activeDebugSession->breakpointAddresses.erase(breakpointAddress);
} catch (const Exception& exception) {
Logger::error(
"Failed to remove breakpoint at address " + std::to_string(breakpointAddress) + " from target - "
+ exception.getMessage()
);
}
}
this->activeDebugSession->breakpointAddressesPendingRemoval.clear();
}
} }

View File

@@ -197,5 +197,10 @@ namespace Bloom::DebugServer::Gdb
* a "stop reply" packet to the client once the target execution stops. * a "stop reply" packet to the client once the target execution stops.
*/ */
void onTargetExecutionStopped(const Events::TargetExecutionStopped&); void onTargetExecutionStopped(const Events::TargetExecutionStopped&);
/**
* Actions any pending breakpoint removals.
*/
void flushBreakpointRemovals();
}; };
} }