- Implemented support for range stepping with GDB (vCont... packets)
- Refactored some bits of the generic GDB server class, along with the AVR-specific implementation
This commit is contained in:
@@ -29,6 +29,7 @@ target_sources(
|
|||||||
# AVR GDB RSP Server
|
# AVR GDB RSP Server
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/AvrGdbRsp.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/AvrGdbRsp.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/TargetDescriptor.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/TargetDescriptor.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/DebugSession.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/ReadRegisters.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/ReadRegisters.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/ReadRegister.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/ReadRegister.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/WriteRegister.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/WriteRegister.cpp
|
||||||
@@ -38,6 +39,10 @@ target_sources(
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/FlashErase.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/FlashErase.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/FlashWrite.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/FlashWrite.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/FlashDone.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/FlashDone.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/VContSupportedActionsQuery.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/VContContinueExecution.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/VContStepExecution.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/VContRangeStep.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (NOT EXCLUDE_INSIGHT)
|
if (NOT EXCLUDE_INSIGHT)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "AvrGdbRsp.hpp"
|
#include "AvrGdbRsp.hpp"
|
||||||
|
|
||||||
|
#include "src/Services/StringService.hpp"
|
||||||
|
|
||||||
// Command packets
|
// Command packets
|
||||||
#include "CommandPackets/ReadRegister.hpp"
|
#include "CommandPackets/ReadRegister.hpp"
|
||||||
#include "CommandPackets/ReadRegisters.hpp"
|
#include "CommandPackets/ReadRegisters.hpp"
|
||||||
@@ -10,6 +12,10 @@
|
|||||||
#include "CommandPackets/FlashErase.hpp"
|
#include "CommandPackets/FlashErase.hpp"
|
||||||
#include "CommandPackets/FlashWrite.hpp"
|
#include "CommandPackets/FlashWrite.hpp"
|
||||||
#include "CommandPackets/FlashDone.hpp"
|
#include "CommandPackets/FlashDone.hpp"
|
||||||
|
#include "CommandPackets/VContSupportedActionsQuery.hpp"
|
||||||
|
#include "CommandPackets/VContContinueExecution.hpp"
|
||||||
|
#include "CommandPackets/VContStepExecution.hpp"
|
||||||
|
#include "CommandPackets/VContRangeStep.hpp"
|
||||||
|
|
||||||
namespace DebugServer::Gdb::AvrGdb
|
namespace DebugServer::Gdb::AvrGdb
|
||||||
{
|
{
|
||||||
@@ -24,14 +30,30 @@ namespace DebugServer::Gdb::AvrGdb
|
|||||||
EventFdNotifier& eventNotifier
|
EventFdNotifier& eventNotifier
|
||||||
)
|
)
|
||||||
: GdbRspDebugServer(debugServerConfig, eventListener, eventNotifier)
|
: GdbRspDebugServer(debugServerConfig, eventListener, eventNotifier)
|
||||||
|
, gdbTargetDescriptor(TargetDescriptor(this->targetControllerService.getTargetDescriptor()))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void AvrGdbRsp::init() {
|
DebugSession* AvrGdbRsp::startDebugSession(Connection&& connection) {
|
||||||
DebugServer::Gdb::GdbRspDebugServer::init();
|
this->activeDebugSession.emplace(
|
||||||
|
std::move(connection),
|
||||||
this->gdbTargetDescriptor = TargetDescriptor(
|
this->getSupportedFeatures(),
|
||||||
this->targetControllerService.getTargetDescriptor()
|
this->gdbTargetDescriptor,
|
||||||
|
this->debugServerConfig
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return &*(this->activeDebugSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvrGdbRsp::endDebugSession() {
|
||||||
|
this->activeDebugSession.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Gdb::TargetDescriptor& AvrGdbRsp::getGdbTargetDescriptor() {
|
||||||
|
return this->gdbTargetDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugSession* AvrGdbRsp::getActiveDebugSession() {
|
||||||
|
return this->activeDebugSession.has_value() ? &*(this->activeDebugSession) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Gdb::CommandPackets::CommandPacket> AvrGdbRsp::resolveCommandPacket(
|
std::unique_ptr<Gdb::CommandPackets::CommandPacket> AvrGdbRsp::resolveCommandPacket(
|
||||||
@@ -46,6 +68,10 @@ namespace DebugServer::Gdb::AvrGdb
|
|||||||
using AvrGdb::CommandPackets::FlashErase;
|
using AvrGdb::CommandPackets::FlashErase;
|
||||||
using AvrGdb::CommandPackets::FlashWrite;
|
using AvrGdb::CommandPackets::FlashWrite;
|
||||||
using AvrGdb::CommandPackets::FlashDone;
|
using AvrGdb::CommandPackets::FlashDone;
|
||||||
|
using AvrGdb::CommandPackets::VContSupportedActionsQuery;
|
||||||
|
using AvrGdb::CommandPackets::VContContinueExecution;
|
||||||
|
using AvrGdb::CommandPackets::VContStepExecution;
|
||||||
|
using AvrGdb::CommandPackets::VContRangeStep;
|
||||||
|
|
||||||
if (rawPacket.size() >= 2) {
|
if (rawPacket.size() >= 2) {
|
||||||
if (rawPacket[1] == 'p') {
|
if (rawPacket[1] == 'p') {
|
||||||
@@ -61,11 +87,11 @@ namespace DebugServer::Gdb::AvrGdb
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rawPacket[1] == 'm') {
|
if (rawPacket[1] == 'm') {
|
||||||
return std::make_unique<ReadMemory>(rawPacket, this->gdbTargetDescriptor.value());
|
return std::make_unique<ReadMemory>(rawPacket, this->gdbTargetDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawPacket[1] == 'M') {
|
if (rawPacket[1] == 'M') {
|
||||||
return std::make_unique<WriteMemory>(rawPacket, this->gdbTargetDescriptor.value());
|
return std::make_unique<WriteMemory>(rawPacket, this->gdbTargetDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto rawPacketString = std::string(rawPacket.begin() + 1, rawPacket.end());
|
const auto rawPacketString = std::string(rawPacket.begin() + 1, rawPacket.end());
|
||||||
@@ -85,19 +111,109 @@ namespace DebugServer::Gdb::AvrGdb
|
|||||||
if (rawPacketString.find("vFlashDone") == 0) {
|
if (rawPacketString.find("vFlashDone") == 0) {
|
||||||
return std::make_unique<FlashDone>(rawPacket);
|
return std::make_unique<FlashDone>(rawPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rawPacketString.find("vCont?") == 0) {
|
||||||
|
return std::make_unique<VContSupportedActionsQuery>(rawPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawPacketString.find("vCont;c") == 0 || rawPacketString.find("vCont;C") == 0) {
|
||||||
|
return std::make_unique<VContContinueExecution>(rawPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawPacketString.find("vCont;s") == 0 || rawPacketString.find("vCont;S") == 0) {
|
||||||
|
return std::make_unique<VContStepExecution>(rawPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->debugServerConfig.rangeSteppingEnabled) {
|
||||||
|
if (rawPacketString.find("vCont;r") == 0) {
|
||||||
|
return std::make_unique<VContRangeStep>(rawPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GdbRspDebugServer::resolveCommandPacket(rawPacket);
|
return GdbRspDebugServer::resolveCommandPacket(rawPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<std::pair<Feature, std::optional<std::string>>> AvrGdbRsp::getSupportedFeatures() {
|
std::set<std::pair<Feature, std::optional<std::string>>> AvrGdbRsp::getSupportedFeatures() {
|
||||||
auto supportedFeatures = GdbRspDebugServer::getSupportedFeatures();
|
return {
|
||||||
|
{Feature::SOFTWARE_BREAKPOINTS, std::nullopt},
|
||||||
|
{Feature::MEMORY_MAP_READ, std::nullopt},
|
||||||
|
{Feature::VCONT_ACTIONS_QUERY, std::nullopt},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// The AVR GDB server supports the 'qXfer:memory-map:read' GDB command.
|
void AvrGdbRsp::handleTargetStoppedGdbResponse(Targets::TargetProgramCounter programAddress) {
|
||||||
supportedFeatures.insert({
|
using Services::StringService;
|
||||||
Feature::MEMORY_MAP_READ, std::nullopt
|
|
||||||
});
|
|
||||||
|
|
||||||
return supportedFeatures;
|
Logger::debug("Target stopped at byte address: 0x" + StringService::toHex(programAddress));
|
||||||
|
|
||||||
|
auto& activeRangeSteppingSession = this->activeDebugSession->activeRangeSteppingSession;
|
||||||
|
|
||||||
|
if (
|
||||||
|
activeRangeSteppingSession.has_value()
|
||||||
|
&& programAddress >= activeRangeSteppingSession->range.startAddress
|
||||||
|
&& programAddress < activeRangeSteppingSession->range.endAddress
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
* The target stopped within the stepping range of an active range stepping session.
|
||||||
|
*
|
||||||
|
* We need to figure out why, and determine whether the stop should be reported to GDB.
|
||||||
|
*/
|
||||||
|
if (this->activeDebugSession->externalBreakpointAddresses.contains(programAddress)) {
|
||||||
|
/*
|
||||||
|
* The target stopped due to an external breakpoint, set by GDB.
|
||||||
|
*
|
||||||
|
* We have to end the range stepping session and report this to GDB.
|
||||||
|
*/
|
||||||
|
Logger::debug("Reached external breakpoint within stepping range");
|
||||||
|
|
||||||
|
this->activeDebugSession->terminateRangeSteppingSession(this->targetControllerService);
|
||||||
|
return GdbRspDebugServer::handleTargetStoppedGdbResponse(programAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeRangeSteppingSession->interceptedAddresses.contains(programAddress)) {
|
||||||
|
/*
|
||||||
|
* The target stopped due to an intercepting breakpoint, but we're still within the stepping range,
|
||||||
|
* which can only mean that we weren't sure where this instruction would lead to.
|
||||||
|
*
|
||||||
|
* We must perform a single step and see what happens.
|
||||||
|
*/
|
||||||
|
Logger::debug("Reached intercepting breakpoint within stepping range");
|
||||||
|
Logger::debug("Attempting single step from 0x" + StringService::toHex(programAddress));
|
||||||
|
|
||||||
|
activeRangeSteppingSession->singleStepping = true;
|
||||||
|
this->targetControllerService.stepTargetExecution(std::nullopt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeRangeSteppingSession->singleStepping) {
|
||||||
|
/*
|
||||||
|
* We performed a single step once and we're still within the stepping range, so we're good to
|
||||||
|
* continue the range stepping session.
|
||||||
|
*/
|
||||||
|
Logger::debug("Completed single step from an intercepted address - PC still within stepping range");
|
||||||
|
Logger::debug("Continuing range stepping");
|
||||||
|
|
||||||
|
activeRangeSteppingSession->singleStepping = false;
|
||||||
|
this->targetControllerService.continueTargetExecution(std::nullopt, std::nullopt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we get here, the target stopped for an unknown reason that doesn't seem to have anything to do with
|
||||||
|
* the active range stepping session.
|
||||||
|
*
|
||||||
|
* This could be due to a permanent breakpoint in the target's program code.
|
||||||
|
*
|
||||||
|
* We have to end the range stepping session and report the stop to GDB.
|
||||||
|
*/
|
||||||
|
Logger::debug("Target stopped within stepping range, but for an unknown reason");
|
||||||
|
|
||||||
|
this->activeDebugSession->terminateRangeSteppingSession(this->targetControllerService);
|
||||||
|
return GdbRspDebugServer::handleTargetStoppedGdbResponse(programAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report the stop to GDB
|
||||||
|
return GdbRspDebugServer::handleTargetStoppedGdbResponse(programAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "TargetDescriptor.hpp"
|
#include "TargetDescriptor.hpp"
|
||||||
|
#include "DebugSession.hpp"
|
||||||
|
|
||||||
#include "src/DebugServer/Gdb/GdbRspDebugServer.hpp"
|
#include "src/DebugServer/Gdb/GdbRspDebugServer.hpp"
|
||||||
|
|
||||||
@@ -22,19 +23,37 @@ namespace DebugServer::Gdb::AvrGdb
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void init() override;
|
DebugSession* startDebugSession(Connection&& connection) override;
|
||||||
|
|
||||||
const Gdb::TargetDescriptor& getGdbTargetDescriptor() override {
|
void endDebugSession() override;
|
||||||
return this->gdbTargetDescriptor.value();
|
|
||||||
}
|
const Gdb::TargetDescriptor& getGdbTargetDescriptor() override;
|
||||||
|
|
||||||
|
DebugSession* getActiveDebugSession() override;
|
||||||
|
|
||||||
std::unique_ptr<Gdb::CommandPackets::CommandPacket> resolveCommandPacket(
|
std::unique_ptr<Gdb::CommandPackets::CommandPacket> resolveCommandPacket(
|
||||||
const RawPacket& rawPacket
|
const RawPacket& rawPacket
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
std::set<std::pair<Feature, std::optional<std::string>>> getSupportedFeatures() override;
|
/**
|
||||||
|
* Should return a set of GDB features supported by the AVR GDB server. Each supported feature may come with an
|
||||||
|
* optional value.
|
||||||
|
*
|
||||||
|
* The set of features returned by this function will be stored against the active debug session object.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
std::set<std::pair<Feature, std::optional<std::string>>> getSupportedFeatures();
|
||||||
|
|
||||||
|
void handleTargetStoppedGdbResponse(Targets::TargetProgramCounter programAddress) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::optional<TargetDescriptor> gdbTargetDescriptor;
|
TargetDescriptor gdbTargetDescriptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a connection with a GDB client is established, a new instance of the DebugSession class is created and
|
||||||
|
* held here. A value of std::nullopt means there is no active debug session present.
|
||||||
|
*/
|
||||||
|
std::optional<DebugSession> activeDebugSession;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
: CommandPacket(rawPacket)
|
: CommandPacket(rawPacket)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void FlashDone::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
void FlashDone::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||||
Logger::info("Handling FlashDone packet");
|
Logger::info("Handling FlashDone packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
explicit FlashDone(const RawPacket& rawPacket);
|
explicit FlashDone(const RawPacket& rawPacket);
|
||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlashErase::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
void FlashErase::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||||
Logger::info("Handling FlashErase packet");
|
Logger::info("Handling FlashErase packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
explicit FlashErase(const RawPacket& rawPacket);
|
explicit FlashErase(const RawPacket& rawPacket);
|
||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
this->buffer = Targets::TargetMemoryBuffer(colonIt + 1, this->data.end());
|
this->buffer = Targets::TargetMemoryBuffer(colonIt + 1, this->data.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlashWrite::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
void FlashWrite::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||||
Logger::info("Handling FlashWrite packet");
|
Logger::info("Handling FlashWrite packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
explicit FlashWrite(const RawPacket& rawPacket);
|
explicit FlashWrite(const RawPacket& rawPacket);
|
||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadMemory::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
void ReadMemory::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||||
Logger::info("Handling ReadMemory packet");
|
Logger::info("Handling ReadMemory packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
explicit ReadMemory(const RawPacket& rawPacket, const Gdb::TargetDescriptor& gdbTargetDescriptor);
|
explicit ReadMemory(const RawPacket& rawPacket, const Gdb::TargetDescriptor& gdbTargetDescriptor);
|
||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadMemoryMap::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
void ReadMemoryMap::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||||
Logger::info("Handling ReadMemoryMap packet");
|
Logger::info("Handling ReadMemoryMap packet");
|
||||||
|
|
||||||
using Targets::TargetMemoryType;
|
using Targets::TargetMemoryType;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
explicit ReadMemoryMap(const RawPacket& rawPacket);
|
explicit ReadMemoryMap(const RawPacket& rawPacket);
|
||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadRegister::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
void ReadRegister::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||||
Logger::info("Handling ReadRegister packet");
|
Logger::info("Handling ReadRegister packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
explicit ReadRegister(const RawPacket& rawPacket);
|
explicit ReadRegister(const RawPacket& rawPacket);
|
||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
: CommandPacket(rawPacket)
|
: CommandPacket(rawPacket)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void ReadRegisters::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
void ReadRegisters::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||||
Logger::info("Handling ReadRegisters packet");
|
Logger::info("Handling ReadRegisters packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
explicit ReadRegisters(const RawPacket& rawPacket);
|
explicit ReadRegisters(const RawPacket& rawPacket);
|
||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#include "VContContinueExecution.hpp"
|
||||||
|
|
||||||
|
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||||
|
|
||||||
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
|
{
|
||||||
|
using Services::TargetControllerService;
|
||||||
|
|
||||||
|
using ResponsePackets::ErrorResponsePacket;
|
||||||
|
|
||||||
|
using ::Exceptions::Exception;
|
||||||
|
|
||||||
|
VContContinueExecution::VContContinueExecution(const RawPacket& rawPacket)
|
||||||
|
: CommandPacket(rawPacket)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void VContContinueExecution::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||||
|
Logger::info("Handling VContContinueExecution packet");
|
||||||
|
|
||||||
|
try {
|
||||||
|
targetControllerService.continueTargetExecution(std::nullopt, std::nullopt);
|
||||||
|
debugSession.waitingForBreak = true;
|
||||||
|
|
||||||
|
} catch (const Exception& exception) {
|
||||||
|
Logger::error("Failed to continue execution on target - " + exception.getMessage());
|
||||||
|
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
||||||
|
|
||||||
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The VContContinueExecution class implements a structure for "vCont;c" and "vCont;C" packets. These packets
|
||||||
|
* instruct the server to continue execution on the target.
|
||||||
|
*/
|
||||||
|
class VContContinueExecution: public Gdb::CommandPackets::CommandPacket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit VContContinueExecution(const RawPacket& rawPacket);
|
||||||
|
|
||||||
|
void handle(
|
||||||
|
Gdb::DebugSession& debugSession,
|
||||||
|
Services::TargetControllerService& targetControllerService
|
||||||
|
) override;
|
||||||
|
};
|
||||||
|
}
|
||||||
216
src/DebugServer/Gdb/AvrGdb/CommandPackets/VContRangeStep.cpp
Normal file
216
src/DebugServer/Gdb/AvrGdb/CommandPackets/VContRangeStep.cpp
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
#include "VContRangeStep.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "src/Services/Avr8InstructionService.hpp"
|
||||||
|
#include "src/Services/StringService.hpp"
|
||||||
|
#include "src/Services/PathService.hpp"
|
||||||
|
|
||||||
|
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||||
|
#include "src/Exceptions/Exception.hpp"
|
||||||
|
#include "src/Logger/Logger.hpp"
|
||||||
|
|
||||||
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
|
{
|
||||||
|
using Services::TargetControllerService;
|
||||||
|
|
||||||
|
using ResponsePackets::ErrorResponsePacket;
|
||||||
|
|
||||||
|
using ::Exceptions::Exception;
|
||||||
|
|
||||||
|
VContRangeStep::VContRangeStep(const RawPacket& rawPacket)
|
||||||
|
: CommandPacket(rawPacket)
|
||||||
|
{
|
||||||
|
if (this->data.size() < 10) {
|
||||||
|
throw Exception("Unexpected VContRangeStep packet size");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A single vCont packet can consist of multiple actions - we don't accommodate this ATM. We only focus on
|
||||||
|
* the first action in the packet.
|
||||||
|
*
|
||||||
|
* We also ignore the thread id (as it's not necessary here).
|
||||||
|
*/
|
||||||
|
|
||||||
|
const auto commandData = std::string(this->data.begin() + 7, this->data.end());
|
||||||
|
|
||||||
|
const auto delimiterPosition = commandData.find(',');
|
||||||
|
const auto threadIdDelimiterPosition = commandData.find(':');
|
||||||
|
|
||||||
|
if (delimiterPosition == std::string::npos || delimiterPosition >= (commandData.size() - 1)) {
|
||||||
|
throw Exception("Invalid VContRangeStep packet");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& delimiterIt = commandData.begin() + static_cast<decltype(commandData)::difference_type>(
|
||||||
|
delimiterPosition
|
||||||
|
);
|
||||||
|
const auto startAddressHex = std::string(commandData.begin(), delimiterIt);
|
||||||
|
const auto endAddressHex = std::string(
|
||||||
|
delimiterIt + 1,
|
||||||
|
threadIdDelimiterPosition != std::string::npos
|
||||||
|
? commandData.begin() + static_cast<decltype(commandData)::difference_type>(threadIdDelimiterPosition)
|
||||||
|
: commandData.end()
|
||||||
|
);
|
||||||
|
|
||||||
|
this->startAddress = static_cast<Targets::TargetProgramCounter>(std::stoi(startAddressHex, nullptr, 16));
|
||||||
|
this->endAddress = static_cast<Targets::TargetProgramCounter>(std::stoi(endAddressHex, nullptr, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VContRangeStep::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||||
|
using Services::Avr8InstructionService;
|
||||||
|
using Services::StringService;
|
||||||
|
|
||||||
|
Logger::info("Handling VContRangeStep packet");
|
||||||
|
|
||||||
|
Logger::debug("Requested stepping range start address: 0x" + StringService::toHex(this->startAddress));
|
||||||
|
Logger::debug("Requested stepping range end address (exclusive): 0x" + StringService::toHex(this->endAddress));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const auto& targetDescriptor = debugSession.gdbTargetDescriptor.targetDescriptor;
|
||||||
|
const auto& programMemoryAddressRange = targetDescriptor.memoryDescriptorsByType.at(
|
||||||
|
targetDescriptor.programMemoryType
|
||||||
|
).addressRange;
|
||||||
|
|
||||||
|
if (
|
||||||
|
this->startAddress >= this->endAddress
|
||||||
|
|| (this->startAddress % 2) != 0
|
||||||
|
|| (this->endAddress % 2) != 0
|
||||||
|
|| this->startAddress < programMemoryAddressRange.startAddress
|
||||||
|
|| this->endAddress > programMemoryAddressRange.endAddress
|
||||||
|
) {
|
||||||
|
throw Exception("Invalid address range in VContRangeStep");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debugSession.activeRangeSteppingSession.has_value()) {
|
||||||
|
Logger::warning(
|
||||||
|
"Attempted to start new range stepping session with one already active - terminating active session"
|
||||||
|
);
|
||||||
|
debugSession.terminateRangeSteppingSession(targetControllerService);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((this->endAddress - this->startAddress) == 2) {
|
||||||
|
// Single step requested. No need for a range step here.
|
||||||
|
targetControllerService.stepTargetExecution(std::nullopt);
|
||||||
|
debugSession.waitingForBreak = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto addressRange = Targets::TargetMemoryAddressRange(this->startAddress, this->endAddress);
|
||||||
|
auto rangeSteppingSession = RangeSteppingSession(addressRange, {});
|
||||||
|
|
||||||
|
const auto instructionsByAddress = Avr8InstructionService::fetchInstructions(
|
||||||
|
addressRange,
|
||||||
|
targetDescriptor,
|
||||||
|
targetControllerService
|
||||||
|
);
|
||||||
|
|
||||||
|
Logger::debug(
|
||||||
|
"Inspecting " + std::to_string(instructionsByAddress.size()) + " instructions within stepping range "
|
||||||
|
"(byte addresses) 0x" + StringService::toHex(addressRange.startAddress) + " -> 0x"
|
||||||
|
+ StringService::toHex(addressRange.endAddress) + ", in preparation for new range stepping session"
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const auto& [instructionAddress, instruction] : instructionsByAddress) {
|
||||||
|
if (!instruction.has_value()) {
|
||||||
|
/*
|
||||||
|
* We weren't able to decode the opcode at this address. We have no idea what this instruction
|
||||||
|
* will do.
|
||||||
|
*/
|
||||||
|
Logger::error(
|
||||||
|
"Failed to decode AVR8 opcode at byte address 0x" + StringService::toHex(instructionAddress)
|
||||||
|
+ " - the instruction will have to be intercepted. Please enable debug logging, reproduce "
|
||||||
|
"this message and report as an issue via " + Services::PathService::homeDomainName()
|
||||||
|
+ "/report-issue"
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We have no choice but to intercept it. When we reach it, we'll perform a single step and see
|
||||||
|
* what happens.
|
||||||
|
*/
|
||||||
|
rangeSteppingSession.interceptedAddresses.insert(instructionAddress);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instruction->canChangeProgramFlow) {
|
||||||
|
const auto destinationAddress = Avr8InstructionService::resolveProgramDestinationAddress(
|
||||||
|
*instruction,
|
||||||
|
instructionAddress,
|
||||||
|
instructionsByAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!destinationAddress.has_value()) {
|
||||||
|
/*
|
||||||
|
* We don't know where this instruction may jump to, so we'll have to intercept it and perform
|
||||||
|
* a single step when we reach it.
|
||||||
|
*/
|
||||||
|
Logger::debug(
|
||||||
|
"Intercepting CCPF instruction (\"" + instruction->name + "\") at byte address 0x"
|
||||||
|
+ StringService::toHex(instructionAddress)
|
||||||
|
);
|
||||||
|
rangeSteppingSession.interceptedAddresses.insert(instructionAddress);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
*destinationAddress < programMemoryAddressRange.startAddress
|
||||||
|
|| *destinationAddress > programMemoryAddressRange.endAddress
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
* This instruction may jump to an invalid address. Someone screwed up here - could be
|
||||||
|
* something wrong in Bloom (opcode decoding bug, incorrect program memory address range in
|
||||||
|
* the target descriptor, etc.), or the user has an invalid instruction in their program code.
|
||||||
|
*
|
||||||
|
* We have no choice but to intercept the instruction. When we reach it, we'll perform a single
|
||||||
|
* step and see what happens.
|
||||||
|
*/
|
||||||
|
Logger::debug(
|
||||||
|
"Intercepting CCPF instruction (\"" + instruction->name + "\") with invalid destination "
|
||||||
|
"byte address (0x" + StringService::toHex(*destinationAddress) + "), at byte address 0x"
|
||||||
|
+ StringService::toHex(instructionAddress)
|
||||||
|
);
|
||||||
|
rangeSteppingSession.interceptedAddresses.insert(instructionAddress);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
*destinationAddress < addressRange.startAddress
|
||||||
|
|| *destinationAddress >= addressRange.endAddress
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
* This instruction may jump to an address outside the requested stepping range.
|
||||||
|
*
|
||||||
|
* Because we know exactly where it will jump to (if it jumps), we only need to intercept the
|
||||||
|
* destination address.
|
||||||
|
*/
|
||||||
|
Logger::debug(
|
||||||
|
"Intercepting CCPF instruction (\"" + instruction->name + "\") at destination byte address "
|
||||||
|
"0x" + StringService::toHex(*destinationAddress)
|
||||||
|
);
|
||||||
|
rangeSteppingSession.interceptedAddresses.insert(*destinationAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto subsequentInstructionAddress = instructionAddress + instruction->byteSize;
|
||||||
|
if (subsequentInstructionAddress >= addressRange.endAddress) {
|
||||||
|
/*
|
||||||
|
* Once this instruction has been executed, we'll end up outside the stepping range (so we'll want
|
||||||
|
* to stop there and report back to GDB).
|
||||||
|
*/
|
||||||
|
Logger::debug(
|
||||||
|
"Intercepting subsequent instruction at byte address 0x"
|
||||||
|
+ StringService::toHex(subsequentInstructionAddress)
|
||||||
|
);
|
||||||
|
rangeSteppingSession.interceptedAddresses.insert(subsequentInstructionAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debugSession.startRangeSteppingSession(std::move(rangeSteppingSession), targetControllerService);
|
||||||
|
targetControllerService.continueTargetExecution(std::nullopt, std::nullopt);
|
||||||
|
debugSession.waitingForBreak = true;
|
||||||
|
|
||||||
|
} catch (const Exception& exception) {
|
||||||
|
Logger::error("Failed to start new range stepping session - " + exception.getMessage());
|
||||||
|
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/DebugServer/Gdb/AvrGdb/CommandPackets/VContRangeStep.hpp
Normal file
29
src/DebugServer/Gdb/AvrGdb/CommandPackets/VContRangeStep.hpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
||||||
|
|
||||||
|
#include "src/Targets/TargetMemory.hpp"
|
||||||
|
|
||||||
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The VContRangeStep class implements a structure for the "vCont;r" packet. This packet instructs the server to
|
||||||
|
* step through a particular address range, and only report back to GDB when execution leaves that range, or when an
|
||||||
|
* external breakpoint has been reached.
|
||||||
|
*/
|
||||||
|
class VContRangeStep: public Gdb::CommandPackets::CommandPacket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Targets::TargetProgramCounter startAddress;
|
||||||
|
Targets::TargetProgramCounter endAddress;
|
||||||
|
|
||||||
|
explicit VContRangeStep(const RawPacket& rawPacket);
|
||||||
|
|
||||||
|
void handle(
|
||||||
|
Gdb::DebugSession& debugSession,
|
||||||
|
Services::TargetControllerService& targetControllerService
|
||||||
|
) override;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#include "VContStepExecution.hpp"
|
||||||
|
|
||||||
|
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||||
|
|
||||||
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
|
{
|
||||||
|
using Services::TargetControllerService;
|
||||||
|
|
||||||
|
using ResponsePackets::ErrorResponsePacket;
|
||||||
|
|
||||||
|
using ::Exceptions::Exception;
|
||||||
|
|
||||||
|
VContStepExecution::VContStepExecution(const RawPacket& rawPacket)
|
||||||
|
: CommandPacket(rawPacket)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void VContStepExecution::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||||
|
Logger::info("Handling VContStepExecution packet");
|
||||||
|
|
||||||
|
try {
|
||||||
|
targetControllerService.stepTargetExecution(std::nullopt);
|
||||||
|
debugSession.waitingForBreak = true;
|
||||||
|
|
||||||
|
} catch (const Exception& exception) {
|
||||||
|
Logger::error("Failed to step execution on target - " + exception.getMessage());
|
||||||
|
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
||||||
|
|
||||||
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The VContStepExecution class implements a structure for "vCont;s" and "vCont;S" packets.
|
||||||
|
*/
|
||||||
|
class VContStepExecution: public Gdb::CommandPackets::CommandPacket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit VContStepExecution(const RawPacket& rawPacket);
|
||||||
|
|
||||||
|
void handle(
|
||||||
|
Gdb::DebugSession& debugSession,
|
||||||
|
Services::TargetControllerService& targetControllerService
|
||||||
|
) override;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#include "VContSupportedActionsQuery.hpp"
|
||||||
|
|
||||||
|
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
|
||||||
|
|
||||||
|
#include "src/Logger/Logger.hpp"
|
||||||
|
|
||||||
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
|
{
|
||||||
|
using Services::TargetControllerService;
|
||||||
|
|
||||||
|
VContSupportedActionsQuery::VContSupportedActionsQuery(const RawPacket& rawPacket)
|
||||||
|
: CommandPacket(rawPacket)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void VContSupportedActionsQuery::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||||
|
Logger::info("Handling VContSupportedActionsQuery packet");
|
||||||
|
|
||||||
|
// Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom
|
||||||
|
debugSession.connection.writePacket(ResponsePackets::ResponsePacket(
|
||||||
|
debugSession.serverConfig.rangeSteppingEnabled
|
||||||
|
? "vCont;c;C;s;S;r"
|
||||||
|
: "vCont;c;C;s;S"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp"
|
||||||
|
|
||||||
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The VContSupportedActionsQuery command packet is a query from the GDB client, requesting a list of vCont actions
|
||||||
|
* supported by the server.
|
||||||
|
*
|
||||||
|
* Responses to this command packet should take the form of a ResponsePackets::SupportedFeaturesResponse.
|
||||||
|
*/
|
||||||
|
class VContSupportedActionsQuery: public Gdb::CommandPackets::CommandPacket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit VContSupportedActionsQuery(const RawPacket& rawPacket);
|
||||||
|
|
||||||
|
void handle(
|
||||||
|
Gdb::DebugSession& debugSession,
|
||||||
|
Services::TargetControllerService& targetControllerService
|
||||||
|
) override;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -68,7 +68,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteMemory::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
void WriteMemory::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||||
Logger::info("Handling WriteMemory packet");
|
Logger::info("Handling WriteMemory packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
explicit WriteMemory(const RawPacket& rawPacket, const Gdb::TargetDescriptor& gdbTargetDescriptor);
|
explicit WriteMemory(const RawPacket& rawPacket, const Gdb::TargetDescriptor& gdbTargetDescriptor);
|
||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
std::reverse(this->registerValue.begin(), this->registerValue.end());
|
std::reverse(this->registerValue.begin(), this->registerValue.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteRegister::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
void WriteRegister::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||||
Logger::info("Handling WriteRegister packet");
|
Logger::info("Handling WriteRegister packet");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
explicit WriteRegister(const RawPacket& rawPacket);
|
explicit WriteRegister(const RawPacket& rawPacket);
|
||||||
|
|
||||||
void handle(
|
void handle(
|
||||||
DebugSession& debugSession,
|
Gdb::DebugSession& debugSession,
|
||||||
Services::TargetControllerService& targetControllerService
|
Services::TargetControllerService& targetControllerService
|
||||||
) override;
|
) override;
|
||||||
};
|
};
|
||||||
|
|||||||
18
src/DebugServer/Gdb/AvrGdb/DebugSession.cpp
Normal file
18
src/DebugServer/Gdb/AvrGdb/DebugSession.cpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#include "DebugSession.hpp"
|
||||||
|
|
||||||
|
namespace DebugServer::Gdb::AvrGdb
|
||||||
|
{
|
||||||
|
DebugSession::DebugSession(
|
||||||
|
Connection&& connection,
|
||||||
|
const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures,
|
||||||
|
const TargetDescriptor& targetDescriptor,
|
||||||
|
const GdbDebugServerConfig& serverConfig
|
||||||
|
)
|
||||||
|
: Gdb::DebugSession(
|
||||||
|
std::move(connection),
|
||||||
|
supportedFeatures,
|
||||||
|
targetDescriptor,
|
||||||
|
serverConfig
|
||||||
|
)
|
||||||
|
{}
|
||||||
|
}
|
||||||
19
src/DebugServer/Gdb/AvrGdb/DebugSession.hpp
Normal file
19
src/DebugServer/Gdb/AvrGdb/DebugSession.hpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "src/DebugServer/Gdb/DebugSession.hpp"
|
||||||
|
|
||||||
|
#include "TargetDescriptor.hpp"
|
||||||
|
|
||||||
|
namespace DebugServer::Gdb::AvrGdb
|
||||||
|
{
|
||||||
|
class DebugSession final: public Gdb::DebugSession
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DebugSession(
|
||||||
|
Connection&& connection,
|
||||||
|
const std::set<std::pair<Feature, std::optional<std::string>>>& supportedFeatures,
|
||||||
|
const TargetDescriptor& targetDescriptor,
|
||||||
|
const GdbDebugServerConfig& serverConfig
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -26,8 +26,13 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
targetControllerService.stopTargetExecution();
|
targetControllerService.stopTargetExecution();
|
||||||
debugSession.connection.writePacket(TargetStopped(Signal::INTERRUPTED));
|
|
||||||
|
if (debugSession.activeRangeSteppingSession.has_value()) {
|
||||||
|
debugSession.terminateRangeSteppingSession(targetControllerService);
|
||||||
|
}
|
||||||
|
|
||||||
debugSession.waitingForBreak = false;
|
debugSession.waitingForBreak = false;
|
||||||
|
debugSession.connection.writePacket(TargetStopped(Signal::INTERRUPTED));
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to interrupt execution - " + exception.getMessage());
|
Logger::error("Failed to interrupt execution - " + exception.getMessage());
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
try {
|
try {
|
||||||
Logger::debug("Removing breakpoint at address " + std::to_string(this->address));
|
Logger::debug("Removing breakpoint at address " + std::to_string(this->address));
|
||||||
|
|
||||||
targetControllerService.removeBreakpoint(TargetBreakpoint(this->address));
|
debugSession.removeExternalBreakpoint(TargetBreakpoint(this->address), targetControllerService);
|
||||||
debugSession.connection.writePacket(OkResponsePacket());
|
debugSession.connection.writePacket(OkResponsePacket());
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ namespace DebugServer::Gdb::CommandPackets
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
targetControllerService.setBreakpoint(TargetBreakpoint(this->address));
|
debugSession.setExternalBreakpoint(TargetBreakpoint(this->address), targetControllerService);
|
||||||
debugSession.connection.writePacket(OkResponsePacket());
|
debugSession.connection.writePacket(OkResponsePacket());
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
|
|||||||
@@ -25,4 +25,88 @@ namespace DebugServer::Gdb
|
|||||||
DebugSession::~DebugSession() {
|
DebugSession::~DebugSession() {
|
||||||
EventManager::triggerEvent(std::make_shared<Events::DebugSessionFinished>());
|
EventManager::triggerEvent(std::make_shared<Events::DebugSessionFinished>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DebugSession::setInternalBreakpoint(
|
||||||
|
const Targets::TargetBreakpoint& breakpoint,
|
||||||
|
Services::TargetControllerService& targetControllerService
|
||||||
|
) {
|
||||||
|
if (this->internalBreakpointAddresses.contains(breakpoint.address)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->externalBreakpointAddresses.contains(breakpoint.address)) {
|
||||||
|
targetControllerService.setBreakpoint(breakpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->internalBreakpointAddresses.insert(breakpoint.address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSession::removeInternalBreakpoint(
|
||||||
|
const Targets::TargetBreakpoint& breakpoint,
|
||||||
|
Services::TargetControllerService& targetControllerService
|
||||||
|
) {
|
||||||
|
if (!this->internalBreakpointAddresses.contains(breakpoint.address)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->externalBreakpointAddresses.contains(breakpoint.address)) {
|
||||||
|
targetControllerService.removeBreakpoint(breakpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->internalBreakpointAddresses.erase(breakpoint.address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSession::setExternalBreakpoint(
|
||||||
|
const Targets::TargetBreakpoint& breakpoint,
|
||||||
|
Services::TargetControllerService& targetControllerService
|
||||||
|
) {
|
||||||
|
if (this->externalBreakpointAddresses.contains(breakpoint.address)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->internalBreakpointAddresses.contains(breakpoint.address)) {
|
||||||
|
targetControllerService.setBreakpoint(breakpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->externalBreakpointAddresses.insert(breakpoint.address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSession::removeExternalBreakpoint(
|
||||||
|
const Targets::TargetBreakpoint& breakpoint,
|
||||||
|
Services::TargetControllerService& targetControllerService
|
||||||
|
) {
|
||||||
|
if (!this->externalBreakpointAddresses.contains(breakpoint.address)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->internalBreakpointAddresses.contains(breakpoint.address)) {
|
||||||
|
targetControllerService.removeBreakpoint(breakpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->externalBreakpointAddresses.erase(breakpoint.address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSession::startRangeSteppingSession(
|
||||||
|
RangeSteppingSession&& session,
|
||||||
|
Services::TargetControllerService& targetControllerService
|
||||||
|
) {
|
||||||
|
for (const auto& interceptAddress : session.interceptedAddresses) {
|
||||||
|
this->setInternalBreakpoint(Targets::TargetBreakpoint(interceptAddress), targetControllerService);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->activeRangeSteppingSession = std::move(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSession::terminateRangeSteppingSession(Services::TargetControllerService& targetControllerService) {
|
||||||
|
if (!this->activeRangeSteppingSession.has_value()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear all intercepting breakpoints
|
||||||
|
for (const auto& interceptAddress : this->activeRangeSteppingSession->interceptedAddresses) {
|
||||||
|
this->removeInternalBreakpoint(Targets::TargetBreakpoint(interceptAddress), targetControllerService);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->activeRangeSteppingSession.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,19 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include "TargetDescriptor.hpp"
|
#include "TargetDescriptor.hpp"
|
||||||
#include "GdbDebugServerConfig.hpp"
|
#include "GdbDebugServerConfig.hpp"
|
||||||
#include "Connection.hpp"
|
#include "Connection.hpp"
|
||||||
#include "Feature.hpp"
|
#include "Feature.hpp"
|
||||||
#include "ProgrammingSession.hpp"
|
#include "ProgrammingSession.hpp"
|
||||||
|
#include "RangeSteppingSession.hpp"
|
||||||
|
|
||||||
#include "src/Targets/TargetMemory.hpp"
|
#include "src/Targets/TargetMemory.hpp"
|
||||||
|
#include "src/Targets/TargetBreakpoint.hpp"
|
||||||
|
|
||||||
|
#include "src/Services/TargetControllerService.hpp"
|
||||||
|
|
||||||
namespace DebugServer::Gdb
|
namespace DebugServer::Gdb
|
||||||
{
|
{
|
||||||
@@ -37,6 +42,22 @@ namespace DebugServer::Gdb
|
|||||||
*/
|
*/
|
||||||
const GdbDebugServerConfig& serverConfig;
|
const GdbDebugServerConfig& serverConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* During a debug session, we can set two types of breakpoints: internal and external.
|
||||||
|
*
|
||||||
|
* - Internal breakpoints are set by Bloom's GDB server, for reasons that are specific to Bloom's internals.
|
||||||
|
* For example, we use internal breakpoints to facilitate range stepping sessions, where we place
|
||||||
|
* intercepting breakpoints in places where we suspect the target will leave the stepping range.
|
||||||
|
*
|
||||||
|
* - External breakpoints are requested by the connected client (GDB), typically on behalf of the user.
|
||||||
|
* Sometimes GDB will set some internal breakpoints of its own, but from our perspective these are considered
|
||||||
|
* to be external breakpoints.
|
||||||
|
*
|
||||||
|
* We track internal and external breakpoints separately.
|
||||||
|
*/
|
||||||
|
std::set<Targets::TargetMemoryAddress> internalBreakpointAddresses;
|
||||||
|
std::set<Targets::TargetMemoryAddress> externalBreakpointAddresses;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
@@ -60,6 +81,14 @@ namespace DebugServer::Gdb
|
|||||||
*/
|
*/
|
||||||
std::optional<ProgrammingSession> programmingSession = std::nullopt;
|
std::optional<ProgrammingSession> programmingSession = std::nullopt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When we're range stepping, we maintain a range stepping session, which holds all info related to that
|
||||||
|
* particular range stepping session.
|
||||||
|
*
|
||||||
|
* This member should only be populated during a range stepping session.
|
||||||
|
*/
|
||||||
|
std::optional<RangeSteppingSession> activeRangeSteppingSession = std::nullopt;
|
||||||
|
|
||||||
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,
|
||||||
@@ -73,6 +102,35 @@ namespace DebugServer::Gdb
|
|||||||
DebugSession& operator = (const DebugSession& other) = delete;
|
DebugSession& operator = (const DebugSession& other) = delete;
|
||||||
DebugSession& operator = (DebugSession&& other) = delete;
|
DebugSession& operator = (DebugSession&& other) = delete;
|
||||||
|
|
||||||
~DebugSession();
|
virtual ~DebugSession();
|
||||||
|
|
||||||
|
virtual void setInternalBreakpoint(
|
||||||
|
const Targets::TargetBreakpoint& breakpoint,
|
||||||
|
Services::TargetControllerService& targetControllerService
|
||||||
|
);
|
||||||
|
|
||||||
|
virtual void removeInternalBreakpoint(
|
||||||
|
const Targets::TargetBreakpoint& breakpoint,
|
||||||
|
Services::TargetControllerService& targetControllerService
|
||||||
|
);
|
||||||
|
|
||||||
|
virtual void setExternalBreakpoint(
|
||||||
|
const Targets::TargetBreakpoint& breakpoint,
|
||||||
|
Services::TargetControllerService& targetControllerService
|
||||||
|
);
|
||||||
|
|
||||||
|
virtual void removeExternalBreakpoint(
|
||||||
|
const Targets::TargetBreakpoint& breakpoint,
|
||||||
|
Services::TargetControllerService& targetControllerService
|
||||||
|
);
|
||||||
|
|
||||||
|
virtual void startRangeSteppingSession(
|
||||||
|
RangeSteppingSession&& session,
|
||||||
|
Services::TargetControllerService& targetControllerService
|
||||||
|
);
|
||||||
|
|
||||||
|
virtual void terminateRangeSteppingSession(
|
||||||
|
Services::TargetControllerService& targetControllerService
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ namespace DebugServer::Gdb
|
|||||||
HARDWARE_BREAKPOINTS,
|
HARDWARE_BREAKPOINTS,
|
||||||
PACKET_SIZE,
|
PACKET_SIZE,
|
||||||
MEMORY_MAP_READ,
|
MEMORY_MAP_READ,
|
||||||
|
VCONT_ACTIONS_QUERY,
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline BiMap<Feature, std::string> getGdbFeatureToNameMapping() {
|
static inline BiMap<Feature, std::string> getGdbFeatureToNameMapping() {
|
||||||
@@ -18,6 +19,7 @@ namespace DebugServer::Gdb
|
|||||||
{Feature::SOFTWARE_BREAKPOINTS, "swbreak"},
|
{Feature::SOFTWARE_BREAKPOINTS, "swbreak"},
|
||||||
{Feature::PACKET_SIZE, "PacketSize"},
|
{Feature::PACKET_SIZE, "PacketSize"},
|
||||||
{Feature::MEMORY_MAP_READ, "qXfer:memory-map:read"},
|
{Feature::MEMORY_MAP_READ, "qXfer:memory-map:read"},
|
||||||
|
{Feature::VCONT_ACTIONS_QUERY, "vContSupported"},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
#include "ResponsePackets/TargetStopped.hpp"
|
#include "ResponsePackets/TargetStopped.hpp"
|
||||||
|
|
||||||
#include "src/Services/ProcessService.hpp"
|
#include "src/Services/ProcessService.hpp"
|
||||||
|
#include "src/Services/StringService.hpp"
|
||||||
|
|
||||||
namespace DebugServer::Gdb
|
namespace DebugServer::Gdb
|
||||||
{
|
{
|
||||||
@@ -135,7 +136,7 @@ namespace DebugServer::Gdb
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GdbRspDebugServer::close() {
|
void GdbRspDebugServer::close() {
|
||||||
this->activeDebugSession.reset();
|
this->endDebugSession();
|
||||||
|
|
||||||
if (this->serverSocketFileDescriptor.has_value()) {
|
if (this->serverSocketFileDescriptor.has_value()) {
|
||||||
::close(this->serverSocketFileDescriptor.value());
|
::close(this->serverSocketFileDescriptor.value());
|
||||||
@@ -144,19 +145,14 @@ namespace DebugServer::Gdb
|
|||||||
|
|
||||||
void GdbRspDebugServer::run() {
|
void GdbRspDebugServer::run() {
|
||||||
try {
|
try {
|
||||||
if (!this->activeDebugSession.has_value()) {
|
if (this->getActiveDebugSession() == nullptr) {
|
||||||
Logger::info("Waiting for GDB RSP connection");
|
Logger::info("Waiting for GDB RSP connection");
|
||||||
|
|
||||||
auto connection = this->waitForConnection();
|
auto connection = this->waitForConnection();
|
||||||
|
|
||||||
Logger::info("Accepted GDP RSP connection from " + connection.getIpAddress());
|
Logger::info("Accepted GDP RSP connection from " + connection.getIpAddress());
|
||||||
|
|
||||||
this->activeDebugSession.emplace(
|
this->startDebugSession(std::move(connection));
|
||||||
std::move(connection),
|
|
||||||
this->getSupportedFeatures(),
|
|
||||||
this->getGdbTargetDescriptor(),
|
|
||||||
this->debugServerConfig
|
|
||||||
);
|
|
||||||
|
|
||||||
this->targetControllerService.stopTargetExecution();
|
this->targetControllerService.stopTargetExecution();
|
||||||
this->targetControllerService.resetTarget();
|
this->targetControllerService.resetTarget();
|
||||||
@@ -165,29 +161,29 @@ namespace DebugServer::Gdb
|
|||||||
const auto commandPacket = this->waitForCommandPacket();
|
const auto commandPacket = this->waitForCommandPacket();
|
||||||
|
|
||||||
if (commandPacket) {
|
if (commandPacket) {
|
||||||
commandPacket->handle(this->activeDebugSession.value(), this->targetControllerService);
|
commandPacket->handle(*(this->getActiveDebugSession()), this->targetControllerService);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (const ClientDisconnected&) {
|
} catch (const ClientDisconnected&) {
|
||||||
Logger::info("GDB RSP client disconnected");
|
Logger::info("GDB RSP client disconnected");
|
||||||
this->activeDebugSession.reset();
|
this->endDebugSession();
|
||||||
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->activeDebugSession.reset();
|
this->endDebugSession();
|
||||||
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->activeDebugSession.reset();
|
this->endDebugSession();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} catch (const DebugSessionInitialisationFailure& exception) {
|
} catch (const DebugSessionInitialisationFailure& exception) {
|
||||||
Logger::warning("GDB debug session initialisation failure - " + exception.getMessage());
|
Logger::warning("GDB debug session initialisation failure - " + exception.getMessage());
|
||||||
this->activeDebugSession.reset();
|
this->endDebugSession();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} catch (const DebugServerInterrupted&) {
|
} catch (const DebugServerInterrupted&) {
|
||||||
@@ -219,14 +215,15 @@ namespace DebugServer::Gdb
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<CommandPacket> GdbRspDebugServer::waitForCommandPacket() {
|
std::unique_ptr<CommandPacket> GdbRspDebugServer::waitForCommandPacket() {
|
||||||
const auto rawPackets = this->activeDebugSession->connection.readRawPackets();
|
auto* debugSession = this->getActiveDebugSession();
|
||||||
|
const auto rawPackets = debugSession->connection.readRawPackets();
|
||||||
|
|
||||||
if (rawPackets.size() > 1) {
|
if (rawPackets.size() > 1) {
|
||||||
const auto& firstRawPacket = rawPackets.front();
|
const auto& firstRawPacket = rawPackets.front();
|
||||||
|
|
||||||
if (firstRawPacket.size() == 5 && firstRawPacket[1] == 0x03) {
|
if (firstRawPacket.size() == 5 && firstRawPacket[1] == 0x03) {
|
||||||
// Interrupt packet that came in too quickly before another packet
|
// Interrupt packet that came in too quickly before another packet
|
||||||
this->activeDebugSession->pendingInterrupt = true;
|
debugSession->pendingInterrupt = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only process the last packet - any others will probably be duplicates from an impatient client.
|
// We only process the last packet - any others will probably be duplicates from an impatient client.
|
||||||
@@ -312,68 +309,55 @@ namespace DebugServer::Gdb
|
|||||||
return std::make_unique<CommandPacket>(rawPacket);
|
return std::make_unique<CommandPacket>(rawPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<std::pair<Feature, std::optional<std::string>>> GdbRspDebugServer::getSupportedFeatures() {
|
void GdbRspDebugServer::onTargetExecutionStopped(const Events::TargetExecutionStopped& stoppedEvent) {
|
||||||
return {
|
using Services::StringService;
|
||||||
{Feature::SOFTWARE_BREAKPOINTS, std::nullopt},
|
|
||||||
};
|
auto* debugSession = this->getActiveDebugSession();
|
||||||
}
|
|
||||||
|
|
||||||
void GdbRspDebugServer::onTargetExecutionStopped(const Events::TargetExecutionStopped&) {
|
|
||||||
try {
|
try {
|
||||||
if (this->activeDebugSession.has_value() && this->activeDebugSession->waitingForBreak) {
|
if (debugSession != nullptr && debugSession->waitingForBreak) {
|
||||||
this->activeDebugSession->connection.writePacket(
|
this->handleTargetStoppedGdbResponse(stoppedEvent.programCounter);
|
||||||
ResponsePackets::TargetStopped(Signal::TRAP)
|
|
||||||
);
|
|
||||||
this->activeDebugSession->waitingForBreak = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (const ClientDisconnected&) {
|
} catch (const ClientDisconnected&) {
|
||||||
Logger::info("GDB RSP client disconnected");
|
Logger::info("GDB RSP client disconnected");
|
||||||
this->activeDebugSession.reset();
|
this->endDebugSession();
|
||||||
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->activeDebugSession.reset();
|
this->endDebugSession();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} catch (const DebugServerInterrupted&) {
|
} catch (const DebugServerInterrupted&) {
|
||||||
// Server was interrupted
|
// Server was interrupted
|
||||||
Logger::debug("GDB RSP interrupted");
|
Logger::debug("GDB RSP interrupted");
|
||||||
return;
|
return;
|
||||||
|
} catch (const Exception& exception) {
|
||||||
|
Logger::error("Failed to handle target execution stopped event - " + exception.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbRspDebugServer::onTargetExecutionResumed(const Events::TargetExecutionResumed&) {
|
void GdbRspDebugServer::onTargetExecutionResumed(const Events::TargetExecutionResumed&) {
|
||||||
|
auto* debugSession = this->getActiveDebugSession();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (
|
if (debugSession != nullptr) {
|
||||||
this->activeDebugSession.has_value()
|
this->handleTargetResumedGdbResponse();
|
||||||
&& this->activeDebugSession->pendingInterrupt
|
|
||||||
&& this->activeDebugSession->waitingForBreak
|
|
||||||
) {
|
|
||||||
Logger::info("Servicing pending interrupt");
|
|
||||||
this->targetControllerService.stopTargetExecution();
|
|
||||||
|
|
||||||
this->activeDebugSession->connection.writePacket(
|
|
||||||
ResponsePackets::TargetStopped(Signal::INTERRUPTED)
|
|
||||||
);
|
|
||||||
|
|
||||||
this->activeDebugSession->pendingInterrupt = false;
|
|
||||||
this->activeDebugSession->waitingForBreak = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (const ClientDisconnected&) {
|
} catch (const ClientDisconnected&) {
|
||||||
Logger::info("GDB RSP client disconnected");
|
Logger::info("GDB RSP client disconnected");
|
||||||
this->activeDebugSession.reset();
|
this->endDebugSession();
|
||||||
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->activeDebugSession.reset();
|
this->endDebugSession();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} catch (const DebugServerInterrupted&) {
|
} catch (const DebugServerInterrupted&) {
|
||||||
@@ -382,7 +366,35 @@ namespace DebugServer::Gdb
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
} catch (const Exception& exception) {
|
} catch (const Exception& exception) {
|
||||||
Logger::error("Failed to interrupt execution - " + exception.getMessage());
|
Logger::error("Failed to handle target execution resumed event - " + exception.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbRspDebugServer::handleTargetStoppedGdbResponse(Targets::TargetProgramCounter programAddress) {
|
||||||
|
auto* debugSession = this->getActiveDebugSession();
|
||||||
|
|
||||||
|
if (debugSession->activeRangeSteppingSession.has_value()) {
|
||||||
|
debugSession->terminateRangeSteppingSession(this->targetControllerService);
|
||||||
|
}
|
||||||
|
|
||||||
|
debugSession->connection.writePacket(ResponsePackets::TargetStopped(Signal::TRAP));
|
||||||
|
debugSession->waitingForBreak = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbRspDebugServer::handleTargetResumedGdbResponse() {
|
||||||
|
auto* debugSession = this->getActiveDebugSession();
|
||||||
|
|
||||||
|
if (debugSession->waitingForBreak && debugSession->pendingInterrupt) {
|
||||||
|
Logger::info("Servicing pending interrupt");
|
||||||
|
this->targetControllerService.stopTargetExecution();
|
||||||
|
|
||||||
|
if (debugSession->activeRangeSteppingSession.has_value()) {
|
||||||
|
debugSession->terminateRangeSteppingSession(this->targetControllerService);
|
||||||
|
}
|
||||||
|
|
||||||
|
debugSession->connection.writePacket(ResponsePackets::TargetStopped(Signal::INTERRUPTED));
|
||||||
|
debugSession->pendingInterrupt = false;
|
||||||
|
debugSession->waitingForBreak = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "src/DebugServer/ServerInterface.hpp"
|
#include "src/DebugServer/ServerInterface.hpp"
|
||||||
|
|
||||||
@@ -127,10 +128,25 @@ namespace DebugServer::Gdb
|
|||||||
std::optional<int> serverSocketFileDescriptor;
|
std::optional<int> serverSocketFileDescriptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When a connection with a GDB client is established, a new instance of the DebugSession class is created and
|
* Should start a new debug session for the newly established `connection`.
|
||||||
* held here. A value of std::nullopt means there is no active debug session present.
|
*
|
||||||
|
* @param connection
|
||||||
|
* @return
|
||||||
*/
|
*/
|
||||||
std::optional<DebugSession> activeDebugSession;
|
virtual DebugSession* startDebugSession(Connection&& connection) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should end the currently active debug session, if one exists.
|
||||||
|
*/
|
||||||
|
virtual void endDebugSession() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should return a non-owning pointer of the currently active debug session. Or nullptr if there is no active
|
||||||
|
* session.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
virtual DebugSession* getActiveDebugSession() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits for a GDB client to connect on the listening socket.
|
* Waits for a GDB client to connect on the listening socket.
|
||||||
@@ -161,19 +177,6 @@ namespace DebugServer::Gdb
|
|||||||
*/
|
*/
|
||||||
virtual std::unique_ptr<CommandPackets::CommandPacket> resolveCommandPacket(const RawPacket& rawPacket);
|
virtual std::unique_ptr<CommandPackets::CommandPacket> resolveCommandPacket(const RawPacket& rawPacket);
|
||||||
|
|
||||||
/**
|
|
||||||
* Should return a set of GDB features supported by the GDB server. Each supported feature may come with an
|
|
||||||
* optional value.
|
|
||||||
*
|
|
||||||
* The set of features returned by this function will be stored against the active debug session object.
|
|
||||||
*
|
|
||||||
* Derived GDB server implementations may override this function to include any features that are specific to
|
|
||||||
* those implementations.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
virtual std::set<std::pair<Feature, std::optional<std::string>>> getSupportedFeatures();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should return the GDB target descriptor for the connected target.
|
* Should return the GDB target descriptor for the connected target.
|
||||||
*
|
*
|
||||||
@@ -189,11 +192,14 @@ namespace DebugServer::Gdb
|
|||||||
* If the GDB client is currently waiting for the target execution to stop, this event handler will issue
|
* If the GDB client is currently waiting for the target execution to stop, this event handler will issue
|
||||||
* 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& stoppedEvent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Services any pending interrupts.
|
* Services any pending interrupts.
|
||||||
*/
|
*/
|
||||||
void onTargetExecutionResumed(const Events::TargetExecutionResumed&);
|
void onTargetExecutionResumed(const Events::TargetExecutionResumed&);
|
||||||
|
|
||||||
|
virtual void handleTargetStoppedGdbResponse(Targets::TargetProgramCounter programAddress);
|
||||||
|
virtual void handleTargetResumedGdbResponse();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
46
src/DebugServer/Gdb/RangeSteppingSession.hpp
Normal file
46
src/DebugServer/Gdb/RangeSteppingSession.hpp
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include "src/Targets/TargetMemory.hpp"
|
||||||
|
|
||||||
|
namespace DebugServer::Gdb
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A range stepping session is created upon the receipt of a VContRangeStep command, from GDB.
|
||||||
|
*
|
||||||
|
* Any information related to the range stepping session is held here.
|
||||||
|
*
|
||||||
|
* See DebugSession::activeRangeSteppingSession for more.
|
||||||
|
*/
|
||||||
|
struct RangeSteppingSession
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The (byte) address range we're stepping over.
|
||||||
|
*
|
||||||
|
* NOTE: range::endAddress is exclusive!
|
||||||
|
*/
|
||||||
|
Targets::TargetMemoryAddressRange range;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any program memory (byte) addresses that we had to intercept as part of this session.
|
||||||
|
*/
|
||||||
|
std::set<Targets::TargetMemoryAddress> interceptedAddresses;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether we're currently performing a single step, in this session, to observe the behaviour of a particular
|
||||||
|
* instruction.
|
||||||
|
*
|
||||||
|
* See AvrGdbRsp::handleTargetStoppedGdbResponse() for more.
|
||||||
|
*/
|
||||||
|
bool singleStepping = false;
|
||||||
|
|
||||||
|
RangeSteppingSession(
|
||||||
|
const Targets::TargetMemoryAddressRange& range,
|
||||||
|
const std::set<Targets::TargetMemoryAddress>& interceptedAddresses
|
||||||
|
)
|
||||||
|
: range(range)
|
||||||
|
, interceptedAddresses(interceptedAddresses)
|
||||||
|
{};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -37,6 +37,12 @@ namespace Services
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string StringService::toHex(std::uint32_t value) {
|
||||||
|
auto stream = std::stringstream();
|
||||||
|
stream << std::hex << std::setfill('0') << std::setw(8) << static_cast<unsigned int>(value);
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
std::string StringService::toHex(unsigned char value) {
|
std::string StringService::toHex(unsigned char value) {
|
||||||
auto stream = std::stringstream();
|
auto stream = std::stringstream();
|
||||||
stream << std::hex << std::setfill('0') << std::setw(2) << static_cast<unsigned int>(value);
|
stream << std::hex << std::setfill('0') << std::setw(2) << static_cast<unsigned int>(value);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Services
|
namespace Services
|
||||||
@@ -15,6 +16,7 @@ namespace Services
|
|||||||
static bool isAscii(const std::string& str);
|
static bool isAscii(const std::string& str);
|
||||||
static std::string replaceUnprintable(std::string str);
|
static std::string replaceUnprintable(std::string str);
|
||||||
|
|
||||||
|
static std::string toHex(std::uint32_t value);
|
||||||
static std::string toHex(unsigned char value);
|
static std::string toHex(unsigned char value);
|
||||||
static std::string toHex(const std::vector<unsigned char>& data);
|
static std::string toHex(const std::vector<unsigned char>& data);
|
||||||
static std::string toHex(const std::string& data);
|
static std::string toHex(const std::string& data);
|
||||||
|
|||||||
Reference in New Issue
Block a user