Support for hardware breakpoints

This commit is contained in:
Nav
2023-09-20 23:37:54 +01:00
parent df5a141089
commit d7b59cac59
24 changed files with 480 additions and 68 deletions

View File

@@ -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 {

View 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;
}
};
}

View File

@@ -16,5 +16,6 @@ namespace TargetController::Responses
TARGET_PIN_STATES,
TARGET_STACK_POINTER,
TARGET_PROGRAM_COUNTER,
BREAKPOINT,
};
}

View File

@@ -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>();
}

View File

@@ -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);