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:
Nav
2024-07-23 21:14:22 +01:00
parent 2986934485
commit 6cdbfbe950
331 changed files with 8815 additions and 8565 deletions

View File

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

View File

@@ -16,6 +16,8 @@ namespace DebugServer::Gdb::CommandPackets
void handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

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

View File

@@ -18,6 +18,8 @@ namespace DebugServer::Gdb::CommandPackets
void handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

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

View File

@@ -18,6 +18,8 @@ namespace DebugServer::Gdb::CommandPackets
void handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

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

View File

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

View File

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

View File

@@ -30,6 +30,8 @@ namespace DebugServer::Gdb::CommandPackets
void handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

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

View File

@@ -11,6 +11,8 @@ namespace DebugServer::Gdb::CommandPackets
void handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,6 +18,8 @@ namespace DebugServer::Gdb::CommandPackets
void handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

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

View File

@@ -19,6 +19,8 @@ namespace DebugServer::Gdb::CommandPackets
void handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

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

View File

@@ -33,6 +33,8 @@ namespace DebugServer::Gdb::CommandPackets
void handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;

View File

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

View File

@@ -32,6 +32,8 @@ namespace DebugServer::Gdb::CommandPackets
void handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

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

View File

@@ -19,6 +19,8 @@ namespace DebugServer::Gdb::CommandPackets
void handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

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

View File

@@ -32,6 +32,8 @@ namespace DebugServer::Gdb::CommandPackets
void handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

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

View File

@@ -25,6 +25,8 @@ namespace DebugServer::Gdb::CommandPackets
void handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;
};

View File

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

View File

@@ -34,6 +34,8 @@ namespace DebugServer::Gdb::CommandPackets
void handle(
DebugSession& debugSession,
const TargetDescriptor& gdbTargetDescriptor,
const Targets::TargetDescriptor& targetDescriptor,
Services::TargetControllerService& targetControllerService
) override;