WCH RISC-V software breakpoints, and a few other bits of refactoring/tidying

This commit is contained in:
Nav
2024-12-05 23:09:01 +00:00
parent 966244a01a
commit 33ed399337
55 changed files with 1530 additions and 686 deletions

View File

@@ -9,6 +9,8 @@
#include "CommandPackets/ReadMemory.hpp"
#include "CommandPackets/WriteMemory.hpp"
#include "CommandPackets/ReadMemoryMap.hpp"
#include "CommandPackets/SetBreakpoint.hpp"
#include "CommandPackets/RemoveBreakpoint.hpp"
#include "CommandPackets/FlashErase.hpp"
#include "CommandPackets/FlashWrite.hpp"
#include "CommandPackets/FlashDone.hpp"
@@ -56,6 +58,8 @@ namespace DebugServer::Gdb::AvrGdb
using CommandPackets::ReadMemory;
using CommandPackets::WriteMemory;
using CommandPackets::ReadMemoryMap;
using CommandPackets::SetBreakpoint;
using CommandPackets::RemoveBreakpoint;
using CommandPackets::FlashErase;
using CommandPackets::FlashWrite;
using CommandPackets::FlashDone;
@@ -87,6 +91,14 @@ namespace DebugServer::Gdb::AvrGdb
return std::make_unique<WriteMemory>(rawPacket, this->gdbTargetDescriptor);
}
if (rawPacket[1] == 'Z') {
return std::make_unique<SetBreakpoint>(rawPacket);
}
if (rawPacket[1] == 'z') {
return std::make_unique<RemoveBreakpoint>(rawPacket);
}
if (rawPacket.size() > 1) {
const auto rawPacketString = std::string{rawPacket.begin() + 1, rawPacket.end()};
@@ -161,7 +173,12 @@ namespace DebugServer::Gdb::AvrGdb
*
* We need to figure out why, and determine whether the stop should be reported to GDB.
*/
if (this->debugSession->externalBreakpointsByAddress.contains(programAddress)) {
if (
this->debugSession->externalBreakpointRegistry.contains(
activeRangeSteppingSession->addressSpaceDescriptor.id,
programAddress
)
) {
/*
* The target stopped due to an external breakpoint, set by GDB.
*

View File

@@ -0,0 +1,76 @@
#include "RemoveBreakpoint.hpp"
#include <string>
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/EmptyResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetBreakpoint.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace DebugServer::Gdb::AvrGdb::CommandPackets
{
using Services::TargetControllerService;
using ResponsePackets::OkResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using ResponsePackets::EmptyResponsePacket;
using ::Exceptions::Exception;
RemoveBreakpoint::RemoveBreakpoint(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{
const auto packetString = std::string{this->data.begin(), this->data.end()};
if (packetString.size() < 6) {
throw Exception{"Unexpected RemoveBreakpoint packet size"};
}
this->type = (packetString[1] == '0')
? BreakpointType::SOFTWARE_BREAKPOINT
: (packetString[1] == '1') ? BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
const auto firstCommaPos = packetString.find_first_of(',');
const auto secondCommaPos = packetString.find_first_of(',', firstCommaPos + 1);
if (firstCommaPos == std::string::npos || secondCommaPos == std::string::npos) {
throw Exception{"Invalid RemoveBreakpoint packet"};
}
const auto addressString = packetString.substr(firstCommaPos + 1, secondCommaPos - firstCommaPos - 1);
const auto sizeString = packetString.substr(
secondCommaPos + 1,
packetString.find_first_of(';', secondCommaPos) - secondCommaPos - 1
);
this->address = Services::StringService::toUint32(addressString, 16);
this->size = Services::StringService::toUint32(sizeString, 10);
}
void RemoveBreakpoint::handle(
DebugSession& debugSession,
const AvrGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor&,
TargetControllerService& targetControllerService
) {
Logger::info("Handling RemoveBreakpoint packet");
try {
debugSession.removeExternalBreakpoint(
gdbTargetDescriptor.programAddressSpaceDescriptor,
this->address,
targetControllerService
);
debugSession.connection.writePacket(OkResponsePacket{});
} catch (const Exception& exception) {
Logger::error("Failed to remove breakpoint on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include <cstdint>
#include "AvrGdbCommandPacketInterface.hpp"
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/BreakpointType.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace DebugServer::Gdb::AvrGdb::CommandPackets
{
/**
* The RemoveBreakpoint class implements the structure for "Z" command packets. Upon receiving this command, the
* server is expected to set a breakpoint at the specified address.
*/
class RemoveBreakpoint
: public CommandPackets::AvrGdbCommandPacketInterface
, private Gdb::CommandPackets::CommandPacket
{
public:
/**
* The requested breakpoint type.
*
* We don't actually honor this. GDB assumes flash memory cannot be written to outside of a programming
* session, so it refuses to even attempt to insert software breakpoints in flash memory. It always requests
* hardware breakpoints. This is why ignore the requested breakpoint type and just insert whatever type we can.
*/
BreakpointType type = BreakpointType::UNKNOWN;
Targets::TargetMemoryAddress address = 0;
Targets::TargetMemorySize size = 0;
explicit RemoveBreakpoint(const RawPacket& rawPacket);
void handle(
DebugSession& debugSession,
const AvrGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -1,6 +1,6 @@
#include "SetBreakpoint.hpp"
#include <QtCore/QString>
#include <string>
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/EmptyResponsePacket.hpp"
@@ -8,15 +8,15 @@
#include "src/Targets/TargetBreakpoint.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace DebugServer::Gdb::CommandPackets
namespace DebugServer::Gdb::AvrGdb::CommandPackets
{
using Services::TargetControllerService;
using Targets::TargetBreakpoint;
using ResponsePackets::OkResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using ResponsePackets::EmptyResponsePacket;
@@ -26,36 +26,36 @@ namespace DebugServer::Gdb::CommandPackets
SetBreakpoint::SetBreakpoint(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{
if (this->data.size() < 6) {
const auto packetString = std::string{this->data.begin(), this->data.end()};
if (packetString.size() < 6) {
throw Exception{"Unexpected SetBreakpoint packet size"};
}
// Z0 = SW breakpoint, Z1 = HW breakpoint
this->type = (this->data[1] == '0')
this->type = (packetString[1] == '0')
? BreakpointType::SOFTWARE_BREAKPOINT
: (this->data[1] == '1') ? BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
: (packetString[1] == '1') ? BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
auto packetData = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 2),
static_cast<int>(this->data.size() - 2)
const auto firstCommaPos = packetString.find_first_of(',');
const auto secondCommaPos = packetString.find_first_of(',', firstCommaPos + 1);
if (firstCommaPos == std::string::npos || secondCommaPos == std::string::npos) {
throw Exception{"Invalid SetBreakpoint packet"};
}
const auto addressString = packetString.substr(firstCommaPos + 1, secondCommaPos - firstCommaPos - 1);
const auto sizeString = packetString.substr(
secondCommaPos + 1,
packetString.find_first_of(';', secondCommaPos) - secondCommaPos - 1
);
auto packetSegments = packetData.split(",");
if (packetSegments.size() < 3) {
throw Exception{"Unexpected number of packet segments in SetBreakpoint packet"};
}
bool conversionStatus = true;
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception{"Failed to convert address hex value from SetBreakpoint packet."};
}
this->address = Services::StringService::toUint32(addressString, 16);
this->size = Services::StringService::toUint32(sizeString, 10);
}
void SetBreakpoint::handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const AvrGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor&,
TargetControllerService& targetControllerService
) {
@@ -71,7 +71,11 @@ namespace DebugServer::Gdb::CommandPackets
return;
}
debugSession.setExternalBreakpoint(this->address, targetControllerService);
debugSession.setExternalBreakpoint(
gdbTargetDescriptor.programAddressSpaceDescriptor,
gdbTargetDescriptor.programMemorySegmentDescriptor,
this->address, this->size, targetControllerService
);
debugSession.connection.writePacket(OkResponsePacket{});
} catch (const Exception& exception) {

View File

@@ -0,0 +1,31 @@
#pragma once
#include <cstdint>
#include "AvrGdbCommandPacketInterface.hpp"
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/BreakpointType.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace DebugServer::Gdb::AvrGdb::CommandPackets
{
class SetBreakpoint
: public CommandPackets::AvrGdbCommandPacketInterface
, private Gdb::CommandPackets::CommandPacket
{
public:
BreakpointType type = BreakpointType::UNKNOWN;
Targets::TargetMemoryAddress address = 0;
Targets::TargetMemorySize size = 0;
explicit SetBreakpoint(const RawPacket& rawPacket);
void handle(
DebugSession& debugSession,
const AvrGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -84,7 +84,12 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
return;
}
auto rangeSteppingSession = RangeSteppingSession{stepAddressRange, {}};
auto rangeSteppingSession = RangeSteppingSession{
gdbTargetDescriptor.programAddressSpaceDescriptor,
gdbTargetDescriptor.programMemorySegmentDescriptor,
stepAddressRange,
{}
};
const auto instructionsByAddress = Decoder::decode(
stepAddressRange.startAddress,

View File

@@ -1,71 +0,0 @@
#include "RemoveBreakpoint.hpp"
#include <QtCore/QString>
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetBreakpoint.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace DebugServer::Gdb::CommandPackets
{
using Services::TargetControllerService;
using Targets::TargetBreakpoint;
using ResponsePackets::OkResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using ::Exceptions::Exception;
RemoveBreakpoint::RemoveBreakpoint(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{
if (this->data.size() < 6) {
throw Exception{"Unexpected RemoveBreakpoint packet size"};
}
// z0 = SW breakpoint, z1 = HW breakpoint
this->type = (this->data[1] == '0')
? BreakpointType::SOFTWARE_BREAKPOINT
: (this->data[1] == '1') ? BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
const auto packetData = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 2),
static_cast<int>(this->data.size() - 2)
);
auto packetSegments = packetData.split(",");
if (packetSegments.size() < 3) {
throw Exception{"Unexpected number of packet segments in RemoveBreakpoint packet"};
}
bool conversionStatus = true;
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception{"Failed to convert address hex value from RemoveBreakpoint packet."};
}
}
void RemoveBreakpoint::handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor&,
TargetControllerService& targetControllerService
) {
Logger::info("Handling RemoveBreakpoint packet");
try {
debugSession.removeExternalBreakpoint(this->address, targetControllerService);
debugSession.connection.writePacket(OkResponsePacket{});
} catch (const Exception& exception) {
Logger::error("Failed to remove breakpoint on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -1,40 +0,0 @@
#pragma once
#include <cstdint>
#include <string>
#include <set>
#include "CommandPacket.hpp"
#include "src/DebugServer/Gdb/BreakpointType.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace DebugServer::Gdb::CommandPackets
{
/**
* The RemoveBreakpoint class implements the structure for "z" command packets. Upon receiving this command, the
* server is expected to remove a breakpoint at the specified address.
*/
class RemoveBreakpoint: public CommandPacket
{
public:
/**
* Breakpoint type (Software or Hardware)
*/
BreakpointType type = BreakpointType::UNKNOWN;
/**
* Address at which the breakpoint should be located.
*/
Targets::TargetMemoryAddress address = 0;
explicit RemoveBreakpoint(const RawPacket& rawPacket);
void handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -1,40 +0,0 @@
#pragma once
#include <cstdint>
#include <string>
#include <set>
#include "CommandPacket.hpp"
#include "src/DebugServer/Gdb/BreakpointType.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace DebugServer::Gdb::CommandPackets
{
/**
* The SetBreakpoint class implements the structure for "Z" command packets. Upon receiving this command, the
* server is expected to set a breakpoint at the specified address.
*/
class SetBreakpoint: public CommandPacket
{
public:
/**
* Breakpoint type (Software or Hardware)
*/
BreakpointType type = BreakpointType::UNKNOWN;
/**
* Address at which the breakpoint should be located.
*/
Targets::TargetMemoryAddress address = 0;
explicit SetBreakpoint(const RawPacket& rawPacket);
void handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -26,78 +26,93 @@ namespace DebugServer::Gdb
}
void DebugSession::setInternalBreakpoint(
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
Targets::TargetMemoryAddress address,
Targets::TargetMemorySize size,
Services::TargetControllerService& targetControllerService
) {
if (this->internalBreakpointsByAddress.contains(address)) {
if (this->internalBreakpointRegistry.contains(addressSpaceDescriptor.id, address)) {
return;
}
const auto externalBreakpointIt = this->externalBreakpointsByAddress.find(address);
if (externalBreakpointIt != this->externalBreakpointsByAddress.end()) {
const auto externalBreakpoint = this->externalBreakpointRegistry.find(addressSpaceDescriptor.id, address);
if (externalBreakpoint.has_value()) {
// We already have an external breakpoint at this address
this->internalBreakpointsByAddress.emplace(address, externalBreakpointIt->second);
this->internalBreakpointRegistry.insert(externalBreakpoint->get());
return;
}
this->internalBreakpointsByAddress.emplace(
address,
targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE)
this->internalBreakpointRegistry.insert(
targetControllerService.setProgramBreakpointAnyType(
addressSpaceDescriptor,
memorySegmentDescriptor,
address,
size
)
);
}
void DebugSession::removeInternalBreakpoint(
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
Targets::TargetMemoryAddress address,
Services::TargetControllerService& targetControllerService
) {
const auto breakpointIt = this->internalBreakpointsByAddress.find(address);
if (breakpointIt == this->internalBreakpointsByAddress.end()) {
const auto breakpoint = this->internalBreakpointRegistry.find(addressSpaceDescriptor.id, address);
if (!breakpoint.has_value()) {
return;
}
if (!this->externalBreakpointsByAddress.contains(address)) {
targetControllerService.removeBreakpoint(breakpointIt->second);
if (!this->externalBreakpointRegistry.contains(addressSpaceDescriptor.id, address)) {
targetControllerService.removeProgramBreakpoint(breakpoint->get());
}
this->internalBreakpointsByAddress.erase(breakpointIt);
this->internalBreakpointRegistry.remove(addressSpaceDescriptor.id, address);
}
void DebugSession::setExternalBreakpoint(
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
Targets::TargetMemoryAddress address,
Targets::TargetMemorySize size,
Services::TargetControllerService& targetControllerService
) {
if (this->externalBreakpointsByAddress.contains(address)) {
if (this->externalBreakpointRegistry.contains(addressSpaceDescriptor.id, address)) {
return;
}
const auto internalBreakpointIt = this->internalBreakpointsByAddress.find(address);
if (internalBreakpointIt != this->internalBreakpointsByAddress.end()) {
const auto internalBreakpoint = this->internalBreakpointRegistry.find(addressSpaceDescriptor.id, address);
if (internalBreakpoint.has_value()) {
// We already have an internal breakpoint at this address
this->externalBreakpointsByAddress.emplace(address, internalBreakpointIt->second);
this->externalBreakpointRegistry.insert(internalBreakpoint->get());
return;
}
this->externalBreakpointsByAddress.emplace(
address,
targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE)
this->externalBreakpointRegistry.insert(
targetControllerService.setProgramBreakpointAnyType(
addressSpaceDescriptor,
memorySegmentDescriptor,
address,
size
)
);
}
void DebugSession::removeExternalBreakpoint(
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
Targets::TargetMemoryAddress address,
Services::TargetControllerService& targetControllerService
) {
const auto breakpointIt = this->externalBreakpointsByAddress.find(address);
if (breakpointIt == this->externalBreakpointsByAddress.end()) {
const auto breakpoint = this->externalBreakpointRegistry.find(addressSpaceDescriptor.id, address);
if (!breakpoint.has_value()) {
return;
}
if (!this->internalBreakpointsByAddress.contains(address)) {
targetControllerService.removeBreakpoint(breakpointIt->second);
if (!this->internalBreakpointRegistry.contains(addressSpaceDescriptor.id, address)) {
targetControllerService.removeProgramBreakpoint(breakpoint->get());
}
this->externalBreakpointsByAddress.erase(breakpointIt);
this->externalBreakpointRegistry.remove(addressSpaceDescriptor.id, address);
}
void DebugSession::startRangeSteppingSession(
@@ -105,10 +120,22 @@ namespace DebugServer::Gdb
Services::TargetControllerService& targetControllerService
) {
for (const auto& interceptAddress : session.interceptedAddresses) {
this->setInternalBreakpoint(interceptAddress, targetControllerService);
/*
* Have hard-coded the breakpoint size here, as range stepping is only supported on AVR targets.
*
* TODO: Review this after v2.0.0. Maybe move this range-stepping code out to an AVR-specific DebugSession
* struct. Or, refactor the range stepping session object to accommodate breakpoint sizes.
*/
this->setInternalBreakpoint(
session.addressSpaceDescriptor,
session.memorySegmentDescriptor,
interceptAddress,
2,
targetControllerService
);
}
this->activeRangeSteppingSession = std::move(session);
this->activeRangeSteppingSession.emplace(std::move(session));
}
void DebugSession::terminateRangeSteppingSession(Services::TargetControllerService& targetControllerService) {
@@ -118,7 +145,11 @@ namespace DebugServer::Gdb
// Clear all intercepting breakpoints
for (const auto& interceptAddress : this->activeRangeSteppingSession->interceptedAddresses) {
this->removeInternalBreakpoint(interceptAddress, targetControllerService);
this->removeInternalBreakpoint(
this->activeRangeSteppingSession->addressSpaceDescriptor,
interceptAddress,
targetControllerService
);
}
this->activeRangeSteppingSession.reset();

View File

@@ -12,7 +12,10 @@
#include "RangeSteppingSession.hpp"
#include "src/Targets/TargetMemory.hpp"
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
#include "src/Targets/TargetBreakpoint.hpp"
#include "src/Targets/ProgramBreakpointRegistry.hpp"
#include "src/Services/TargetControllerService.hpp"
@@ -53,8 +56,8 @@ namespace DebugServer::Gdb
*
* We track internal and external breakpoints separately.
*/
std::map<Targets::TargetMemoryAddress, Targets::TargetBreakpoint> internalBreakpointsByAddress;
std::map<Targets::TargetMemoryAddress, Targets::TargetBreakpoint> externalBreakpointsByAddress;
Targets::ProgramBreakpointRegistry internalBreakpointRegistry;
Targets::ProgramBreakpointRegistry externalBreakpointRegistry;
/**
* When the GDB client is waiting for the target to halt, this is set to true so we know when to notify the
@@ -102,21 +105,29 @@ namespace DebugServer::Gdb
virtual ~DebugSession();
virtual void setInternalBreakpoint(
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
Targets::TargetMemoryAddress address,
Targets::TargetMemorySize size,
Services::TargetControllerService& targetControllerService
);
virtual void removeInternalBreakpoint(
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
Targets::TargetMemoryAddress address,
Services::TargetControllerService& targetControllerService
);
virtual void setExternalBreakpoint(
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
Targets::TargetMemoryAddress address,
Targets::TargetMemorySize size,
Services::TargetControllerService& targetControllerService
);
virtual void removeExternalBreakpoint(
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
Targets::TargetMemoryAddress address,
Services::TargetControllerService& targetControllerService
);

View File

@@ -29,8 +29,6 @@
#include "CommandPackets/InterruptExecution.hpp"
#include "CommandPackets/ContinueExecution.hpp"
#include "CommandPackets/StepExecution.hpp"
#include "CommandPackets/SetBreakpoint.hpp"
#include "CommandPackets/RemoveBreakpoint.hpp"
#include "CommandPackets/Monitor.hpp"
#include "CommandPackets/ResetTarget.hpp"
#include "CommandPackets/HelpMonitorInfo.hpp"
@@ -332,6 +330,26 @@ namespace DebugServer::Gdb
std::optional<DebugSessionType> debugSession;
void endDebugSession() {
if (!debugSession.has_value()) {
return;
}
/*
* When GDB is configured to leave breakpoints in place, it will sometimes not even bother to remove them
* at the end of the debug session.
*/
for (const auto& [addressSpaceId, breakpointsByAddress] : this->debugSession->internalBreakpointRegistry) {
for (const auto& [address, breakpoint] : breakpointsByAddress) {
this->targetControllerService.removeProgramBreakpoint(breakpoint);
}
}
for (const auto& [addressSpaceId, breakpointsByAddress] : this->debugSession->externalBreakpointRegistry) {
for (const auto& [address, breakpoint] : breakpointsByAddress) {
this->targetControllerService.removeProgramBreakpoint(breakpoint);
}
}
this->debugSession.reset();
}
@@ -421,14 +439,6 @@ namespace DebugServer::Gdb
return std::make_unique<CommandPackets::StepExecution>(rawPacket);
}
if (rawPacket[1] == 'Z') {
return std::make_unique<CommandPackets::SetBreakpoint>(rawPacket);
}
if (rawPacket[1] == 'z') {
return std::make_unique<CommandPackets::RemoveBreakpoint>(rawPacket);
}
if (rawPacket[1] == 'D') {
return std::make_unique<CommandPackets::Detach>(rawPacket);
}

View File

@@ -3,6 +3,8 @@
#include <set>
#include "src/Targets/TargetMemory.hpp"
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
namespace DebugServer::Gdb
{
@@ -15,6 +17,12 @@ namespace DebugServer::Gdb
*/
struct RangeSteppingSession
{
/**
* The address space and memory segment of the program memory which we are stepping over.
*/
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor;
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor;
/**
* The (byte) address range we're stepping over.
*
@@ -36,11 +44,23 @@ namespace DebugServer::Gdb
bool singleStepping = false;
RangeSteppingSession(
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
const Targets::TargetMemoryAddressRange& range,
const std::set<Targets::TargetMemoryAddress>& interceptedAddresses
)
: range(range)
: addressSpaceDescriptor(addressSpaceDescriptor)
, memorySegmentDescriptor(memorySegmentDescriptor)
, range(range)
, interceptedAddresses(interceptedAddresses)
{};
RangeSteppingSession(RangeSteppingSession&& other) noexcept
: addressSpaceDescriptor(other.addressSpaceDescriptor)
, memorySegmentDescriptor(other.memorySegmentDescriptor)
, range(other.range)
, interceptedAddresses(std::move(other.interceptedAddresses))
, singleStepping(other.singleStepping)
{}
};
}

View File

@@ -0,0 +1,76 @@
#include "RemoveBreakpoint.hpp"
#include <string>
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/EmptyResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetBreakpoint.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
using Services::TargetControllerService;
using ResponsePackets::OkResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using ResponsePackets::EmptyResponsePacket;
using ::Exceptions::Exception;
RemoveBreakpoint::RemoveBreakpoint(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{
const auto packetString = std::string{this->data.begin(), this->data.end()};
if (packetString.size() < 6) {
throw Exception{"Unexpected RemoveBreakpoint packet size"};
}
this->type = (packetString[1] == '0')
? BreakpointType::SOFTWARE_BREAKPOINT
: (packetString[1] == '1') ? BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
const auto firstCommaPos = packetString.find_first_of(',');
const auto secondCommaPos = packetString.find_first_of(',', firstCommaPos + 1);
if (firstCommaPos == std::string::npos || secondCommaPos == std::string::npos) {
throw Exception{"Invalid RemoveBreakpoint packet"};
}
const auto addressString = packetString.substr(firstCommaPos + 1, secondCommaPos - firstCommaPos - 1);
const auto sizeString = packetString.substr(
secondCommaPos + 1,
packetString.find_first_of(';', secondCommaPos) - secondCommaPos - 1
);
this->address = Services::StringService::toUint32(addressString, 16);
this->size = Services::StringService::toUint32(sizeString, 10);
}
void RemoveBreakpoint::handle(
DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor&,
TargetControllerService& targetControllerService
) {
Logger::info("Handling RemoveBreakpoint packet");
try {
debugSession.removeExternalBreakpoint(
gdbTargetDescriptor.systemAddressSpaceDescriptor,
this->address,
targetControllerService
);
debugSession.connection.writePacket(OkResponsePacket{});
} catch (const Exception& exception) {
Logger::error("Failed to remove breakpoint on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include <cstdint>
#include "RiscVGdbCommandPacketInterface.hpp"
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/BreakpointType.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
class RemoveBreakpoint
: public CommandPackets::RiscVGdbCommandPacketInterface
, private Gdb::CommandPackets::CommandPacket
{
public:
BreakpointType type = BreakpointType::UNKNOWN;
Targets::TargetMemoryAddress address = 0;
Targets::TargetMemorySize size = 0;
explicit RemoveBreakpoint(const RawPacket& rawPacket);
void handle(
DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -0,0 +1,103 @@
#include "SetBreakpoint.hpp"
#include <string>
#include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/EmptyResponsePacket.hpp"
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
#include "src/Targets/TargetBreakpoint.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/Exception.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
using Services::TargetControllerService;
using ResponsePackets::OkResponsePacket;
using ResponsePackets::ErrorResponsePacket;
using ResponsePackets::EmptyResponsePacket;
using ::Exceptions::Exception;
SetBreakpoint::SetBreakpoint(const RawPacket& rawPacket)
: CommandPacket(rawPacket)
{
const auto packetString = std::string{this->data.begin(), this->data.end()};
if (packetString.size() < 6) {
throw Exception{"Unexpected SetBreakpoint packet size"};
}
// Z0 = SW breakpoint, Z1 = HW breakpoint
this->type = (packetString[1] == '0')
? BreakpointType::SOFTWARE_BREAKPOINT
: (packetString[1] == '1') ? BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
const auto firstCommaPos = packetString.find_first_of(',');
const auto secondCommaPos = packetString.find_first_of(',', firstCommaPos + 1);
if (firstCommaPos == std::string::npos || secondCommaPos == std::string::npos) {
throw Exception{"Invalid SetBreakpoint packet"};
}
const auto addressString = packetString.substr(firstCommaPos + 1, secondCommaPos - firstCommaPos - 1);
const auto sizeString = packetString.substr(
secondCommaPos + 1,
packetString.find_first_of(';', secondCommaPos) - secondCommaPos - 1
);
this->address = Services::StringService::toUint32(addressString, 16);
this->size = Services::StringService::toUint32(sizeString, 10);
}
void SetBreakpoint::handle(
DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor&,
TargetControllerService& targetControllerService
) {
Logger::info("Handling SetBreakpoint packet");
try {
if (this->type == BreakpointType::UNKNOWN) {
Logger::debug(
"Rejecting breakpoint at address " + std::to_string(this->address)
+ " - unsupported breakpoint type"
);
debugSession.connection.writePacket(EmptyResponsePacket{});
return;
}
if (this->type == BreakpointType::SOFTWARE_BREAKPOINT && this->size != 2 && this->size != 4) {
throw Exception{"Invalid breakpoint size - expected 2 or 4, got " + std::to_string(this->size)};
}
const auto memorySegmentDescriptors = gdbTargetDescriptor.systemAddressSpaceDescriptor.getIntersectingMemorySegmentDescriptors(
Targets::TargetMemoryAddressRange{this->address, this->address + this->size - 1}
);
if (memorySegmentDescriptors.size() != 1) {
throw Exception{
memorySegmentDescriptors.empty()
? "Invalid breakpoint address - no containing memory segments found for the given address"
: "Invalid breakpoint address - the address range intersects with multiple memory segments"
};
}
debugSession.setExternalBreakpoint(
gdbTargetDescriptor.systemAddressSpaceDescriptor,
*(memorySegmentDescriptors.front()),
this->address,
this->size, targetControllerService
);
debugSession.connection.writePacket(OkResponsePacket{});
} catch (const Exception& exception) {
Logger::error("Failed to set breakpoint on target - " + exception.getMessage());
debugSession.connection.writePacket(ErrorResponsePacket{});
}
}
}

View File

@@ -0,0 +1,38 @@
#pragma once
#include <cstdint>
#include "RiscVGdbCommandPacketInterface.hpp"
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
#include "src/DebugServer/Gdb/BreakpointType.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace DebugServer::Gdb::RiscVGdb::CommandPackets
{
class SetBreakpoint
: public CommandPackets::RiscVGdbCommandPacketInterface
, private Gdb::CommandPackets::CommandPacket
{
public:
/**
* The requested breakpoint type.
*
* We don't actually honor this. GDB assumes flash memory cannot be written to outside of a programming
* session, so it refuses to even attempt to insert software breakpoints in flash memory. It always requests
* hardware breakpoints. This is why ignore the requested breakpoint type and just insert whatever type we can.
*/
BreakpointType type = BreakpointType::UNKNOWN;
Targets::TargetMemoryAddress address = 0;
Targets::TargetMemorySize size = 0;
explicit SetBreakpoint(const RawPacket& rawPacket);
void handle(
DebugSession& debugSession,
const RiscVGdbTargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};
}

View File

@@ -9,6 +9,8 @@
#include "CommandPackets/ReadMemory.hpp"
#include "CommandPackets/WriteMemory.hpp"
#include "CommandPackets/ReadMemoryMap.hpp"
#include "CommandPackets/SetBreakpoint.hpp"
#include "CommandPackets/RemoveBreakpoint.hpp"
#include "CommandPackets/FlashErase.hpp"
#include "CommandPackets/FlashWrite.hpp"
#include "CommandPackets/FlashDone.hpp"
@@ -53,6 +55,8 @@ namespace DebugServer::Gdb::RiscVGdb
using CommandPackets::ReadMemory;
using CommandPackets::WriteMemory;
using CommandPackets::ReadMemoryMap;
using CommandPackets::SetBreakpoint;
using CommandPackets::RemoveBreakpoint;
using CommandPackets::FlashErase;
using CommandPackets::FlashWrite;
using CommandPackets::FlashDone;
@@ -82,6 +86,14 @@ namespace DebugServer::Gdb::RiscVGdb
return std::make_unique<WriteMemory>(rawPacket, this->gdbTargetDescriptor);
}
if (rawPacket[1] == 'Z') {
return std::make_unique<SetBreakpoint>(rawPacket);
}
if (rawPacket[1] == 'z') {
return std::make_unique<RemoveBreakpoint>(rawPacket);
}
if (rawPacket.size() > 1) {
const auto rawPacketString = std::string{rawPacket.begin() + 1, rawPacket.end()};