Deleted debug server documentation as I don't have time to maintain it.
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user