Support for hardware breakpoints
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user