Made a start with documentation for the GDB server implementation
This commit is contained in:
124
src/DebugServer/Gdb/README.md
Normal file
124
src/DebugServer/Gdb/README.md
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
## 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
|
||||||
|
[`Bloom::DebugServer::Gdb::CommandPackets::CommandPacket`](./CommandPackets/CommandPacket.hpp) and
|
||||||
|
[`Bloom::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 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 [`Bloom::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 std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {
|
||||||
|
this->init();
|
||||||
|
};
|
||||||
|
|
||||||
|
void handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
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). These two fields are initialised from the raw command packet, during object construction.
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
|
||||||
|
Handling the `SetBreakpoint` command packet:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
void SetBreakpoint::handle(DebugSession& debugSession, TargetControllerConsole& targetControllerConsole) {
|
||||||
|
/*
|
||||||
|
* 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 {
|
||||||
|
targetControllerConsole.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 [`Bloom::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 [`Bloom::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.
|
||||||
@@ -32,6 +32,8 @@ running (it hasn't received an event which triggers a shutdown), the `ServerInte
|
|||||||
again, and control will be handed back to the server implementation. The AVR GDB server implementation employs this
|
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.
|
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
|
Any blocking I/O employed by a server implementation can support event interrupts using an
|
||||||
[`EventNotifier`](../Helpers/EventNotifier.hpp) and [`EpollInstance`](../Helpers/EpollInstance.hpp).
|
[`EventNotifier`](../Helpers/EventNotifier.hpp) and [`EpollInstance`](../Helpers/EpollInstance.hpp).
|
||||||
|
|
||||||
@@ -54,13 +56,15 @@ the interruption of blocking I/O operations when an event is triggered. Specific
|
|||||||
[`GdbRspDebugServer::waitForConnection()`](./Gdb/GdbRspDebugServer.hpp) or
|
[`GdbRspDebugServer::waitForConnection()`](./Gdb/GdbRspDebugServer.hpp) or
|
||||||
[`Gdb::Connection::read()`](./Gdb/Connection.hpp).
|
[`Gdb::Connection::read()`](./Gdb/Connection.hpp).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Server implementations
|
### Server implementations
|
||||||
|
|
||||||
Currently, there is only one server implementation. Others may be added upon request.
|
Currently, there is only one server implementation. Others may be added upon request.
|
||||||
|
|
||||||
| Server Name | Brief Description | Documentation |
|
| Server Name | Brief Description | Documentation |
|
||||||
|----------------|-------------------|---------------|
|
|----------------|-------------------------------------------------------------------------------|---------------------------------------------------|
|
||||||
| AVR GDB Server | An AVR-specific implementation of the GDB Remote Serial Protocol over TCP/IP. | To follow |
|
| 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
|
#### Adding new server implementations
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user