#pragma once #include #include #include #include #include #include #include "src/DebugServers/ServerInterface.hpp" #include "GdbDebugServerConfig.hpp" #include "src/EventManager/EventListener.hpp" #include "src/TargetController/TargetControllerConsole.hpp" #include "Connection.hpp" #include "TargetDescriptor.hpp" #include "DebugSession.hpp" #include "Signal.hpp" #include "RegisterDescriptor.hpp" #include "Feature.hpp" #include "CommandPackets/CommandPacket.hpp" #include "src/EventManager/Events/TargetControllerStateReported.hpp" #include "src/EventManager/Events/TargetExecutionStopped.hpp" #include "src/Helpers/BiMap.hpp" namespace Bloom::DebugServers::Gdb { /** * The GdbRspDebugServer is an implementation of a GDB server using the GDB Remote Serial Protocol. * * This DebugServer employs TCP/IP sockets to interface with GDB clients. The listening address can be configured * in the user's project config file. * * See https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html for more info on the GDB Remote * Serial Protocol. * * @TODO: This could do with some cleaning. */ class GdbRspDebugServer: public ServerInterface { public: explicit GdbRspDebugServer( const DebugServerConfig& debugServerConfig, EventListener& eventListener ); [[nodiscard]] std::string getName() const override { return "GDB Remote Serial Protocol DebugServer"; }; /** * Prepares the GDB server for listing on the selected address and port. */ void init() override; /** * Terminates any active debug session and closes the listening socket. */ void close() override; /** * Waits for a connection from a GDB client or services an active one. * * This function will return when any blocking operation is interrupted via this->interruptEventNotifier. */ void run() override; protected: std::optional debugServerConfig; EventListener& eventListener; EventNotifier* interruptEventNotifier = nullptr; TargetControllerConsole targetControllerConsole = TargetControllerConsole(this->eventListener); /** * Listening socket address */ struct sockaddr_in socketAddress = {}; /** * Listening socket file descriptor */ int serverSocketFileDescriptor = -1; /** * We don't listen on the this->serverSocketFileDescriptor directly. Instead, we add it to an epoll set, along * with the this->interruptEventNotifier FD. This allows us to interrupt any blocking socket IO calls when * we have other things to do. * * See GdbRspDebugServer::init() * See DebugServer::interruptEventNotifier * See EventNotifier */ int eventFileDescriptor = -1; /** * SO_REUSEADDR option value for listening socket. */ int enableReuseAddressSocketOption = 1; /** * When a connection with a GDB client is established, a new instance of the DebugSession class is created and * held here. A value of std::nullopt means there is no active debug session present. */ std::optional activeDebugSession; /** * Waits for a GDB client to connect on the listening socket. * * This function may return an std::nullopt, if the waiting was interrupted by some other event. */ std::optional waitForConnection(); /** * Waits for a command packet from the connected GDB client. * * @return */ std::unique_ptr waitForCommandPacket(); /** * Should construct a derived instance of the CommandPackets::CommandPacket class, from a raw packet. * * Derived implementations of this GDB server class can override this function to include support for more * specific GDB commands. For example, the AVR GDB implementation overrides this function to support AVR * specific implementations of the read and write memory GDB commands. * * This function should never return a nullptr. If the command is unknown, it should simply return an * instance of the CommandPackets::CommandPacket base class. The handler (CommandPacket::handle()) for the base * class will handle unknown packets by responding with an empty response packet (as is expected by the GDB * client). * * @param rawPacket * @return */ virtual std::unique_ptr resolveCommandPacket(const RawPacketType& rawPacket); /** * Terminates any active debug session (if any) by closing the connection to the GDB client. */ void terminateActiveDebugSession(); /** * Should return the GDB target descriptor for the connected target. * * NOTE: This function returns a target descriptor specific to GDB and the target. It's not the same data * struct as Targets::TargetDescriptor. But the GDB target descriptor does hold an instance to * Targets::TargetDescriptor. See the Gdb::TargetDescriptor::targetDescriptor class member. * * @return */ virtual const TargetDescriptor& getGdbTargetDescriptor() = 0; /** * Responds to any unexpected TargetController state changes. * * @param event */ void onTargetControllerStateReported(const Events::TargetControllerStateReported& event); /** * If the GDB client is currently waiting for the target execution to stop, this event handler will issue * a "stop reply" packet to the client once the target execution stops. */ void onTargetExecutionStopped(const Events::TargetExecutionStopped&); }; }