diff --git a/src/DebugServer/DebugServerComponent.hpp b/src/DebugServer/DebugServerComponent.hpp index a777fe9f..cde4fe6f 100644 --- a/src/DebugServer/DebugServerComponent.hpp +++ b/src/DebugServer/DebugServerComponent.hpp @@ -21,8 +21,6 @@ namespace DebugServer { /** * The DebugServer exposes the connected target to third-party debugging software such as IDEs. - * - * See documentation in src/DebugServer/README.md for more. */ class DebugServerComponent: public Thread { @@ -58,8 +56,7 @@ namespace DebugServer /** * This EventFdNotifier is injected into this->eventListener. It can be used by server implementations to - * interrupt blocking I/O calls upon an event being triggered. For more, see the "Servicing events" section in - * the DebugServer documentation (src/DebugServer/README.md). + * interrupt blocking I/O calls upon an event being triggered. */ EventFdNotifier interruptEventNotifier = EventFdNotifier(); diff --git a/src/DebugServer/Gdb/Exceptions/DebugServerInterrupted.hpp b/src/DebugServer/Gdb/Exceptions/DebugServerInterrupted.hpp index 604e6970..c395282b 100644 --- a/src/DebugServer/Gdb/Exceptions/DebugServerInterrupted.hpp +++ b/src/DebugServer/Gdb/Exceptions/DebugServerInterrupted.hpp @@ -9,9 +9,6 @@ namespace DebugServer::Gdb::Exceptions * from the connected GDB client. The server implementation allows for interruptions to blocking IO calls. * * When an interrupt occurs, this exception is thrown and handled appropriately. - * - * For more on how the GDB server implementation allows for interruptions, see the "Servicing events" section in - * src/DebugServer/README.md. */ class DebugServerInterrupted: public ::Exceptions::Exception { diff --git a/src/DebugServer/Gdb/GdbRspDebugServer.hpp b/src/DebugServer/Gdb/GdbRspDebugServer.hpp index 7f852974..a6adcdac 100644 --- a/src/DebugServer/Gdb/GdbRspDebugServer.hpp +++ b/src/DebugServer/Gdb/GdbRspDebugServer.hpp @@ -305,8 +305,6 @@ namespace DebugServer::Gdb * EventFdNotifier object for interrupting blocking I/O operations. * * Extracted from this->eventListener. - * - * See documentation in src/DebugServer/README.md for more. */ EventFdNotifier& interruptEventNotifier; diff --git a/src/DebugServer/Gdb/README.md b/src/DebugServer/Gdb/README.md deleted file mode 100644 index c5b6c610..00000000 --- a/src/DebugServer/Gdb/README.md +++ /dev/null @@ -1,126 +0,0 @@ -## GDB Remote Serial Protocol (RSP) debug server - -The GDB RSP debug server implements GDB's -[Remote Serial Protocol](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html) over a TCP/IP connection. -With this debug server, users can perform debugging operations on the connected target via GDB. This can be done via -GDB's command line interface, or via an IDE that supports remote GDB capabilities (See -[GDB's machine interface](https://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI.html) for more on how GDB is integrated -into IDEs). - -The implementation can be found in the `GdbRspDebugServer` class. This class is an abstract class - it does not -implement any target architecture specific functionality. -See ['Target architecture specific functionality'](#target-architecture-specific-functionality) section below for more. - ---- - -### Commands and responses - -The objective of the GDB server is to provide GDB with an interface to the connected target, to enable debugging -operations such as memory access and program flow control. The GDB server achieves this by actioning commands sent by -the client (GDB) and returning responses. - -Simply put, once GDB has established a connection with the GDB server, it will begin to send commands to the server. -These commands are to be actioned by the GDB server, with the appropriate response being returned, if necessary. -For example, when the GDB user runs the command `b main.cpp:22` in the GDB CLI, instructing GDB to insert a breakpoint -at line 22 in main.cpp, GDB will send a command to the GDB server, requesting that a breakpoint be added at the -appropriate address in program memory. The GDB server will action this command, and then return a response indicating -success or failure. - -Commands and responses are delivered in packets. The -[`DebugServer::Gdb::CommandPackets::CommandPacket`](./CommandPackets/CommandPacket.hpp) and -[`DebugServer::Gdb::ResponsePackets::ResponsePacket`](./ResponsePackets/ResponsePacket.hpp) classes are base -classes for these packets. For most GDB commands supported by this server implementation, there is a specific command -packet class that can be found in [/src/DebugServer/Gdb/CommandPackets](./CommandPackets). When the server receives a -command packet from the GDB client, the appropriate (`CommandPacket` derived) object is constructed, which encapsulates -all of the relevant information for the particular command. - -Consider the [`DebugServer::Gdb::CommandPackets::SetBreakpoint`](./CommandPackets/SetBreakpoint.hpp) command -packet class: - -```c++ -class SetBreakpoint: public CommandPacket -{ -public: - /** - * Breakpoint type (Software or Hardware) - */ - BreakpointType type = BreakpointType::UNKNOWN; - - /** - * Address at which the breakpoint should be located. - */ - std::uint32_t address = 0; - - explicit SetBreakpoint(const RawPacket& rawPacket); - - void handle(DebugSession& debugSession, TargetControllerService& targetControllerService) override; -}; -``` - -The `SetBreakpoint` class consists of two public fields; The `address` (at which the breakpoint is to be set). And the -`type` (of breakpoint to set). During object construction, these two fields are initialised from the raw command packet -(received by the GDB client). - -#### Command handling - -Upon receiving a command packet from the GDB client, the command must be handled and the appropriate response delivered. -Each command packet class implements a `handle()` member function. This function is called upon receipt of the command -and is expected to handle the command and deliver any necessary response to the client. Two parameters are passed to the -`handle()` member function - a reference to the active `DebugSession` object, and a reference to a -`TargetControllerService` object. The `DebugSession` object provides access to the current connection with the GDB -client, as well as other debug session specific information. The `TargetControllerService` object provides an interface -to the `TargetController`, for any GDB commands that need to interface with the connected target (see the -[TargetController documentation](../../TargetController/README.md) for more on this). - -Handling the `SetBreakpoint` command packet: - -```c++ -void SetBreakpoint::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) { - /* - * I know the breakpoint type (this->type) isn't used in here - this is because the current implementation only - * supports software breakpoints, so we don't do anything with this->type, for now. - */ - - Logger::debug("Handling SetBreakpoint packet"); - - try { - targetControllerService.setBreakpoint(TargetBreakpoint(this->address)); - debugSession.connection.writePacket(OkResponsePacket()); - - } catch (const Exception& exception) { - Logger::error("Failed to set breakpoint on target - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); - } -} -``` - -Some GDB commands are handled in the base `CommandPacket` class (see -[`CommandPacket::handle()`](./CommandPackets/CommandPacket.cpp)). Either these commands were too trivial to justify a -new command packet class, or I was too lazy to create one. - ---- - -### Target architecture specific functionality - -Within the source code of GDB, there are some hardcoded concepts that are specific to GDB and a particular target -architecture. For example, the AVR implementation of GDB defines a mapping of AVR registers to GDB register numbers. -The GDB register numbers are just identifiers for AVR registers - they allow GDB to refer to a particular AVR register. - -| AVR Register Name | GDB Register Number | -|-------------------------------------------|---------------------| -| R0 -> R31 (general purpose CPU registers) | 0 -> 31 | -| Status Register (SREG) | 32 | -| Stack Pointer (SP) | 33 | -| Program Counter (PC) | 34 | - -GDB expects the server to be aware of these predefined concepts. This is why the `GdbRspDebugServer` class is an -abstract class - it implements the generic GDB server functionality, but leaves the target architecture specific -functionality to be implemented in derived classes. - -#### GDB target descriptor - -The [`DebugServer::Gdb::TargetDescriptor`](./TargetDescriptor.hpp) abstract class provides access to any -information that is specific to the target architecture and GDB. For example, the register mapping described above, for -AVR targets, is implemented in [`DebugServer::Gdb::AvrGdb::TargetDescriptor`](./AvrGdb/TargetDescriptor.hpp). -That class is derived from the abstract `TargetDescriptor` class. It implements the AVR specific concepts that the -server is expected to be aware of. diff --git a/src/DebugServer/README.md b/src/DebugServer/README.md deleted file mode 100644 index 2627d9eb..00000000 --- a/src/DebugServer/README.md +++ /dev/null @@ -1,73 +0,0 @@ -## DebugServer - -The DebugServer component exposes an interface to the connected target, for third-party programs such as IDEs. The -DebugServer runs on a dedicated thread. The entry point is `DebugServerComponent::run()`. Bloom's main thread will start -the DebugServer thread once the TargetController has been started. See `Applciation::startDebugServer()` for more. - -The DebugServer is designed to accommodate numerous server implementations. The interface exposed by the server is -implementation-defined. For example, the [AVR GDB server](./Gdb/AvrGdb/AvrGdbRsp.hpp) exposes an interface to the -connected AVR target, by implementing the -[GDB Remote Serial Protocol](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html), over a TCP/IP connection. -Each server must implement the interface defined in the [`ServerInterface` class](./ServerInterface.hpp). - -At start up, the DebugServer will select the appropriate server implementation, based on the user's project -configuration (bloom.yaml - see [`DebugServerConfig`](../ProjectConfig.hpp)). Each server implementation is mapped to a -config name, which is to be used in the project configuration file. For the mapping, see -`DebugServerComponent::getAvailableServersByName()`. After initialising the server (via `ServerInterface::init()`), -control of the DebugServer thread will then be handed over to the server implementation (via `ServerInterface::run()`). -For more on this, see `DebugServerComponent::startup()` and `DebugServerComponent::run()`. - -#### Servicing events - -During start up, the DebugServer will register event handlers for certain events. Once control of the DebugServer thread -has been handed over to the selected server implementation, the server must ensure that any incoming events are -processed ASAP. How this is done is implementation-defined. A reference to the DebugServer's event listener -(`DebugServerComponent::eventListener`) can be passed to the server if need be (see -`DebugServerComponent::getAvailableServersByName()` for an example of this). - -A server implementation does not have to service events - it can simply return from `ServerInterface::run()` upon -an event being triggered. Returning from that function will allow control of the thread to be handed back to -`DebugServerComponent::run()`, where any pending events will be processed. Afterwards, if the DebugServer is still -running (it hasn't received an event which triggers a shutdown), the `ServerInterface::run()` function will be called -again, and control will be handed back to the server implementation. The AVR GDB server implementation employs this -method to ensure that events are processed ASAP. See the relevant documentation for more. - ---- - -Any blocking I/O employed by a server implementation can support event interrupts using an -[`EventFdNotifier`](../Helpers/EventFdNotifier.hpp) and [`EpollInstance`](../Helpers/EpollInstance.hpp). - -Key points: -- The `EventFdNotifier` class is an RAII wrapper for a Linux - [eventfd object](https://man7.org/linux/man-pages/man2/eventfd.2.html). It implements the - [`NotifierInterface`](../Helpers/NotifierInterface.hpp). An event can be recorded against the eventfd - object via a call to `EventFdNotifier::notify()`. -- The [`EventListener`](../EventManager/EventListener.hpp) class can accept a `NotifierInterface` object via - `EventListener::setInterruptEventNotifier()`. If a `NotifierInterface` has been set on an `EventListener`, the - `EventListener` will call `NotifierInterface::notify()` everytime an event is registered for that listener. -- The `EpollInstance` class is an RAII wrapper for a Linux - [epoll instance](https://man7.org/linux/man-pages/man7/epoll.7.html). It allows us to wait for any activity on a set - of file descriptors. File descriptors can be added and removed from the epoll instance via `EpollInstance::addEntry()` - and `EpollInstance::removeEntry()`. Calling `EpollInstance::waitForEvent()` will block until there is activity on at - least one of the file descriptors, or a timeout has been reached. - -With an `EventFdNotifier` and `EpollInstance`, one can perform a blocking I/O operation which can be interrupted by an -event. For an example of this, see the AVR GDB server implementation - it employs the method described above to allow -the interruption of blocking I/O operations when an event is triggered. Specifically, -[`GdbRspDebugServer::waitForConnection()`](./Gdb/GdbRspDebugServer.hpp) or -[`Gdb::Connection::read()`](./Gdb/Connection.hpp). - ---- - -### Server implementations - -Currently, there is only one server implementation. Others may be added upon request. - -| Server Name | Brief Description | Documentation | -|----------------|-------------------------------------------------------------------------------|---------------------------------------------------| -| AVR GDB Server | An AVR-specific implementation of the GDB Remote Serial Protocol over TCP/IP. | [/src/DebugServer/Gdb/README.md](./Gdb/README.md) | - -#### Adding new server implementations - -If you're considering adding a new server implementation, please discuss this with me (Nav) before you begin. Creating -a new server implementation is not a trivial task. diff --git a/src/DebugServer/ServerInterface.hpp b/src/DebugServer/ServerInterface.hpp index 9300b023..6201e453 100644 --- a/src/DebugServer/ServerInterface.hpp +++ b/src/DebugServer/ServerInterface.hpp @@ -6,8 +6,6 @@ namespace DebugServer { /** * Every debug server must implement this interface. - * - * See documentation in src/DebugServer/README.md for more. */ class ServerInterface { @@ -32,7 +30,7 @@ namespace DebugServer * * For servicing DebugServer events, the implementation should either service them here or return from here * upon an event being triggered. Returning from this function will allow DebugServerComponent::run() to - * process any pending events. See the DebugServer documentation in src/DebugServer/README.md for more. + * process any pending events. */ virtual void run() = 0;