Deleted debug server documentation as I don't have time to maintain it.

This commit is contained in:
Nav
2024-12-24 20:11:32 +00:00
parent 674b11575d
commit fd45bad970
6 changed files with 2 additions and 211 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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