Massive refactor to accommodate RISC-V targets
- Refactored entire codebase (excluding the Insight component) to accommodate multiple target architectures (no longer specific to AVR) - Deleted 'generate SVD' GDB monitor command - I will eventually move this functionality to the Bloom website - Added unit size property to address spaces - Many other changes which I couldn't be bothered to describe here
This commit is contained in:
@@ -22,19 +22,24 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
: Monitor(std::move(monitorPacket))
|
||||
{}
|
||||
|
||||
void ActivateInsight::handle(DebugSession& debugSession, TargetControllerService&) {
|
||||
void ActivateInsight::handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor&,
|
||||
const Targets::TargetDescriptor&,
|
||||
TargetControllerService&
|
||||
) {
|
||||
Logger::info("Handling ActivateInsight packet");
|
||||
|
||||
try {
|
||||
EventManager::triggerEvent(std::make_shared<Events::InsightActivationRequested>());
|
||||
|
||||
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(
|
||||
"The Insight GUI will be with you shortly.\n"
|
||||
)));
|
||||
debugSession.connection.writePacket(
|
||||
ResponsePacket{Services::StringService::toHex("The Insight GUI will be with you shortly.\n")}
|
||||
);
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to activate Insight - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -22,15 +22,20 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
: Monitor(std::move(monitorPacket))
|
||||
{}
|
||||
|
||||
void BloomVersion::handle(DebugSession& debugSession, TargetControllerService&) {
|
||||
void BloomVersion::handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor&,
|
||||
const Targets::TargetDescriptor&,
|
||||
TargetControllerService&
|
||||
) {
|
||||
Logger::info("Handling BloomVersion packet");
|
||||
|
||||
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(
|
||||
std::string(
|
||||
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(
|
||||
std::string{
|
||||
"Bloom v" + Application::VERSION.toString() + "\n"
|
||||
+ Services::PathService::homeDomainName() + "\n"
|
||||
+ "Nav Mohammed\n"
|
||||
)
|
||||
)));
|
||||
}
|
||||
)});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -21,18 +21,28 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
: Monitor(std::move(monitorPacket))
|
||||
{}
|
||||
|
||||
void BloomVersionMachine::handle(DebugSession& debugSession, TargetControllerService&) {
|
||||
void BloomVersionMachine::handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor&,
|
||||
const Targets::TargetDescriptor&,
|
||||
TargetControllerService&
|
||||
) {
|
||||
Logger::info("Handling BloomVersionMachine packet");
|
||||
|
||||
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(
|
||||
QJsonDocument(QJsonObject({
|
||||
{"version", QString::fromStdString(Application::VERSION.toString())},
|
||||
{"components", QJsonObject({
|
||||
{"major", Application::VERSION.major},
|
||||
{"minor", Application::VERSION.minor},
|
||||
{"patch", Application::VERSION.patch},
|
||||
})},
|
||||
})).toJson().toStdString()
|
||||
)));
|
||||
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(
|
||||
QJsonDocument{
|
||||
QJsonObject{
|
||||
{"version", QString::fromStdString(Application::VERSION.toString())},
|
||||
{
|
||||
"components",
|
||||
QJsonObject{
|
||||
{"major", Application::VERSION.major},
|
||||
{"minor", Application::VERSION.minor},
|
||||
{"patch", Application::VERSION.patch},
|
||||
}
|
||||
},
|
||||
}
|
||||
}.toJson().toStdString()
|
||||
)});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "src/DebugServer/Gdb/Signal.hpp"
|
||||
|
||||
#include "src/DebugServer/Gdb/Exceptions/ClientCommunicationError.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::CommandPackets
|
||||
@@ -20,36 +21,49 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
using ResponsePackets::EmptyResponsePacket;
|
||||
using ResponsePackets::ErrorResponsePacket;
|
||||
|
||||
void CommandPacket::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
const auto packetString = std::string(this->data.begin(), this->data.end());
|
||||
CommandPacket::CommandPacket(const RawPacket& rawPacket) {
|
||||
if (rawPacket.size() < 5) {
|
||||
throw Exceptions::ClientCommunicationError{"Invalid raw packet size"};
|
||||
}
|
||||
|
||||
this->data.insert(this->data.begin(), rawPacket.begin() + 1, rawPacket.end() - 3);
|
||||
}
|
||||
|
||||
void CommandPacket::handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor&,
|
||||
const Targets::TargetDescriptor&,
|
||||
TargetControllerService&
|
||||
) {
|
||||
const auto packetString = std::string{this->data.begin(), this->data.end()};
|
||||
|
||||
if (packetString.empty()) {
|
||||
Logger::error("Empty GDB RSP packet received.");
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
return;
|
||||
}
|
||||
|
||||
if (packetString[0] == '?') {
|
||||
// Status report
|
||||
debugSession.connection.writePacket(TargetStopped(Signal::TRAP));
|
||||
debugSession.connection.writePacket(TargetStopped{Signal::TRAP});
|
||||
return;
|
||||
}
|
||||
|
||||
if (packetString.find("vMustReplyEmpty") == 0) {
|
||||
Logger::info("Handling vMustReplyEmpty");
|
||||
debugSession.connection.writePacket(EmptyResponsePacket());
|
||||
debugSession.connection.writePacket(EmptyResponsePacket{});
|
||||
return;
|
||||
}
|
||||
|
||||
if (packetString.find("qAttached") == 0) {
|
||||
Logger::info("Handling qAttached");
|
||||
debugSession.connection.writePacket(ResponsePacket(std::vector<unsigned char>({1})));
|
||||
debugSession.connection.writePacket(ResponsePacket{std::vector<unsigned char>({1})});
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::debug("Unknown GDB RSP packet: " + packetString + " - returning empty response");
|
||||
|
||||
// Respond with an empty packet
|
||||
debugSession.connection.writePacket(EmptyResponsePacket());
|
||||
// GDB expects an empty response for all unsupported commands
|
||||
debugSession.connection.writePacket(EmptyResponsePacket{});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "src/DebugServer/Gdb/Packet.hpp"
|
||||
#include "src/DebugServer/Gdb/DebugSession.hpp"
|
||||
|
||||
#include "src/DebugServer/Gdb/TargetDescriptor.hpp"
|
||||
#include "src/Targets/TargetDescriptor.hpp"
|
||||
#include "src/Services/TargetControllerService.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::CommandPackets
|
||||
{
|
||||
/**
|
||||
* GDB RSP command packets are sent to the server, from the GDB client. These packets carry instructions that the
|
||||
* server is expected to carry out. Upon completion, the server is expected to respond to the client with
|
||||
* a ResponsePacket.
|
||||
*
|
||||
* For some command packets, we define a specific data structure by extending this CommandPacket class. These
|
||||
* classes extend the data structure to include fields for data which may be specific to the command.
|
||||
* They also implement additional methods that allow us to easily access the additional data. An example
|
||||
* of this would be the SupportedFeaturesQuery class. It extends the CommandPacket class and provides access
|
||||
* to additional data fields that are specific to the command (in this case, a set of GDB features reported to be
|
||||
* supported by the GDB client).
|
||||
*
|
||||
* Typically, command packets that require specific data structures are handled in a dedicated handler method
|
||||
* in the GdbRspDebugServer. This is done by double dispatching the packet object to the appropriate handler.
|
||||
* See CommandPacket::dispatchToHandler(), GdbRspDebugServer::serve() and the overloads
|
||||
* for GdbRspDebugServer::handleGdbPacket() for more on this.
|
||||
*
|
||||
* Some command packets are so simple they do not require a dedicated data structure. An example of this is
|
||||
* the halt reason packet, which contains nothing more than an ? character in the packet body. These packets are
|
||||
* typically handled in the generic GdbRspDebugServer::handleGdbPacket(CommandPacket&) method.
|
||||
*
|
||||
* See the Packet class for information on how the raw packets are formatted.
|
||||
*/
|
||||
class CommandPacket: public Packet
|
||||
class CommandPacket
|
||||
{
|
||||
public:
|
||||
explicit CommandPacket(const RawPacket& rawPacket)
|
||||
: Packet(rawPacket)
|
||||
{}
|
||||
explicit CommandPacket(const RawPacket& rawPacket);
|
||||
|
||||
/**
|
||||
* Should handle the command for the current active debug session.
|
||||
@@ -50,7 +24,12 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
*/
|
||||
virtual void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
);
|
||||
|
||||
protected:
|
||||
std::vector<unsigned char> data;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
@@ -17,21 +18,34 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
{
|
||||
if (this->data.size() > 2) {
|
||||
this->fromAddress = static_cast<Targets::TargetMemoryAddress>(
|
||||
std::stoi(std::string(this->data.begin() + 2, this->data.end()), nullptr, 16)
|
||||
Services::StringService::toUint32(std::string{this->data.begin() + 2, this->data.end()}, 16)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void ContinueExecution::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void ContinueExecution::handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor&,
|
||||
const Targets::TargetDescriptor&,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling ContinueExecution packet");
|
||||
|
||||
try {
|
||||
targetControllerService.continueTargetExecution(this->fromAddress, std::nullopt);
|
||||
{
|
||||
const auto atomicSession = targetControllerService.makeAtomicSession();
|
||||
if (this->fromAddress.has_value()) {
|
||||
targetControllerService.setProgramCounter(*(this->fromAddress));
|
||||
}
|
||||
|
||||
targetControllerService.resumeTargetExecution();
|
||||
}
|
||||
|
||||
debugSession.waitingForBreak = true;
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to continue execution on target - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -20,7 +20,12 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
: CommandPacket(rawPacket)
|
||||
{}
|
||||
|
||||
void Detach::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void Detach::handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor&,
|
||||
const Targets::TargetDescriptor&,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling Detach packet");
|
||||
|
||||
try {
|
||||
@@ -28,11 +33,11 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
targetControllerService.shutdown();
|
||||
}
|
||||
|
||||
debugSession.connection.writePacket(OkResponsePacket());
|
||||
debugSession.connection.writePacket(OkResponsePacket{});
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to shut down TargetController - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
#include "EepromFill.hpp"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <algorithm>
|
||||
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
#include "src/DebugServer/Gdb/Exceptions/InvalidCommandOption.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::CommandPackets
|
||||
{
|
||||
using Services::TargetControllerService;
|
||||
|
||||
using ResponsePackets::ResponsePacket;
|
||||
using ResponsePackets::ErrorResponsePacket;
|
||||
|
||||
using ::Exceptions::Exception;
|
||||
using Exceptions::InvalidCommandOption;
|
||||
|
||||
EepromFill::EepromFill(Monitor&& monitorPacket)
|
||||
: Monitor(std::move(monitorPacket))
|
||||
{
|
||||
const auto fillValueOptionIt = this->commandOptions.find("value");
|
||||
|
||||
if (fillValueOptionIt == this->commandOptions.end() || !fillValueOptionIt->second.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto fillValueByteArray = QByteArray::fromHex(QByteArray::fromStdString(*fillValueOptionIt->second));
|
||||
this->fillValue = Targets::TargetMemoryBuffer(fillValueByteArray.begin(), fillValueByteArray.end());
|
||||
}
|
||||
|
||||
void EepromFill::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
Logger::info("Handling EepromFill packet");
|
||||
|
||||
try {
|
||||
const auto& targetDescriptor = debugSession.gdbTargetDescriptor.targetDescriptor;
|
||||
|
||||
const auto eepromDescriptorIt = targetDescriptor.memoryDescriptorsByType.find(
|
||||
Targets::TargetMemoryType::EEPROM
|
||||
);
|
||||
|
||||
if (eepromDescriptorIt == targetDescriptor.memoryDescriptorsByType.end()) {
|
||||
throw Exception("Target has no EEPROM");
|
||||
}
|
||||
|
||||
const auto& eepromDescriptor = eepromDescriptorIt->second;
|
||||
const auto eepromSize = eepromDescriptor.size();
|
||||
|
||||
const auto fillValueSize = this->fillValue.size();
|
||||
|
||||
if (fillValueSize == 0) {
|
||||
throw InvalidCommandOption("Fill value required");
|
||||
}
|
||||
|
||||
if (fillValueSize > eepromSize) {
|
||||
throw InvalidCommandOption(
|
||||
"Fill value size ("+ std::to_string(fillValueSize) + " bytes) exceeds EEPROM size ("
|
||||
+ std::to_string(eepromSize) + " bytes)"
|
||||
);
|
||||
}
|
||||
|
||||
if ((eepromSize % fillValueSize) != 0) {
|
||||
Logger::warning(
|
||||
"The fill value size (" + std::to_string(fillValueSize) + " bytes) is not a multiple of the EEPROM "
|
||||
"size (" + std::to_string(eepromSize) + " bytes) - the fill value will be truncated"
|
||||
);
|
||||
}
|
||||
|
||||
Logger::warning("Filling " + std::to_string(eepromSize) + " bytes of EEPROM");
|
||||
|
||||
auto data = Targets::TargetMemoryBuffer();
|
||||
data.reserve(eepromSize);
|
||||
|
||||
// Repeat this->fillValue until we've filled `data`
|
||||
while (data.size() < eepromSize) {
|
||||
data.insert(
|
||||
data.end(),
|
||||
this->fillValue.begin(),
|
||||
this->fillValue.begin() + static_cast<std::int32_t>(
|
||||
std::min(fillValueSize, (eepromSize - data.size()))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const auto hexValues = Services::StringService::toHex(data);
|
||||
Logger::debug("Filling EEPROM with values: " + hexValues);
|
||||
|
||||
targetControllerService.writeMemory(
|
||||
Targets::TargetMemoryType::EEPROM,
|
||||
eepromDescriptor.addressRange.startAddress,
|
||||
std::move(data)
|
||||
);
|
||||
|
||||
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(
|
||||
"Filled " + std::to_string(eepromSize) + " bytes of EEPROM, with values: " + hexValues + "\n"
|
||||
)));
|
||||
|
||||
} catch (const InvalidCommandOption& exception) {
|
||||
Logger::error(exception.getMessage());
|
||||
debugSession.connection.writePacket(
|
||||
ResponsePacket(Services::StringService::toHex(exception.getMessage() + "\n"))
|
||||
);
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to fill EEPROM - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Monitor.hpp"
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::CommandPackets
|
||||
{
|
||||
/**
|
||||
* The EepromFill class implements a structure for the "monitor eeprom fill" GDB command.
|
||||
*
|
||||
* This command fills the target's EEPROM with the given value.
|
||||
*/
|
||||
class EepromFill: public Monitor
|
||||
{
|
||||
public:
|
||||
explicit EepromFill(Monitor&& monitorPacket);
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
|
||||
private:
|
||||
Targets::TargetMemoryBuffer fillValue;
|
||||
};
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
#include "GenerateSvd.hpp"
|
||||
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp"
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
#include "src/Application.hpp"
|
||||
|
||||
#include "src/Services/PathService.hpp"
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::CommandPackets
|
||||
{
|
||||
using Services::TargetControllerService;
|
||||
|
||||
using ResponsePackets::ResponsePacket;
|
||||
using ResponsePackets::ErrorResponsePacket;
|
||||
|
||||
using ::Exceptions::Exception;
|
||||
|
||||
GenerateSvd::GenerateSvd(Monitor&& monitorPacket)
|
||||
: Monitor(std::move(monitorPacket))
|
||||
, sendOutput(this->commandOptions.contains("out"))
|
||||
{}
|
||||
|
||||
void GenerateSvd::handle(DebugSession& debugSession, TargetControllerService&) {
|
||||
Logger::info("Handling GenerateSvd packet");
|
||||
|
||||
try {
|
||||
Logger::info("Generating SVD XML for current target");
|
||||
|
||||
const auto& targetDescriptor = debugSession.gdbTargetDescriptor.targetDescriptor;
|
||||
|
||||
const auto svdXml = this->generateSvd(
|
||||
targetDescriptor,
|
||||
debugSession.gdbTargetDescriptor.getMemoryOffset(Targets::TargetMemoryType::RAM)
|
||||
);
|
||||
|
||||
if (this->sendOutput) {
|
||||
debugSession.connection.writePacket(
|
||||
ResponsePacket(Services::StringService::toHex(svdXml.toString().toStdString()))
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto svdOutputFilePath = Services::PathService::projectDirPath() + "/" + targetDescriptor.name + ".svd";
|
||||
auto outputFile = QFile(QString::fromStdString(svdOutputFilePath));
|
||||
|
||||
if (!outputFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {
|
||||
throw Exception(
|
||||
"Failed to open/create SVD output file (" + svdOutputFilePath + "). Check file permissions."
|
||||
);
|
||||
}
|
||||
|
||||
outputFile.write(svdXml.toByteArray());
|
||||
outputFile.close();
|
||||
|
||||
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(
|
||||
"SVD output saved to " + svdOutputFilePath + "\n"
|
||||
)));
|
||||
|
||||
Logger::info("SVD output saved to " + svdOutputFilePath);
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error(exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
}
|
||||
}
|
||||
|
||||
QDomDocument GenerateSvd::generateSvd(
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
std::uint32_t baseAddressOffset
|
||||
) {
|
||||
auto document = QDomDocument();
|
||||
|
||||
const auto createElement = [&document] (const QString& tagName, const QString& value) {
|
||||
auto element = document.createElement(tagName);
|
||||
auto textNode = document.createTextNode(value);
|
||||
element.appendChild(textNode);
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
document.appendChild(document.createComment(
|
||||
" This SVD was generated by Bloom (https://bloom.oscillate.io/). "
|
||||
"Please report any issues via https://bloom.oscillate.io/report-issue "
|
||||
));
|
||||
|
||||
if (baseAddressOffset != 0) {
|
||||
document.appendChild(document.createComment(
|
||||
" Base addresses in this SVD have been offset by 0x" + QString::number(baseAddressOffset, 16)
|
||||
+ ". This offset is required for access via avr-gdb. "
|
||||
));
|
||||
}
|
||||
|
||||
auto deviceElement = document.createElement("device");
|
||||
|
||||
deviceElement.setAttribute("schemaVersion", "1.3");
|
||||
deviceElement.setAttribute("xmlns:xs", "http://www.w3.org/2001/XMLSchema-instance");
|
||||
deviceElement.setAttribute(
|
||||
"xs:noNamespaceSchemaLocation",
|
||||
QString::fromStdString(Services::PathService::homeDomainName() + "/assets/svd-schema.xsd")
|
||||
);
|
||||
|
||||
deviceElement.appendChild(createElement("vendor", QString::fromStdString(targetDescriptor.vendorName)));
|
||||
deviceElement.appendChild(createElement("name", QString::fromStdString(targetDescriptor.name)));
|
||||
|
||||
deviceElement.appendChild(document.createComment(
|
||||
" The version number below is that of the Bloom binary which generated this SVD. "
|
||||
));
|
||||
deviceElement.appendChild(createElement("version", QString::fromStdString(Application::VERSION.toString())));
|
||||
|
||||
deviceElement.appendChild(
|
||||
createElement(
|
||||
"description",
|
||||
QString::fromStdString(targetDescriptor.name) + " from "
|
||||
+ QString::fromStdString(targetDescriptor.vendorName)
|
||||
)
|
||||
);
|
||||
|
||||
/*
|
||||
* TODO: These values should be part of the TargetDescriptor, but given that Bloom only supports 8-bit AVRs,
|
||||
* it really doesn't matter ATM. Will fix it later (lol no I won't).
|
||||
*/
|
||||
deviceElement.appendChild(createElement("addressUnitBits", "8"));
|
||||
deviceElement.appendChild(createElement("width", "8"));
|
||||
deviceElement.appendChild(createElement("size", "8"));
|
||||
|
||||
deviceElement.appendChild(createElement("access", "read-only"));
|
||||
|
||||
struct Peripheral {
|
||||
QString name;
|
||||
std::uint32_t baseAddress;
|
||||
|
||||
Targets::TargetRegisterDescriptors registerDescriptors;
|
||||
};
|
||||
|
||||
auto peripheralsByName = std::map<std::string, Peripheral>();
|
||||
|
||||
for (const auto& [descriptorId, registerDescriptor] : targetDescriptor.registerDescriptorsById) {
|
||||
if (
|
||||
!registerDescriptor.startAddress.has_value()
|
||||
|| !registerDescriptor.name.has_value()
|
||||
|| registerDescriptor.name->empty()
|
||||
|| !registerDescriptor.groupName.has_value()
|
||||
|| (
|
||||
registerDescriptor.type != Targets::TargetRegisterType::OTHER
|
||||
&& registerDescriptor.type != Targets::TargetRegisterType::PORT_REGISTER
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto peripheralIt = peripheralsByName.find(*registerDescriptor.groupName);
|
||||
|
||||
if (peripheralIt == peripheralsByName.end()) {
|
||||
auto peripheral = Peripheral{
|
||||
.name = QString::fromStdString(
|
||||
*registerDescriptor.groupName
|
||||
).replace(QChar(' '), QChar('_')).toUpper(),
|
||||
.baseAddress = baseAddressOffset
|
||||
};
|
||||
|
||||
peripheralIt = peripheralsByName.insert(std::pair(*registerDescriptor.groupName, peripheral)).first;
|
||||
}
|
||||
|
||||
peripheralIt->second.registerDescriptors.insert(registerDescriptor);
|
||||
}
|
||||
|
||||
auto peripheralsElement = document.createElement("peripherals");
|
||||
|
||||
for (const auto& [peripheralName, peripheral] : peripheralsByName) {
|
||||
auto peripheralElement = document.createElement("peripheral");
|
||||
|
||||
peripheralElement.appendChild(createElement("name", peripheral.name));
|
||||
peripheralElement.appendChild(createElement("baseAddress", "0x" + QString::number(peripheral.baseAddress, 16)));
|
||||
|
||||
auto registersElement = document.createElement("registers");
|
||||
|
||||
for (const auto& registerDescriptor : peripheral.registerDescriptors) {
|
||||
auto registerElement = document.createElement("register");
|
||||
|
||||
registerElement.appendChild(
|
||||
createElement(
|
||||
"name",
|
||||
QString::fromStdString(*registerDescriptor.name).replace(QChar(' '), QChar('_')).toUpper()
|
||||
)
|
||||
);
|
||||
|
||||
if (registerDescriptor.description.has_value()) {
|
||||
registerElement.appendChild(
|
||||
createElement("description", QString::fromStdString(*registerDescriptor.description))
|
||||
);
|
||||
}
|
||||
|
||||
registerElement.appendChild(
|
||||
createElement("addressOffset", "0x" + QString::number(*registerDescriptor.startAddress, 16))
|
||||
);
|
||||
|
||||
registerElement.appendChild(
|
||||
createElement("size", QString::number(registerDescriptor.size * 8))
|
||||
);
|
||||
|
||||
registerElement.appendChild(
|
||||
createElement("access", registerDescriptor.access.writable ? "read-write" : "read-only")
|
||||
);
|
||||
|
||||
registersElement.appendChild(registerElement);
|
||||
}
|
||||
|
||||
peripheralElement.appendChild(registersElement);
|
||||
peripheralsElement.appendChild(peripheralElement);
|
||||
}
|
||||
|
||||
deviceElement.appendChild(peripheralsElement);
|
||||
document.appendChild(deviceElement);
|
||||
return document;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Monitor.hpp"
|
||||
|
||||
#include "src/Targets/TargetDescriptor.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::CommandPackets
|
||||
{
|
||||
/**
|
||||
* The GenerateSvd class implements a structure for the "monitor svd" GDB command.
|
||||
*
|
||||
* This command generates XML conforming to the CMSIS-SVD schema, for the connected target. Will output the XML to
|
||||
* a file or send it to GDB.
|
||||
*/
|
||||
class GenerateSvd: public Monitor
|
||||
{
|
||||
public:
|
||||
explicit GenerateSvd(Monitor&& monitorPacket);
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
|
||||
private:
|
||||
bool sendOutput = false;
|
||||
|
||||
QDomDocument generateSvd(
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
std::uint32_t baseAddressOffset = 0
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -25,7 +25,12 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
: Monitor(std::move(monitorPacket))
|
||||
{}
|
||||
|
||||
void HelpMonitorInfo::handle(DebugSession& debugSession, TargetControllerService&) {
|
||||
void HelpMonitorInfo::handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor&,
|
||||
TargetControllerService&
|
||||
) {
|
||||
Logger::info("Handling HelpMonitorInfo packet");
|
||||
|
||||
try {
|
||||
@@ -33,26 +38,24 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
* The file GdbHelpMonitorInfo.txt is included in the binary image as a resource.
|
||||
* See src/DebugServer/CMakeLists.txt for more.
|
||||
*/
|
||||
auto helpFile = QFile(
|
||||
auto helpFile = QFile{
|
||||
QString::fromStdString(":/compiled/src/DebugServer/Gdb/Resources/GdbHelpMonitorInfo.txt")
|
||||
);
|
||||
};
|
||||
|
||||
if (!helpFile.open(QIODevice::ReadOnly)) {
|
||||
throw Exception(
|
||||
throw Exception{
|
||||
"Failed to open GDB monitor info help file - please report this issue at "
|
||||
+ Services::PathService::homeDomainName() + "/report-issue"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
debugSession.connection.writePacket(
|
||||
ResponsePacket(Services::StringService::toHex(
|
||||
"\n" + QTextStream(&helpFile).readAll().toUtf8().toStdString() + "\n"
|
||||
))
|
||||
);
|
||||
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(
|
||||
"\n" + QTextStream{&helpFile}.readAll().toUtf8().toStdString() + "\n"
|
||||
)});
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error(exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -16,10 +16,15 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
using ::Exceptions::Exception;
|
||||
|
||||
void InterruptExecution::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void InterruptExecution::handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor&,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling InterruptExecution packet");
|
||||
|
||||
if (targetControllerService.getTargetState() == Targets::TargetState::STOPPED) {
|
||||
if (targetControllerService.getTargetState().executionState == Targets::TargetExecutionState::STOPPED) {
|
||||
debugSession.pendingInterrupt = true;
|
||||
return;
|
||||
}
|
||||
@@ -32,7 +37,7 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
}
|
||||
|
||||
debugSession.waitingForBreak = false;
|
||||
debugSession.connection.writePacket(TargetStopped(Signal::INTERRUPTED));
|
||||
debugSession.connection.writePacket(TargetStopped{Signal::INTERRUPTED});
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to interrupt execution - " + exception.getMessage());
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/EmptyResponsePacket.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
namespace DebugServer::Gdb::CommandPackets
|
||||
@@ -17,23 +18,28 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
return;
|
||||
}
|
||||
|
||||
const auto decodedCommand = Packet::hexToData(
|
||||
std::string(this->data.begin() + 6, this->data.end())
|
||||
const auto decodedCommand = Services::StringService::dataFromHex(
|
||||
std::string{this->data.begin() + 6, this->data.end()}
|
||||
);
|
||||
|
||||
this->command = std::string(decodedCommand.begin(), decodedCommand.end());
|
||||
this->command = std::string{decodedCommand.begin(), decodedCommand.end()};
|
||||
this->command.erase(this->command.find_last_not_of(" ") + 1);
|
||||
|
||||
this->commandOptions = this->extractCommandOptions(this->command);
|
||||
}
|
||||
|
||||
void Monitor::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void Monitor::handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor&,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::error("Unknown custom GDB command (\"" + this->command + "\") received.");
|
||||
debugSession.connection.writePacket(EmptyResponsePacket());
|
||||
debugSession.connection.writePacket(EmptyResponsePacket{});
|
||||
}
|
||||
|
||||
std::map<std::string, std::optional<std::string>> Monitor::extractCommandOptions(const std::string& command) {
|
||||
auto output = std::map<std::string, std::optional<std::string>>();
|
||||
auto output = std::map<std::string, std::optional<std::string>>{};
|
||||
|
||||
for (std::string::size_type cmdIndex = 1; cmdIndex < command.size(); ++cmdIndex) {
|
||||
const auto cmdChar = command.at(cmdIndex);
|
||||
@@ -43,17 +49,17 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
continue;
|
||||
}
|
||||
|
||||
auto option = std::string();
|
||||
auto optionValue = std::optional<std::string>();
|
||||
auto option = std::string{};
|
||||
auto optionValue = std::optional<std::string>{};
|
||||
|
||||
bool quoted = false;
|
||||
|
||||
auto optIndex = std::string::size_type(0);
|
||||
auto optIndex = std::string::size_type{0};
|
||||
for (optIndex = cmdIndex + 1; optIndex < command.size(); ++optIndex) {
|
||||
const auto optChar = command.at(optIndex);
|
||||
|
||||
if (!option.empty() && ((!quoted && optChar == ' ') || (quoted && optChar == '"'))) {
|
||||
output.insert(std::pair(option, optionValue));
|
||||
output.emplace(option, optionValue);
|
||||
|
||||
option.clear();
|
||||
optionValue.reset();
|
||||
@@ -74,7 +80,7 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
}
|
||||
|
||||
if (optChar == '=') {
|
||||
optionValue = std::string();
|
||||
optionValue = std::string{};
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -83,7 +89,7 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
}
|
||||
|
||||
if (!option.empty()) {
|
||||
output.insert(std::pair(option, optionValue));
|
||||
output.emplace(option, optionValue);
|
||||
cmdIndex = optIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
|
||||
|
||||
@@ -25,12 +25,13 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
: CommandPacket(rawPacket)
|
||||
{
|
||||
if (this->data.size() < 6) {
|
||||
throw Exception("Unexpected RemoveBreakpoint packet size");
|
||||
throw Exception{"Unexpected RemoveBreakpoint packet size"};
|
||||
}
|
||||
|
||||
// z0 = SW breakpoint, z1 = HW breakpoint
|
||||
this->type = (this->data[1] == '0') ? BreakpointType::SOFTWARE_BREAKPOINT : (this->data[1] == '1') ?
|
||||
BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
|
||||
this->type = (this->data[1] == '0')
|
||||
? BreakpointType::SOFTWARE_BREAKPOINT
|
||||
: (this->data[1] == '1') ? BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
|
||||
|
||||
const auto packetData = QString::fromLocal8Bit(
|
||||
reinterpret_cast<const char*>(this->data.data() + 2),
|
||||
@@ -39,29 +40,34 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
auto packetSegments = packetData.split(",");
|
||||
if (packetSegments.size() < 3) {
|
||||
throw Exception("Unexpected number of packet segments in RemoveBreakpoint packet");
|
||||
throw Exception{"Unexpected number of packet segments in RemoveBreakpoint packet"};
|
||||
}
|
||||
|
||||
bool conversionStatus = true;
|
||||
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
|
||||
|
||||
if (!conversionStatus) {
|
||||
throw Exception("Failed to convert address hex value from RemoveBreakpoint packet.");
|
||||
throw Exception{"Failed to convert address hex value from RemoveBreakpoint packet."};
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveBreakpoint::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void RemoveBreakpoint::handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor&,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling RemoveBreakpoint packet");
|
||||
|
||||
try {
|
||||
Logger::debug("Removing breakpoint at address " + std::to_string(this->address));
|
||||
|
||||
debugSession.removeExternalBreakpoint(this->address, targetControllerService);
|
||||
debugSession.connection.writePacket(OkResponsePacket());
|
||||
debugSession.connection.writePacket(OkResponsePacket{});
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to remove breakpoint on target - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -21,7 +21,12 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
: Monitor(std::move(monitorPacket))
|
||||
{}
|
||||
|
||||
void ResetTarget::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void ResetTarget::handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor&,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling ResetTarget packet");
|
||||
|
||||
try {
|
||||
@@ -29,15 +34,15 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
targetControllerService.resetTarget();
|
||||
Logger::info("Target reset complete");
|
||||
|
||||
debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(
|
||||
debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(
|
||||
"Target reset complete\n"
|
||||
"Current PC: 0x" + Services::StringService::toHex(targetControllerService.getProgramCounter()) + "\n"
|
||||
"Use the 'continue' command to begin execution\n"
|
||||
)));
|
||||
)});
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to reset target - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -27,12 +27,13 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
: CommandPacket(rawPacket)
|
||||
{
|
||||
if (this->data.size() < 6) {
|
||||
throw Exception("Unexpected SetBreakpoint packet size");
|
||||
throw Exception{"Unexpected SetBreakpoint packet size"};
|
||||
}
|
||||
|
||||
// Z0 = SW breakpoint, Z1 = HW breakpoint
|
||||
this->type = (this->data[1] == '0') ? BreakpointType::SOFTWARE_BREAKPOINT : (this->data[1] == '1') ?
|
||||
BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
|
||||
this->type = (this->data[1] == '0')
|
||||
? BreakpointType::SOFTWARE_BREAKPOINT
|
||||
: (this->data[1] == '1') ? BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
|
||||
|
||||
auto packetData = QString::fromLocal8Bit(
|
||||
reinterpret_cast<const char*>(this->data.data() + 2),
|
||||
@@ -41,36 +42,41 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
auto packetSegments = packetData.split(",");
|
||||
if (packetSegments.size() < 3) {
|
||||
throw Exception("Unexpected number of packet segments in SetBreakpoint packet");
|
||||
throw Exception{"Unexpected number of packet segments in SetBreakpoint packet"};
|
||||
}
|
||||
|
||||
bool conversionStatus = true;
|
||||
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
|
||||
|
||||
if (!conversionStatus) {
|
||||
throw Exception("Failed to convert address hex value from SetBreakpoint packet.");
|
||||
throw Exception{"Failed to convert address hex value from SetBreakpoint packet."};
|
||||
}
|
||||
}
|
||||
|
||||
void SetBreakpoint::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void SetBreakpoint::handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor&,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling SetBreakpoint packet");
|
||||
|
||||
try {
|
||||
if (this->type == BreakpointType::UNKNOWN) {
|
||||
Logger::debug(
|
||||
"Rejecting breakpoint at address " + std::to_string(this->address)
|
||||
+ " - unsupported breakpoint type"
|
||||
+ " - unsupported breakpoint type"
|
||||
);
|
||||
debugSession.connection.writePacket(EmptyResponsePacket());
|
||||
debugSession.connection.writePacket(EmptyResponsePacket{});
|
||||
return;
|
||||
}
|
||||
|
||||
debugSession.setExternalBreakpoint(this->address, targetControllerService);
|
||||
debugSession.connection.writePacket(OkResponsePacket());
|
||||
debugSession.connection.writePacket(OkResponsePacket{});
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to set breakpoint on target - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
@@ -18,21 +19,31 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
{
|
||||
if (this->data.size() > 2) {
|
||||
this->fromAddress = static_cast<Targets::TargetMemoryAddress>(
|
||||
std::stoi(std::string(this->data.begin() + 2, this->data.end()), nullptr, 16)
|
||||
Services::StringService::toUint32(std::string{this->data.begin() + 2, this->data.end()}, 16)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void StepExecution::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void StepExecution::handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor&,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling StepExecution packet");
|
||||
|
||||
try {
|
||||
targetControllerService.stepTargetExecution(this->fromAddress);
|
||||
const auto atomicSession = targetControllerService.makeAtomicSession();
|
||||
if (this->fromAddress.has_value()) {
|
||||
targetControllerService.setProgramCounter(*(this->fromAddress));
|
||||
}
|
||||
|
||||
targetControllerService.stepTargetExecution();
|
||||
debugSession.waitingForBreak = true;
|
||||
|
||||
} catch (const Exception& exception) {
|
||||
Logger::error("Failed to step execution on target - " + exception.getMessage());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket());
|
||||
debugSession.connection.writePacket(ErrorResponsePacket{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
};
|
||||
|
||||
@@ -51,17 +51,23 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
}
|
||||
}
|
||||
|
||||
void SupportedFeaturesQuery::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) {
|
||||
void SupportedFeaturesQuery::handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor&,
|
||||
const Targets::TargetDescriptor&,
|
||||
TargetControllerService& targetControllerService
|
||||
) {
|
||||
Logger::info("Handling QuerySupport packet");
|
||||
|
||||
if (!this->isFeatureSupported(Feature::HARDWARE_BREAKPOINTS)
|
||||
if (
|
||||
!this->isFeatureSupported(Feature::HARDWARE_BREAKPOINTS)
|
||||
&& !this->isFeatureSupported(Feature::SOFTWARE_BREAKPOINTS)
|
||||
) {
|
||||
// All GDB clients are expected to support breakpoints!
|
||||
throw ClientNotSupported("GDB client does not support HW or SW breakpoints");
|
||||
throw ClientNotSupported{"GDB client does not support HW or SW breakpoints"};
|
||||
}
|
||||
|
||||
// Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom
|
||||
debugSession.connection.writePacket(SupportedFeaturesResponse(debugSession.supportedFeatures));
|
||||
debugSession.connection.writePacket(SupportedFeaturesResponse{debugSession.supportedFeatures});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace DebugServer::Gdb::CommandPackets
|
||||
|
||||
void handle(
|
||||
DebugSession& debugSession,
|
||||
const TargetDescriptor& gdbTargetDescriptor,
|
||||
const Targets::TargetDescriptor& targetDescriptor,
|
||||
Services::TargetControllerService& targetControllerService
|
||||
) override;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user