From a7ffc56d9bff0558f5771a7ab39d4992e489623c Mon Sep 17 00:00:00 2001 From: Nav Date: Mon, 26 Aug 2024 12:38:31 +0100 Subject: [PATCH] New `mon rr` GDB command for reading target registers --- src/CMakeLists.txt | 1 + src/DebugServer/CMakeLists.txt | 1 + .../CommandPackets/ReadRegistersMonitor.cpp | 225 ++++++++++++++++++ .../CommandPackets/ReadRegistersMonitor.hpp | 44 ++++ src/DebugServer/Gdb/GdbRspDebugServer.cpp | 5 + src/Services/IntegerService.cpp | 17 ++ src/Services/IntegerService.hpp | 13 + src/Services/StringService.cpp | 94 ++++++++ src/Services/StringService.hpp | 25 ++ 9 files changed, 425 insertions(+) create mode 100644 src/DebugServer/Gdb/CommandPackets/ReadRegistersMonitor.cpp create mode 100644 src/DebugServer/Gdb/CommandPackets/ReadRegistersMonitor.hpp create mode 100644 src/Services/IntegerService.cpp create mode 100644 src/Services/IntegerService.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a784a823..fe5a298b 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,6 +10,7 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/Services/PathService.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Services/ProcessService.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Services/StringService.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Services/IntegerService.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Services/Avr8InstructionService.cpp # Helpers & other diff --git a/src/DebugServer/CMakeLists.txt b/src/DebugServer/CMakeLists.txt index 695bb8aa..9de5551e 100755 --- a/src/DebugServer/CMakeLists.txt +++ b/src/DebugServer/CMakeLists.txt @@ -23,6 +23,7 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/BloomVersion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/BloomVersionMachine.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/Detach.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/ReadRegistersMonitor.cpp # AVR GDB RSP Server ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/AvrGdbRsp.cpp diff --git a/src/DebugServer/Gdb/CommandPackets/ReadRegistersMonitor.cpp b/src/DebugServer/Gdb/CommandPackets/ReadRegistersMonitor.cpp new file mode 100644 index 00000000..5d900bc6 --- /dev/null +++ b/src/DebugServer/Gdb/CommandPackets/ReadRegistersMonitor.cpp @@ -0,0 +1,225 @@ +#include "ReadRegistersMonitor.hpp" + +#include + +#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp" +#include "src/DebugServer/Gdb/ResponsePackets/PartialResponsePacket.hpp" +#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp" + +#include "src/Services/StringService.hpp" +#include "src/Services/IntegerService.hpp" +#include "src/Logger/Logger.hpp" + +#include "src/Exceptions/Exception.hpp" + +namespace DebugServer::Gdb::CommandPackets +{ + using Services::TargetControllerService; + using Services::StringService; + using Services::IntegerService; + + using ResponsePackets::ErrorResponsePacket; + using ResponsePackets::PartialResponsePacket; + using ResponsePackets::ResponsePacket; + + using ::Exceptions::Exception; + + ReadRegistersMonitor::ReadRegistersMonitor(Monitor&& monitorPacket) + : Monitor(std::move(monitorPacket)) + {} + + void ReadRegistersMonitor::handle( + DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + TargetControllerService& targetControllerService + ) { + Logger::info("Handling ReadRegisterMonitor packet"); + + try { + const auto argCount = this->commandArguments.size(); + if (argCount < 2) { + throw Exception{"Peripheral key required"}; + } + + const auto& peripheralKey = this->commandArguments[1]; + const auto peripheralDescriptorOpt = targetDescriptor.tryGetPeripheralDescriptor(peripheralKey); + + if (!peripheralDescriptorOpt.has_value()) { + throw Exception{"Unknown peripheral key \"" + peripheralKey + "\""}; + } + + const auto& peripheralDescriptor = peripheralDescriptorOpt->get(); + + if (argCount < 3) { + // Register details were not provided - read all registers in the peripheral + this->handlePeripheralOutput(peripheralDescriptor, debugSession, targetControllerService); + return; + } + + const auto& registerGroupKey = this->commandArguments[2]; + auto registerKey = argCount >= 4 ? std::optional{this->commandArguments[3]} : std::nullopt; + + auto registerGroupDescriptorOpt = peripheralDescriptor.tryGetRegisterGroupDescriptor( + registerGroupKey + ); + + if (!registerGroupDescriptorOpt.has_value()) { + if (peripheralDescriptor.registerGroupDescriptorsByKey.size() != 1 || argCount >= 4) { + throw Exception{"Unknown register group key \"" + registerGroupKey + "\""}; + } + + registerGroupDescriptorOpt = peripheralDescriptor.registerGroupDescriptorsByKey.begin()->second; + registerKey = registerGroupKey; + } + + const auto& registerGroupDescriptor = registerGroupDescriptorOpt->get(); + + if (!registerKey.has_value()) { + debugSession.connection.writePacket(PartialResponsePacket{StringService::toHex( + "Reading all registers in " + registerGroupDescriptor.name + " register group, from " + + peripheralDescriptor.name + " peripheral...\n\n" + )}); + this->handleRegisterGroupOutput(registerGroupDescriptor, debugSession, targetControllerService); + debugSession.connection.writePacket(ResponsePacket{StringService::toHex("\n")}); + return; + } + + const auto registerDescriptorOpt = registerGroupDescriptor.tryGetRegisterDescriptor(*registerKey); + + if (!registerDescriptorOpt.has_value()) { + throw Exception{"Unknown register key \"" + *registerKey + "\""}; + } + + this->handleSingleRegisterOutput(registerDescriptorOpt->get(), debugSession, targetControllerService); + + } catch (const Exception& exception) { + debugSession.connection.writePacket(ResponsePacket{ + StringService::toHex( + StringService::applyTerminalColor( + "Error: " + exception.getMessage() + "\n", + StringService::TerminalColor::DARK_RED + ) + ) + }); + } + } + + void ReadRegistersMonitor::handleSingleRegisterOutput( + const Targets::TargetRegisterDescriptor& registerDescriptor, + DebugSession& debugSession, + TargetControllerService& targetControllerService + ) { + auto output = std::string{"\nName: " + registerDescriptor.name + "\n"}; + output += "Address: " + StringService::applyTerminalColor( + "0x" + StringService::asciiToUpper(StringService::toHex(registerDescriptor.startAddress)), + StringService::TerminalColor::DARK_BLUE + ) + "\n"; + output += "Size: " + std::to_string(registerDescriptor.size) + " byte(s)\n\n"; + + output += "----------- Value -----------\n"; + + if (registerDescriptor.access.readable) { + const auto value = IntegerService::toUint64( + targetControllerService.readRegister(registerDescriptor) + ); + + output += StringService::applyTerminalColor( + "0x" + StringService::asciiToUpper(StringService::toHex(value)).substr(16 - (registerDescriptor.size * 2)), + StringService::TerminalColor::DARK_YELLOW + ); + output += " (" + StringService::applyTerminalColor( + std::to_string(value), + StringService::TerminalColor::DARK_YELLOW + ); + output += ", " + StringService::applyTerminalColor( + "0b" + std::bitset<64>{value}.to_string().substr(64 - (registerDescriptor.size * 8)), + StringService::TerminalColor::DARK_YELLOW + ) + ")\n"; + + for (const auto& [bitFieldKey, bitFieldDescriptor] : registerDescriptor.bitFieldDescriptorsByKey) { + output += bitFieldDescriptor.name + ": " + StringService::applyTerminalColor( + "0b" + StringService::toBinaryStringWithMask(value, bitFieldDescriptor.mask), + StringService::TerminalColor::DARK_YELLOW + ) + " "; + + // TODO: Warn about bit field value being meaningless if the bit field isn't readable + } + + } else { + output += StringService::applyTerminalColor("inaccessible\n", StringService::TerminalColor::DARK_RED); + } + + output += "\n"; + debugSession.connection.writePacket(ResponsePacket{StringService::toHex(output)}); + } + + void ReadRegistersMonitor::handlePeripheralOutput( + const Targets::TargetPeripheralDescriptor& peripheralDescriptor, + DebugSession& debugSession, + TargetControllerService& targetControllerService + ) { + debugSession.connection.writePacket(PartialResponsePacket{StringService::toHex( + "Reading " + peripheralDescriptor.name + " peripheral registers...\n\n" + )}); + + for (const auto& [groupKey, groupDescriptor] : peripheralDescriptor.registerGroupDescriptorsByKey) { + this->handleRegisterGroupOutput(groupDescriptor, debugSession, targetControllerService); + } + + debugSession.connection.writePacket(ResponsePacket{StringService::toHex("\n")}); + } + + void ReadRegistersMonitor::handleRegisterGroupOutput( + const Targets::TargetRegisterGroupDescriptor& groupDescriptor, + DebugSession& debugSession, + TargetControllerService& targetControllerService + ) { + for (const auto& [registerKey, registerDescriptor] : groupDescriptor.registerDescriptorsByKey) { + auto output = std::string{registerDescriptor.absoluteGroupKey + ", "}; + output += registerDescriptor.key + ", "; + output += registerDescriptor.name + ", "; + output += StringService::applyTerminalColor( + "0x" + StringService::asciiToUpper(StringService::toHex(registerDescriptor.startAddress)), + StringService::TerminalColor::DARK_BLUE + ) + ", "; + output += std::to_string(registerDescriptor.size) + " byte(s) | "; + + if (registerDescriptor.access.readable) { + const auto value = IntegerService::toUint64( + targetControllerService.readRegister(registerDescriptor) + ); + + output += StringService::applyTerminalColor( + "0x" + StringService::asciiToUpper(StringService::toHex(value)).substr(16 - (registerDescriptor.size * 2)), + StringService::TerminalColor::DARK_YELLOW + ); + output += " (" + StringService::applyTerminalColor( + std::to_string(value), + StringService::TerminalColor::DARK_YELLOW + ); + output += ", " + StringService::applyTerminalColor( + "0b" + std::bitset<64>{value}.to_string().substr(64 - (registerDescriptor.size * 8)), + StringService::TerminalColor::DARK_YELLOW + ) + ")"; + + for (const auto& [bitFieldKey, bitFieldDescriptor] : registerDescriptor.bitFieldDescriptorsByKey) { + output += ", " + bitFieldDescriptor.name + ": " + StringService::applyTerminalColor( + "0b" + StringService::toBinaryStringWithMask(value, bitFieldDescriptor.mask), + StringService::TerminalColor::DARK_YELLOW + ); + } + + } else { + output += StringService::applyTerminalColor("inaccessible", StringService::TerminalColor::DARK_RED); + } + + output += "\n"; + debugSession.connection.writePacket(PartialResponsePacket{StringService::toHex(output)}); + } + + for (const auto& [subGroupKey, subGroupDescriptor] : groupDescriptor.subgroupDescriptorsByKey) { + this->handleRegisterGroupOutput(subGroupDescriptor, debugSession, targetControllerService); + } + } +} diff --git a/src/DebugServer/Gdb/CommandPackets/ReadRegistersMonitor.hpp b/src/DebugServer/Gdb/CommandPackets/ReadRegistersMonitor.hpp new file mode 100644 index 00000000..e179c9b4 --- /dev/null +++ b/src/DebugServer/Gdb/CommandPackets/ReadRegistersMonitor.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include "Monitor.hpp" + +#include "src/Targets/TargetRegisterDescriptor.hpp" +#include "src/Targets/TargetRegisterGroupDescriptor.hpp" +#include "src/Targets/TargetPeripheralDescriptor.hpp" + +namespace DebugServer::Gdb::CommandPackets +{ + class ReadRegistersMonitor: public Monitor + { + public: + explicit ReadRegistersMonitor(Monitor&& monitorPacket); + + void handle( + DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + Services::TargetControllerService& targetControllerService + ) override; + + protected: + void handleSingleRegisterOutput( + const Targets::TargetRegisterDescriptor& registerDescriptor, + DebugSession& debugSession, + Services::TargetControllerService& targetControllerService + ); + + void handlePeripheralOutput( + const Targets::TargetPeripheralDescriptor& peripheralDescriptor, + DebugSession& debugSession, + Services::TargetControllerService& targetControllerService + ); + + void handleRegisterGroupOutput( + const Targets::TargetRegisterGroupDescriptor& groupDescriptor, + DebugSession& debugSession, + Services::TargetControllerService& targetControllerService + ); + }; +} diff --git a/src/DebugServer/Gdb/GdbRspDebugServer.cpp b/src/DebugServer/Gdb/GdbRspDebugServer.cpp index e051fbd8..001ca7ca 100644 --- a/src/DebugServer/Gdb/GdbRspDebugServer.cpp +++ b/src/DebugServer/Gdb/GdbRspDebugServer.cpp @@ -29,6 +29,7 @@ #include "CommandPackets/BloomVersion.hpp" #include "CommandPackets/BloomVersionMachine.hpp" #include "CommandPackets/Detach.hpp" +#include "CommandPackets/ReadRegistersMonitor.hpp" #ifndef EXCLUDE_INSIGHT #include "CommandPackets/ActivateInsight.hpp" @@ -291,6 +292,10 @@ namespace DebugServer::Gdb return std::make_unique(std::move(*(monitorCommand.release()))); } + if (monitorCommand->command.find("rr") == 0) { + return std::make_unique(std::move(*(monitorCommand.release()))); + } + #ifndef EXCLUDE_INSIGHT if (monitorCommand->command.find("insight") == 0) { return std::make_unique(std::move(*(monitorCommand.release()))); diff --git a/src/Services/IntegerService.cpp b/src/Services/IntegerService.cpp new file mode 100644 index 00000000..74a3ae1f --- /dev/null +++ b/src/Services/IntegerService.cpp @@ -0,0 +1,17 @@ +#include "IntegerService.hpp" + +#include + +namespace Services +{ + std::uint64_t IntegerService::toUint64(const std::vector& data) { + assert(data.size() <= 8); + auto output = std::uint64_t{0}; + + for (const auto& byte : data) { + output = (output << 8) | byte; + } + + return output; + } +} diff --git a/src/Services/IntegerService.hpp b/src/Services/IntegerService.hpp new file mode 100644 index 00000000..9ba8fd96 --- /dev/null +++ b/src/Services/IntegerService.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +namespace Services +{ + class IntegerService + { + public: + static std::uint64_t toUint64(const std::vector& data); + }; +} diff --git a/src/Services/StringService.cpp b/src/Services/StringService.cpp index 41cfaba0..49a3b7ac 100644 --- a/src/Services/StringService.cpp +++ b/src/Services/StringService.cpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace Services { @@ -41,6 +42,12 @@ namespace Services return str; } + std::string StringService::toHex(std::uint64_t value) { + auto stream = std::stringstream{}; + stream << std::hex << std::setfill('0') << std::setw(16) << static_cast(value); + return stream.str(); + } + std::string StringService::toHex(std::uint32_t value) { auto stream = std::stringstream{}; stream << std::hex << std::setfill('0') << std::setw(8) << static_cast(value); @@ -75,6 +82,20 @@ namespace Services return stream.str(); } + std::string StringService::toBinaryStringWithMask(std::uint64_t value, std::uint64_t mask) { + auto output = std::string{}; + + const auto maskBitset = std::bitset<64>{mask}; + for (auto i = std::size_t{0}; i < maskBitset.size(); ++i) { + const auto& bit = maskBitset[i]; + if (bit) { + output.insert(output.begin(), (value & (0x01 << i)) != 0 ? '1' : '0'); + } + } + + return output; + } + std::vector StringService::dataFromHex(const std::string& hexData) { auto output = std::vector{}; @@ -126,4 +147,77 @@ namespace Services return {std::ranges::begin(range), std::ranges::end(range)}; } + + std::string StringService::applyTerminalColor(const std::string& string, TerminalColor color) { + auto colorCode = std::string{}; + + switch (color) { + case TerminalColor::BLACK: { + colorCode = "\033[30m"; + break; + } + case TerminalColor::DARK_RED: { + colorCode = "\033[31m"; + break; + } + case TerminalColor::DARK_GREEN: { + colorCode = "\033[32m"; + break; + } + case TerminalColor::DARK_YELLOW: { + colorCode = "\033[33m"; + break; + } + case TerminalColor::DARK_BLUE: { + colorCode = "\033[34m"; + break; + } + case TerminalColor::DARK_MAGENTA: { + colorCode = "\033[35m"; + break; + } + case TerminalColor::DARK_CYAN: { + colorCode = "\033[36m"; + break; + } + case TerminalColor::LIGHT_GRAY: { + colorCode = "\033[37m"; + break; + } + case TerminalColor::DARK_GRAY: { + colorCode = "\033[90m"; + break; + } + case TerminalColor::RED: { + colorCode = "\033[91m"; + break; + } + case TerminalColor::GREEN: { + colorCode = "\033[92m"; + break; + } + case TerminalColor::ORANGE: { + colorCode = "\033[93m"; + break; + } + case TerminalColor::BLUE: { + colorCode = "\033[94m"; + break; + } + case TerminalColor::MAGENTA: { + colorCode = "\033[95m"; + break; + } + case TerminalColor::CYAN: { + colorCode = "\033[96m"; + break; + } + case TerminalColor::WHITE: { + colorCode = "\033[97m"; + break; + } + } + + return colorCode + string + "\033[0m"; + } } diff --git a/src/Services/StringService.hpp b/src/Services/StringService.hpp index b1003de1..8992b493 100644 --- a/src/Services/StringService.hpp +++ b/src/Services/StringService.hpp @@ -17,11 +17,14 @@ namespace Services static bool isAscii(const std::string& str); static std::string replaceUnprintable(std::string str); + static std::string toHex(std::uint64_t value); static std::string toHex(std::uint32_t value); static std::string toHex(unsigned char value); static std::string toHex(const std::vector& data); static std::string toHex(const std::string& data); + static std::string toBinaryStringWithMask(std::uint64_t value, std::uint64_t mask); + static std::vector dataFromHex(const std::string& hexData); static std::uint64_t toUint64(const std::string& str, int base = 0); @@ -54,5 +57,27 @@ namespace Services static std::size_t generateUniqueInteger(const std::string& str); static std::vector split(std::string_view str, char delimiter); + + enum class TerminalColor: std::uint8_t + { + BLACK, + DARK_RED, + DARK_GREEN, + DARK_YELLOW, + DARK_BLUE, + DARK_MAGENTA, + DARK_CYAN, + LIGHT_GRAY, + DARK_GRAY, + RED, + GREEN, + ORANGE, + BLUE, + MAGENTA, + CYAN, + WHITE, + }; + + static std::string applyTerminalColor(const std::string& string, TerminalColor color); }; }