Support for hardware breakpoints
This commit is contained in:
@@ -159,7 +159,7 @@ namespace DebugServer::Gdb::AvrGdb
|
||||
*
|
||||
* We need to figure out why, and determine whether the stop should be reported to GDB.
|
||||
*/
|
||||
if (this->activeDebugSession->externalBreakpointAddresses.contains(programAddress)) {
|
||||
if (this->activeDebugSession->externalBreakpointsByAddress.contains(programAddress)) {
|
||||
/*
|
||||
* The target stopped due to an external breakpoint, set by GDB.
|
||||
*
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
try {
|
||||
Logger::debug("Removing breakpoint at address " + std::to_string(this->address));
|
||||
|
||||
debugSession.removeExternalBreakpoint(TargetBreakpoint(this->address), targetControllerService);
|
||||
debugSession.removeExternalBreakpoint(this->address, targetControllerService);
|
||||
debugSession.connection.writePacket(OkResponsePacket());
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
return;
|
||||
}
|
||||
|
||||
debugSession.setExternalBreakpoint(TargetBreakpoint(this->address), targetControllerService);
|
||||
debugSession.setExternalBreakpoint(this->address, targetControllerService);
|
||||
debugSession.connection.writePacket(OkResponsePacket());
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
|
||||
@@ -27,63 +27,83 @@ namespace DebugServer::Gdb
|
||||
}
|
||||
|
||||
void DebugSession::setInternalBreakpoint(
|
||||
const Targets::TargetBreakpoint& breakpoint,
|
||||
Targets::TargetMemoryAddress address,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) {
|
||||
if (this->internalBreakpointAddresses.contains(breakpoint.address)) {
|
||||
if (this->internalBreakpointsByAddress.contains(address)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->externalBreakpointAddresses.contains(breakpoint.address)) {
|
||||
targetControllerService.setBreakpoint(breakpoint);
|
||||
const auto externalBreakpointIt = this->externalBreakpointsByAddress.find(address);
|
||||
|
||||
if (externalBreakpointIt != this->externalBreakpointsByAddress.end()) {
|
||||
// We already have an external breakpoint at this address
|
||||
this->internalBreakpointsByAddress.insert(std::pair(address, externalBreakpointIt->second));
|
||||
return;
|
||||
}
|
||||
|
||||
this->internalBreakpointAddresses.insert(breakpoint.address);
|
||||
this->internalBreakpointsByAddress.insert(
|
||||
std::pair(
|
||||
address,
|
||||
targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void DebugSession::removeInternalBreakpoint(
|
||||
const Targets::TargetBreakpoint& breakpoint,
|
||||
Targets::TargetMemoryAddress address,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) {
|
||||
if (!this->internalBreakpointAddresses.contains(breakpoint.address)) {
|
||||
const auto breakpointIt = this->internalBreakpointsByAddress.find(address);
|
||||
if (breakpointIt == this->internalBreakpointsByAddress.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->externalBreakpointAddresses.contains(breakpoint.address)) {
|
||||
targetControllerService.removeBreakpoint(breakpoint);
|
||||
if (!this->externalBreakpointsByAddress.contains(address)) {
|
||||
targetControllerService.removeBreakpoint(breakpointIt->second);
|
||||
}
|
||||
|
||||
this->internalBreakpointAddresses.erase(breakpoint.address);
|
||||
this->internalBreakpointsByAddress.erase(breakpointIt);
|
||||
}
|
||||
|
||||
void DebugSession::setExternalBreakpoint(
|
||||
const Targets::TargetBreakpoint& breakpoint,
|
||||
Targets::TargetMemoryAddress address,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) {
|
||||
if (this->externalBreakpointAddresses.contains(breakpoint.address)) {
|
||||
if (this->externalBreakpointsByAddress.contains(address)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->internalBreakpointAddresses.contains(breakpoint.address)) {
|
||||
targetControllerService.setBreakpoint(breakpoint);
|
||||
const auto internalBreakpointIt = this->internalBreakpointsByAddress.find(address);
|
||||
|
||||
if (internalBreakpointIt != this->internalBreakpointsByAddress.end()) {
|
||||
// We already have an internal breakpoint at this address
|
||||
this->externalBreakpointsByAddress.insert(std::pair(address, internalBreakpointIt->second));
|
||||
return;
|
||||
}
|
||||
|
||||
this->externalBreakpointAddresses.insert(breakpoint.address);
|
||||
this->externalBreakpointsByAddress.insert(
|
||||
std::pair(
|
||||
address,
|
||||
targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void DebugSession::removeExternalBreakpoint(
|
||||
const Targets::TargetBreakpoint& breakpoint,
|
||||
Targets::TargetMemoryAddress address,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) {
|
||||
if (!this->externalBreakpointAddresses.contains(breakpoint.address)) {
|
||||
const auto breakpointIt = this->externalBreakpointsByAddress.find(address);
|
||||
if (breakpointIt == this->externalBreakpointsByAddress.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->internalBreakpointAddresses.contains(breakpoint.address)) {
|
||||
targetControllerService.removeBreakpoint(breakpoint);
|
||||
if (!this->internalBreakpointsByAddress.contains(address)) {
|
||||
targetControllerService.removeBreakpoint(breakpointIt->second);
|
||||
}
|
||||
|
||||
this->externalBreakpointAddresses.erase(breakpoint.address);
|
||||
this->externalBreakpointsByAddress.erase(breakpointIt);
|
||||
}
|
||||
|
||||
void DebugSession::startRangeSteppingSession(
|
||||
@@ -91,7 +111,7 @@ namespace DebugServer::Gdb
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) {
|
||||
for (const auto& interceptAddress : session.interceptedAddresses) {
|
||||
this->setInternalBreakpoint(Targets::TargetBreakpoint(interceptAddress), targetControllerService);
|
||||
this->setInternalBreakpoint(interceptAddress, targetControllerService);
|
||||
}
|
||||
|
||||
this->activeRangeSteppingSession = std::move(session);
|
||||
@@ -104,7 +124,7 @@ namespace DebugServer::Gdb
|
||||
|
||||
// Clear all intercepting breakpoints
|
||||
for (const auto& interceptAddress : this->activeRangeSteppingSession->interceptedAddresses) {
|
||||
this->removeInternalBreakpoint(Targets::TargetBreakpoint(interceptAddress), targetControllerService);
|
||||
this->removeInternalBreakpoint(interceptAddress, targetControllerService);
|
||||
}
|
||||
|
||||
this->activeRangeSteppingSession.reset();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
#include "TargetDescriptor.hpp"
|
||||
#include "GdbDebugServerConfig.hpp"
|
||||
@@ -55,8 +55,8 @@ namespace DebugServer::Gdb
|
||||
*
|
||||
* We track internal and external breakpoints separately.
|
||||
*/
|
||||
std::set<Targets::TargetMemoryAddress> internalBreakpointAddresses;
|
||||
std::set<Targets::TargetMemoryAddress> externalBreakpointAddresses;
|
||||
std::map<Targets::TargetMemoryAddress, Targets::TargetBreakpoint> internalBreakpointsByAddress;
|
||||
std::map<Targets::TargetMemoryAddress, Targets::TargetBreakpoint> externalBreakpointsByAddress;
|
||||
|
||||
/**
|
||||
* When the GDB client is waiting for the target to halt, this is set to true so we know when to notify the
|
||||
@@ -105,22 +105,22 @@ namespace DebugServer::Gdb
|
||||
virtual ~DebugSession();
|
||||
|
||||
virtual void setInternalBreakpoint(
|
||||
const Targets::TargetBreakpoint& breakpoint,
|
||||
Targets::TargetMemoryAddress address,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
);
|
||||
|
||||
virtual void removeInternalBreakpoint(
|
||||
const Targets::TargetBreakpoint& breakpoint,
|
||||
Targets::TargetMemoryAddress address,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
);
|
||||
|
||||
virtual void setExternalBreakpoint(
|
||||
const Targets::TargetBreakpoint& breakpoint,
|
||||
Targets::TargetMemoryAddress address,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
);
|
||||
|
||||
virtual void removeExternalBreakpoint(
|
||||
const Targets::TargetBreakpoint& breakpoint,
|
||||
Targets::TargetMemoryAddress address,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class ClearHardwareBreakpoint: public Avr8GenericCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
explicit ClearHardwareBreakpoint(std::uint8_t number)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The clear hardware breakpoint command consists of 3 bytes:
|
||||
*
|
||||
* 1. Command ID (0x41)
|
||||
* 2. Version (0x00)
|
||||
* 3. Breakpoint Number (1, 2, or 3)
|
||||
*/
|
||||
this->payload = {
|
||||
0x41,
|
||||
0x00,
|
||||
number
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class SetHardwareBreakpoint: public Avr8GenericCommandFrame<std::array<unsigned char, 9>>
|
||||
{
|
||||
public:
|
||||
explicit SetHardwareBreakpoint(std::uint32_t address, std::uint8_t number)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The set hardware breakpoint command consists of 9 bytes:
|
||||
*
|
||||
* 1. Command ID (0x40)
|
||||
* 2. Version (0x00)
|
||||
* 3. Breakpoint Type (0x01 for program break)
|
||||
* 4. Breakpoint Number (1, 2, or 3)
|
||||
* 5. Program address (4 bytes) - the EDBG Protocol document states that this should be the word address,
|
||||
* but that seems to be incorrect. The tool expects a byte address here.
|
||||
* 5. Mode (0x03 for program break)
|
||||
*/
|
||||
this->payload = {
|
||||
0x40,
|
||||
0x00,
|
||||
0x01,
|
||||
number,
|
||||
static_cast<unsigned char>(address),
|
||||
static_cast<unsigned char>(address >> 8),
|
||||
static_cast<unsigned char>(address >> 16),
|
||||
static_cast<unsigned char>(address >> 24),
|
||||
0x03
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -34,6 +34,8 @@
|
||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/SetSoftwareBreakpoints.hpp"
|
||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/ClearAllSoftwareBreakpoints.hpp"
|
||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/ClearSoftwareBreakpoints.hpp"
|
||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/SetHardwareBreakpoint.hpp"
|
||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/ClearHardwareBreakpoint.hpp"
|
||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/EnterProgrammingMode.hpp"
|
||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/LeaveProgrammingMode.hpp"
|
||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/EraseMemory.hpp"
|
||||
@@ -58,6 +60,8 @@ namespace DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
||||
using CommandFrames::Avr8Generic::SetSoftwareBreakpoints;
|
||||
using CommandFrames::Avr8Generic::ClearSoftwareBreakpoints;
|
||||
using CommandFrames::Avr8Generic::ClearAllSoftwareBreakpoints;
|
||||
using CommandFrames::Avr8Generic::SetHardwareBreakpoint;
|
||||
using CommandFrames::Avr8Generic::ClearHardwareBreakpoint;
|
||||
using CommandFrames::Avr8Generic::ReadMemory;
|
||||
using CommandFrames::Avr8Generic::EnterProgrammingMode;
|
||||
using CommandFrames::Avr8Generic::LeaveProgrammingMode;
|
||||
@@ -346,7 +350,7 @@ namespace DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
||||
return responseFrame.extractSignature(this->targetConfig.physicalInterface);
|
||||
}
|
||||
|
||||
void EdbgAvr8Interface::setBreakpoint(TargetMemoryAddress address) {
|
||||
void EdbgAvr8Interface::setSoftwareBreakpoint(TargetMemoryAddress address) {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
SetSoftwareBreakpoints({address})
|
||||
);
|
||||
@@ -356,7 +360,7 @@ namespace DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
||||
}
|
||||
}
|
||||
|
||||
void EdbgAvr8Interface::clearBreakpoint(TargetMemoryAddress address) {
|
||||
void EdbgAvr8Interface::clearSoftwareBreakpoint(TargetMemoryAddress address) {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
ClearSoftwareBreakpoints({address})
|
||||
);
|
||||
@@ -366,6 +370,55 @@ namespace DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
||||
}
|
||||
}
|
||||
|
||||
void EdbgAvr8Interface::setHardwareBreakpoint(TargetMemoryAddress address) {
|
||||
static const auto getAvailableBreakpointNumbers = [this] () {
|
||||
auto breakpointNumbers = std::set<std::uint8_t>({1, 2, 3});
|
||||
|
||||
for (const auto& [address, allocatedNumber] : this->hardwareBreakpointNumbersByAddress) {
|
||||
breakpointNumbers.erase(allocatedNumber);
|
||||
}
|
||||
|
||||
return breakpointNumbers;
|
||||
};
|
||||
|
||||
const auto availableBreakpointNumbers = getAvailableBreakpointNumbers();
|
||||
|
||||
if (availableBreakpointNumbers.empty()) {
|
||||
throw Exception("Maximum hardware breakpoints have been allocated");
|
||||
}
|
||||
|
||||
const auto breakpointNumber = *(availableBreakpointNumbers.begin());
|
||||
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
SetHardwareBreakpoint(address, breakpointNumber)
|
||||
);
|
||||
|
||||
if (responseFrame.id == Avr8ResponseId::FAILED) {
|
||||
throw Avr8CommandFailure("AVR8 Set hardware breakpoint command failed", responseFrame);
|
||||
}
|
||||
|
||||
this->hardwareBreakpointNumbersByAddress.insert(std::pair(address, breakpointNumber));
|
||||
}
|
||||
|
||||
void EdbgAvr8Interface::clearHardwareBreakpoint(TargetMemoryAddress address) {
|
||||
const auto breakpointNumberIt = this->hardwareBreakpointNumbersByAddress.find(address);
|
||||
|
||||
if (breakpointNumberIt == this->hardwareBreakpointNumbersByAddress.end()) {
|
||||
Logger::error("No hardware breakpoint at byte address 0x" + Services::StringService::toHex(address));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
ClearHardwareBreakpoint(breakpointNumberIt->second)
|
||||
);
|
||||
|
||||
if (responseFrame.id == Avr8ResponseId::FAILED) {
|
||||
throw Avr8CommandFailure("AVR8 Clear hardware breakpoint command failed", responseFrame);
|
||||
}
|
||||
|
||||
this->hardwareBreakpointNumbersByAddress.erase(address);
|
||||
}
|
||||
|
||||
void EdbgAvr8Interface::clearAllBreakpoints() {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
ClearAllSoftwareBreakpoints()
|
||||
|
||||
@@ -164,18 +164,36 @@ namespace DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
||||
* byte address.
|
||||
*
|
||||
* @param address
|
||||
* The byte address to position the breakpoint.
|
||||
* The byte address to place the breakpoint.
|
||||
*/
|
||||
void setBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
void setSoftwareBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
|
||||
/**
|
||||
* Issues the "Software Breakpoint Clear" command to the debug tool, clearing any breakpoint at the given
|
||||
* byte address.
|
||||
* Issues the "Software Breakpoint Clear" command to the debug tool, clearing any software breakpoint at the
|
||||
* given byte address.
|
||||
*
|
||||
* @param address
|
||||
* The byte address of the breakpoint to clear.
|
||||
*/
|
||||
void clearBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
void clearSoftwareBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
|
||||
/**
|
||||
* Issues the "Hardware Breakpoint Set" command to the debug tool, setting a hardware breakpoint at the given
|
||||
* byte address.
|
||||
*
|
||||
* @param address
|
||||
* The byte address to place the breakpoint.
|
||||
*/
|
||||
void setHardwareBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
|
||||
/**
|
||||
* Issues the "Hardware Breakpoint Clear" command to the debug tool, clearing any hardware breakpoint at the
|
||||
* given byte address.
|
||||
*
|
||||
* @param address
|
||||
* The byte address of the breakpoint to clear.
|
||||
*/
|
||||
void clearHardwareBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
|
||||
/**
|
||||
* Issues the "Software Breakpoint Clear All" command to the debug tool, clearing all software breakpoints
|
||||
@@ -345,6 +363,12 @@ namespace DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
||||
|
||||
bool programmingModeEnabled = false;
|
||||
|
||||
/**
|
||||
* Every hardware breakpoint is assigned a "breakpoint number", which we need to keep track of in order to
|
||||
* clear a hardware breakpoint.
|
||||
*/
|
||||
std::map<Targets::TargetMemoryAddress, std::uint8_t> hardwareBreakpointNumbersByAddress;
|
||||
|
||||
/**
|
||||
* Sends the necessary target parameters to the debug tool.
|
||||
*
|
||||
|
||||
@@ -100,14 +100,28 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8
|
||||
*
|
||||
* @param address
|
||||
*/
|
||||
virtual void setBreakpoint(Targets::TargetMemoryAddress address) = 0;
|
||||
virtual void setSoftwareBreakpoint(Targets::TargetMemoryAddress address) = 0;
|
||||
|
||||
/**
|
||||
* Should remove a software breakpoint at a given address.
|
||||
*
|
||||
* @param address
|
||||
*/
|
||||
virtual void clearBreakpoint(Targets::TargetMemoryAddress address) = 0;
|
||||
virtual void clearSoftwareBreakpoint(Targets::TargetMemoryAddress address) = 0;
|
||||
|
||||
/**
|
||||
* Should set a hardware breakpoint at a given address.
|
||||
*
|
||||
* @param address
|
||||
*/
|
||||
virtual void setHardwareBreakpoint(Targets::TargetMemoryAddress address) = 0;
|
||||
|
||||
/**
|
||||
* Should remove a hardware breakpoint at a given address.
|
||||
*
|
||||
* @param address
|
||||
*/
|
||||
virtual void clearHardwareBreakpoint(Targets::TargetMemoryAddress address) = 0;
|
||||
|
||||
/**
|
||||
* Should remove all software and hardware breakpoints on the target.
|
||||
|
||||
@@ -134,6 +134,10 @@ TargetConfig::TargetConfig(const YAML::Node& targetNode) {
|
||||
this->variantName = StringService::asciiToLower(targetNode["variantName"].as<std::string>());
|
||||
}
|
||||
|
||||
if (targetNode["hardwareBreakpoints"]) {
|
||||
this->hardwareBreakpoints = targetNode["hardwareBreakpoints"].as<bool>(this->hardwareBreakpoints);
|
||||
}
|
||||
|
||||
this->targetNode = targetNode;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,11 @@ struct TargetConfig
|
||||
*/
|
||||
std::optional<std::string> variantName;
|
||||
|
||||
/**
|
||||
* Determines whether Bloom will make use of the target's hardware breakpoint resources (if available).
|
||||
*/
|
||||
bool hardwareBreakpoints = true;
|
||||
|
||||
/**
|
||||
* For extracting any target specific configuration. See Avr8TargetConfig::Avr8TargetConfig() and
|
||||
* Avr8::preActivationConfigure() for an example of this.
|
||||
|
||||
@@ -218,12 +218,15 @@ namespace Services
|
||||
);
|
||||
}
|
||||
|
||||
void TargetControllerService::setBreakpoint(TargetBreakpoint breakpoint) const {
|
||||
this->commandManager.sendCommandAndWaitForResponse(
|
||||
std::make_unique<SetBreakpoint>(breakpoint),
|
||||
Targets::TargetBreakpoint TargetControllerService::setBreakpoint(
|
||||
Targets::TargetMemoryAddress address,
|
||||
Targets::TargetBreakpoint::Type preferredType
|
||||
) const {
|
||||
return this->commandManager.sendCommandAndWaitForResponse(
|
||||
std::make_unique<SetBreakpoint>(address, preferredType),
|
||||
this->defaultTimeout,
|
||||
this->activeAtomicSessionId
|
||||
);
|
||||
)->breakpoint;
|
||||
}
|
||||
|
||||
void TargetControllerService::removeBreakpoint(TargetBreakpoint breakpoint) const {
|
||||
|
||||
@@ -142,9 +142,16 @@ namespace Services
|
||||
/**
|
||||
* Requests the TargetController to set a breakpoint on the target.
|
||||
*
|
||||
* @param breakpoint
|
||||
* @param address
|
||||
* @param preferredType
|
||||
*
|
||||
* @return
|
||||
* The installed breakpoint.
|
||||
*/
|
||||
void setBreakpoint(Targets::TargetBreakpoint breakpoint) const;
|
||||
Targets::TargetBreakpoint setBreakpoint(
|
||||
Targets::TargetMemoryAddress address,
|
||||
Targets::TargetBreakpoint::Type preferredType = Targets::TargetBreakpoint::Type::HARDWARE
|
||||
) const;
|
||||
|
||||
/**
|
||||
* Requests the TargetController to remove a breakpoint from the target.
|
||||
|
||||
@@ -1,22 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "Command.hpp"
|
||||
#include "src/TargetController/Responses/Breakpoint.hpp"
|
||||
|
||||
#include "src/Targets/TargetBreakpoint.hpp"
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
namespace TargetController::Commands
|
||||
{
|
||||
class SetBreakpoint: public Command
|
||||
{
|
||||
public:
|
||||
using SuccessResponseType = Responses::Breakpoint;
|
||||
|
||||
static constexpr CommandType type = CommandType::SET_BREAKPOINT;
|
||||
static const inline std::string name = "SetBreakpoint";
|
||||
|
||||
Targets::TargetBreakpoint breakpoint;
|
||||
/**
|
||||
* Byte address in program memory.
|
||||
*/
|
||||
Targets::TargetMemoryAddress address;
|
||||
|
||||
SetBreakpoint() = default;
|
||||
explicit SetBreakpoint(const Targets::TargetBreakpoint& breakpoint)
|
||||
: breakpoint(breakpoint)
|
||||
/**
|
||||
* The preferred breakpoint type (HARDWARE/SOFTWARE).
|
||||
*
|
||||
* There is no guarantee that the TC will be able to allocate resources for the preferred type.
|
||||
* If the preferredType is set to HARDWARE, but the target doesn't have any available resources, or hardware
|
||||
* breakpoints have been disabled (@see TargetConfig::hardwareBreakpoints), the TC will fall back to software
|
||||
* breakpoints.
|
||||
*/
|
||||
Targets::TargetBreakpoint::Type preferredType;
|
||||
|
||||
SetBreakpoint(Targets::TargetMemoryAddress address, Targets::TargetBreakpoint::Type preferredType)
|
||||
: address(address)
|
||||
, preferredType(preferredType)
|
||||
{};
|
||||
|
||||
[[nodiscard]] CommandType getType() const override {
|
||||
|
||||
24
src/TargetController/Responses/Breakpoint.hpp
Normal file
24
src/TargetController/Responses/Breakpoint.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Response.hpp"
|
||||
|
||||
#include "src/Targets/TargetBreakpoint.hpp"
|
||||
|
||||
namespace TargetController::Responses
|
||||
{
|
||||
class Breakpoint: public Response
|
||||
{
|
||||
public:
|
||||
static constexpr ResponseType type = ResponseType::BREAKPOINT;
|
||||
|
||||
Targets::TargetBreakpoint breakpoint;
|
||||
|
||||
explicit Breakpoint(const Targets::TargetBreakpoint& breakpoint)
|
||||
: breakpoint(breakpoint)
|
||||
{}
|
||||
|
||||
[[nodiscard]] ResponseType getType() const override {
|
||||
return Breakpoint::type;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -16,5 +16,6 @@ namespace TargetController::Responses
|
||||
TARGET_PIN_STATES,
|
||||
TARGET_STACK_POINTER,
|
||||
TARGET_PROGRAM_COUNTER,
|
||||
BREAKPOINT,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Responses/Error.hpp"
|
||||
|
||||
#include "src/Services/ProcessService.hpp"
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
#include "src/Exceptions/InvalidConfig.hpp"
|
||||
@@ -51,6 +52,7 @@ namespace TargetController
|
||||
using Responses::TargetPinStates;
|
||||
using Responses::TargetStackPointer;
|
||||
using Responses::TargetProgramCounter;
|
||||
using Responses::Breakpoint;
|
||||
|
||||
TargetControllerComponent::TargetControllerComponent(
|
||||
const ProjectConfig& projectConfig,
|
||||
@@ -504,6 +506,17 @@ namespace TargetController
|
||||
|
||||
Logger::info("Target ID: " + targetDescriptor.id);
|
||||
Logger::info("Target name: " + targetDescriptor.name);
|
||||
|
||||
if (!this->environmentConfig.targetConfig.hardwareBreakpoints) {
|
||||
Logger::warning("Hardware breakpoints have been disabled");
|
||||
|
||||
} else if (targetDescriptor.breakpointResources.maximumHardwareBreakpoints.has_value()) {
|
||||
Logger::info(
|
||||
"Available hardware breakpoints: " + std::to_string(
|
||||
*(targetDescriptor.breakpointResources.maximumHardwareBreakpoints)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void TargetControllerComponent::releaseHardware() {
|
||||
@@ -877,13 +890,76 @@ namespace TargetController
|
||||
return std::make_unique<Response>();
|
||||
}
|
||||
|
||||
std::unique_ptr<Response> TargetControllerComponent::handleSetBreakpoint(SetBreakpoint& command) {
|
||||
this->target->setBreakpoint(command.breakpoint.address);
|
||||
return std::make_unique<Response>();
|
||||
std::unique_ptr<Breakpoint> TargetControllerComponent::handleSetBreakpoint(SetBreakpoint& command) {
|
||||
using Targets::TargetBreakpoint;
|
||||
using Services::StringService;
|
||||
|
||||
auto breakpoint = TargetBreakpoint(command.address, TargetBreakpoint::Type::SOFTWARE);
|
||||
|
||||
const auto& targetBreakpointResources = this->getTargetDescriptor().breakpointResources;
|
||||
if (
|
||||
command.preferredType == Targets::TargetBreakpoint::Type::HARDWARE
|
||||
&& this->environmentConfig.targetConfig.hardwareBreakpoints
|
||||
) {
|
||||
static auto exhaustedResourcesWarning = false;
|
||||
|
||||
if (
|
||||
!targetBreakpointResources.maximumHardwareBreakpoints.has_value()
|
||||
|| this->hardwareBreakpointsByAddress.size() < *(targetBreakpointResources.maximumHardwareBreakpoints)
|
||||
) {
|
||||
exhaustedResourcesWarning = true;
|
||||
|
||||
Logger::debug(
|
||||
"Installing hardware breakpoint at byte address 0x" + StringService::toHex(command.address)
|
||||
);
|
||||
|
||||
this->target->setHardwareBreakpoint(command.address);
|
||||
this->hardwareBreakpointsByAddress.insert(std::pair(command.address, breakpoint));
|
||||
|
||||
breakpoint.type = TargetBreakpoint::Type::HARDWARE;
|
||||
return std::make_unique<Breakpoint>(breakpoint);
|
||||
}
|
||||
|
||||
if (exhaustedResourcesWarning) {
|
||||
exhaustedResourcesWarning = false;
|
||||
Logger::warning(
|
||||
"Hardware breakpoint resources have been exhausted. Falling back to software breakpoints"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Logger::debug(
|
||||
"Installing software breakpoint at byte address 0x" + StringService::toHex(command.address)
|
||||
);
|
||||
|
||||
this->target->setSoftwareBreakpoint(command.address);
|
||||
this->softwareBreakpointsByAddress.insert(std::pair(command.address, breakpoint));
|
||||
|
||||
return std::make_unique<Breakpoint>(breakpoint);
|
||||
}
|
||||
|
||||
std::unique_ptr<Response> TargetControllerComponent::handleRemoveBreakpoint(RemoveBreakpoint& command) {
|
||||
this->target->removeBreakpoint(command.breakpoint.address);
|
||||
using Services::StringService;
|
||||
|
||||
if (command.breakpoint.type == Targets::TargetBreakpoint::Type::HARDWARE) {
|
||||
assert(this->environmentConfig.targetConfig.hardwareBreakpoints);
|
||||
|
||||
Logger::debug(
|
||||
"Removing hardware breakpoint at byte address 0x" + StringService::toHex(command.breakpoint.address)
|
||||
);
|
||||
|
||||
this->target->removeHardwareBreakpoint(command.breakpoint.address);
|
||||
this->hardwareBreakpointsByAddress.erase(command.breakpoint.address);
|
||||
|
||||
} else {
|
||||
Logger::debug(
|
||||
"Removing software breakpoint at byte address 0x" + StringService::toHex(command.breakpoint.address)
|
||||
);
|
||||
|
||||
this->target->removeSoftwareBreakpoint(command.breakpoint.address);
|
||||
this->softwareBreakpointsByAddress.erase(command.breakpoint.address);
|
||||
}
|
||||
|
||||
return std::make_unique<Response>();
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
#include "Responses/TargetPinStates.hpp"
|
||||
#include "Responses/TargetStackPointer.hpp"
|
||||
#include "Responses/TargetProgramCounter.hpp"
|
||||
#include "Responses/Breakpoint.hpp"
|
||||
|
||||
#include "src/DebugToolDrivers/DebugTools.hpp"
|
||||
#include "src/Targets/Target.hpp"
|
||||
@@ -161,6 +162,12 @@ namespace TargetController
|
||||
*/
|
||||
std::map<Targets::TargetMemoryType, Targets::TargetMemoryAddressRange> registerAddressRangeByMemoryType;
|
||||
|
||||
/**
|
||||
* The TargetController keeps track of all installed breakpoints.
|
||||
*/
|
||||
std::map<Targets::TargetMemoryAddress, Targets::TargetBreakpoint> softwareBreakpointsByAddress;
|
||||
std::map<Targets::TargetMemoryAddress, Targets::TargetBreakpoint> hardwareBreakpointsByAddress;
|
||||
|
||||
/**
|
||||
* Registers a handler function for a particular command type.
|
||||
* Only one handler function can be registered per command type.
|
||||
@@ -339,7 +346,7 @@ namespace TargetController
|
||||
std::unique_ptr<Responses::Response> handleWriteTargetMemory(Commands::WriteTargetMemory& command);
|
||||
std::unique_ptr<Responses::Response> handleEraseTargetMemory(Commands::EraseTargetMemory& command);
|
||||
std::unique_ptr<Responses::Response> handleStepTargetExecution(Commands::StepTargetExecution& command);
|
||||
std::unique_ptr<Responses::Response> handleSetBreakpoint(Commands::SetBreakpoint& command);
|
||||
std::unique_ptr<Responses::Breakpoint> handleSetBreakpoint(Commands::SetBreakpoint& command);
|
||||
std::unique_ptr<Responses::Response> handleRemoveBreakpoint(Commands::RemoveBreakpoint& command);
|
||||
std::unique_ptr<Responses::Response> handleSetProgramCounter(Commands::SetTargetProgramCounter& command);
|
||||
std::unique_ptr<Responses::TargetPinStates> handleGetTargetPinStates(Commands::GetTargetPinStates& command);
|
||||
|
||||
@@ -268,6 +268,7 @@ namespace Targets::Microchip::Avr::Avr8Bit
|
||||
"Microchip",
|
||||
this->targetMemoryDescriptorsByType,
|
||||
this->targetRegisterDescriptorsById,
|
||||
this->getBreakpointResources(),
|
||||
{},
|
||||
Targets::TargetMemoryType::FLASH
|
||||
);
|
||||
@@ -304,12 +305,20 @@ namespace Targets::Microchip::Avr::Avr8Bit
|
||||
this->avr8DebugInterface->reset();
|
||||
}
|
||||
|
||||
void Avr8::setBreakpoint(std::uint32_t address) {
|
||||
this->avr8DebugInterface->setBreakpoint(address);
|
||||
void Avr8::setSoftwareBreakpoint(TargetMemoryAddress address) {
|
||||
this->avr8DebugInterface->setSoftwareBreakpoint(address);
|
||||
}
|
||||
|
||||
void Avr8::removeBreakpoint(std::uint32_t address) {
|
||||
this->avr8DebugInterface->clearBreakpoint(address);
|
||||
void Avr8::removeSoftwareBreakpoint(TargetMemoryAddress address) {
|
||||
this->avr8DebugInterface->clearSoftwareBreakpoint(address);
|
||||
}
|
||||
|
||||
void Avr8::setHardwareBreakpoint(TargetMemoryAddress address) {
|
||||
this->avr8DebugInterface->setHardwareBreakpoint(address);
|
||||
}
|
||||
|
||||
void Avr8::removeHardwareBreakpoint(TargetMemoryAddress address) {
|
||||
this->avr8DebugInterface->clearHardwareBreakpoint(address);
|
||||
}
|
||||
|
||||
void Avr8::clearAllBreakpoints() {
|
||||
@@ -687,6 +696,33 @@ namespace Targets::Microchip::Avr::Avr8Bit
|
||||
}
|
||||
}
|
||||
|
||||
BreakpointResources Avr8::getBreakpointResources() {
|
||||
auto maxHardwareBreakpoints = 0;
|
||||
|
||||
switch (this->targetConfig.physicalInterface) {
|
||||
case PhysicalInterface::JTAG: {
|
||||
maxHardwareBreakpoints = this->family == Family::XMEGA ? 2 : 3;
|
||||
break;
|
||||
}
|
||||
case PhysicalInterface::PDI: {
|
||||
maxHardwareBreakpoints = 2;
|
||||
break;
|
||||
}
|
||||
case PhysicalInterface::UPDI: {
|
||||
maxHardwareBreakpoints = 1;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return BreakpointResources(
|
||||
maxHardwareBreakpoints,
|
||||
std::nullopt
|
||||
);
|
||||
}
|
||||
|
||||
bool Avr8::isFuseEnabled(const FuseBitsDescriptor& descriptor, unsigned char fuseByteValue) const {
|
||||
const auto programmedValue = static_cast<unsigned char>(
|
||||
this->fuseEnableStrategy == FuseEnableStrategy::SET
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "src/Targets/Microchip/AVR/Fuse.hpp"
|
||||
#include "src/Targets/TargetRegister.hpp"
|
||||
#include "src/Targets/TargetBreakpoint.hpp"
|
||||
|
||||
#include "TargetDescription/TargetDescriptionFile.hpp"
|
||||
|
||||
@@ -58,8 +59,11 @@ namespace Targets::Microchip::Avr::Avr8Bit
|
||||
void step() override;
|
||||
void reset() override;
|
||||
|
||||
void setBreakpoint(TargetProgramCounter address) override;
|
||||
void removeBreakpoint(TargetProgramCounter address) override;
|
||||
void setSoftwareBreakpoint(TargetProgramCounter address) override;
|
||||
void removeSoftwareBreakpoint(TargetProgramCounter address) override;
|
||||
|
||||
void setHardwareBreakpoint(TargetProgramCounter address) override;
|
||||
void removeHardwareBreakpoint(TargetProgramCounter address) override;
|
||||
void clearAllBreakpoints() override;
|
||||
|
||||
void writeRegisters(TargetRegisters registers) override;
|
||||
@@ -143,6 +147,8 @@ namespace Targets::Microchip::Avr::Avr8Bit
|
||||
|
||||
void loadTargetMemoryDescriptors();
|
||||
|
||||
BreakpointResources getBreakpointResources();
|
||||
|
||||
/**
|
||||
* Checks if a particular fuse is enabled in the given fuse byte value. Takes the target's fuse enable strategy
|
||||
* into account.
|
||||
|
||||
@@ -111,18 +111,32 @@ namespace Targets
|
||||
virtual void reset() = 0;
|
||||
|
||||
/**
|
||||
* Should set a breakpoint on the target, at the given address.
|
||||
* Should set a software breakpoint on the target, at the given address.
|
||||
*
|
||||
* @param address
|
||||
*/
|
||||
virtual void setBreakpoint(TargetMemoryAddress address) = 0;
|
||||
virtual void setSoftwareBreakpoint(TargetMemoryAddress address) = 0;
|
||||
|
||||
/**
|
||||
* Should remove a breakpoint at the given address.
|
||||
* Should remove a software breakpoint at the given address.
|
||||
*
|
||||
* @param address
|
||||
*/
|
||||
virtual void removeBreakpoint(TargetMemoryAddress address) = 0;
|
||||
virtual void removeSoftwareBreakpoint(TargetMemoryAddress address) = 0;
|
||||
|
||||
/**
|
||||
* Should set a hardware breakpoint on the target, at the given address.
|
||||
*
|
||||
* @param address
|
||||
*/
|
||||
virtual void setHardwareBreakpoint(TargetMemoryAddress address) = 0;
|
||||
|
||||
/**
|
||||
* Should remove a hardware breakpoint at the given address.
|
||||
*
|
||||
* @param address
|
||||
*/
|
||||
virtual void removeHardwareBreakpoint(TargetMemoryAddress address) = 0;
|
||||
|
||||
/**
|
||||
* Should clear all breakpoints on the target.
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#include "TargetMemory.hpp"
|
||||
|
||||
namespace Targets
|
||||
{
|
||||
enum class TargetBreakCause: int
|
||||
enum class TargetBreakCause: std::uint8_t
|
||||
{
|
||||
BREAKPOINT,
|
||||
UNKNOWN,
|
||||
@@ -14,12 +15,38 @@ namespace Targets
|
||||
|
||||
struct TargetBreakpoint
|
||||
{
|
||||
enum class Type: std::uint8_t
|
||||
{
|
||||
HARDWARE,
|
||||
SOFTWARE,
|
||||
};
|
||||
|
||||
/**
|
||||
* Byte address of the breakpoint.
|
||||
*/
|
||||
TargetMemoryAddress address = 0;
|
||||
|
||||
Type type = Type::SOFTWARE;
|
||||
|
||||
TargetBreakpoint() = default;
|
||||
explicit TargetBreakpoint(TargetMemoryAddress address): address(address) {};
|
||||
|
||||
explicit TargetBreakpoint(TargetMemoryAddress address, Type type = Type::SOFTWARE)
|
||||
: address(address)
|
||||
, type(type)
|
||||
{};
|
||||
};
|
||||
|
||||
struct BreakpointResources
|
||||
{
|
||||
std::optional<std::uint16_t> maximumHardwareBreakpoints;
|
||||
std::optional<std::uint16_t> maximumSoftwareBreakpoints;
|
||||
|
||||
BreakpointResources(
|
||||
std::optional<std::uint16_t> maximumHardwareBreakpoints,
|
||||
std::optional<std::uint16_t> maximumSoftwareBreakpoints
|
||||
)
|
||||
: maximumHardwareBreakpoints(maximumHardwareBreakpoints)
|
||||
, maximumSoftwareBreakpoints(maximumSoftwareBreakpoints)
|
||||
{}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "TargetMemory.hpp"
|
||||
#include "TargetRegister.hpp"
|
||||
#include "TargetVariant.hpp"
|
||||
#include "TargetBreakpoint.hpp"
|
||||
|
||||
namespace Targets
|
||||
{
|
||||
@@ -20,6 +21,7 @@ namespace Targets
|
||||
std::string vendorName;
|
||||
std::map<TargetMemoryType, TargetMemoryDescriptor> memoryDescriptorsByType;
|
||||
std::map<TargetRegisterDescriptorId, TargetRegisterDescriptor> registerDescriptorsById;
|
||||
BreakpointResources breakpointResources;
|
||||
std::vector<TargetVariant> variants;
|
||||
|
||||
TargetMemoryType programMemoryType;
|
||||
@@ -30,6 +32,7 @@ namespace Targets
|
||||
const std::string& vendorName,
|
||||
const std::map<TargetMemoryType, TargetMemoryDescriptor>& memoryDescriptorsByType,
|
||||
const std::map<TargetRegisterDescriptorId, TargetRegisterDescriptor>& registerDescriptorsById,
|
||||
const BreakpointResources& breakpointResources,
|
||||
const std::vector<TargetVariant>& variants,
|
||||
TargetMemoryType programMemoryType
|
||||
)
|
||||
@@ -38,6 +41,7 @@ namespace Targets
|
||||
, vendorName(vendorName)
|
||||
, memoryDescriptorsByType(memoryDescriptorsByType)
|
||||
, registerDescriptorsById(registerDescriptorsById)
|
||||
, breakpointResources(breakpointResources)
|
||||
, variants(variants)
|
||||
, programMemoryType(programMemoryType)
|
||||
{}
|
||||
|
||||
Reference in New Issue
Block a user