diff --git a/src/tools/ecode/plugins/debugger/bus.cpp b/src/tools/ecode/plugins/debugger/bus.cpp index 56ebf9461..d175a47a2 100644 --- a/src/tools/ecode/plugins/debugger/bus.cpp +++ b/src/tools/ecode/plugins/debugger/bus.cpp @@ -2,6 +2,18 @@ namespace ecode { - - +Bus::State Bus::state() const { + return mState; } + +void Bus::setState( State state ) { + if ( state == mState ) + return; + mState = state; + onStateChanged( state ); +} + +void Bus::onStateChanged( State state ) { +} + +} // namespace ecode diff --git a/src/tools/ecode/plugins/debugger/bus.hpp b/src/tools/ecode/plugins/debugger/bus.hpp index 743a99cb3..87981315f 100644 --- a/src/tools/ecode/plugins/debugger/bus.hpp +++ b/src/tools/ecode/plugins/debugger/bus.hpp @@ -1,18 +1,31 @@ #pragma once -#include "config.hpp" +#include namespace ecode { class Bus { public: + enum class State { None, Running, Closed }; + typedef std::function ReadFn; + State state() const; + virtual bool start() = 0; + virtual bool close() = 0; + virtual void startAsyncRead( ReadFn readFn ) = 0; virtual size_t write( const char* buffer, const size_t& size ) = 0; + + protected: + void setState( State state ); + + void onStateChanged( State state ); + + State mState{ State::None }; }; } // namespace ecode diff --git a/src/tools/ecode/plugins/debugger/busprocess.cpp b/src/tools/ecode/plugins/debugger/busprocess.cpp index ff4380de7..11617674b 100644 --- a/src/tools/ecode/plugins/debugger/busprocess.cpp +++ b/src/tools/ecode/plugins/debugger/busprocess.cpp @@ -5,10 +5,23 @@ namespace ecode { BusProcess::BusProcess( const Command& command ) : mCommand( command ), mProcess() {} bool BusProcess::start() { - return mProcess.create( mCommand.command, mCommand.arguments, - Process::getDefaultOptions() | Process::Options::EnableAsync | - Process::Options::CombinedStdoutStderr, - mCommand.environment ); + bool res = mProcess.create( mCommand.command, mCommand.arguments, + Process::getDefaultOptions() | Process::Options::EnableAsync | + Process::Options::CombinedStdoutStderr, + mCommand.environment ); + if ( res ) + setState( State::Running ); + + return res; +} + +bool BusProcess::close() { + if ( mState == State::Running ) { + bool res = mProcess.kill(); + if ( res ) + setState( State::Closed ); + } + return false; } void BusProcess::startAsyncRead( ReadFn readFn ) { diff --git a/src/tools/ecode/plugins/debugger/busprocess.hpp b/src/tools/ecode/plugins/debugger/busprocess.hpp index c5bcea657..4da07ee43 100644 --- a/src/tools/ecode/plugins/debugger/busprocess.hpp +++ b/src/tools/ecode/plugins/debugger/busprocess.hpp @@ -1,4 +1,5 @@ #include "bus.hpp" +#include "config.hpp" #include using namespace EE::System; @@ -11,6 +12,8 @@ class BusProcess : public Bus { bool start() override; + bool close() override; + void startAsyncRead( ReadFn readFn ) override; size_t write( const char* buffer, const size_t& size ) override; diff --git a/src/tools/ecode/plugins/debugger/bussocket.cpp b/src/tools/ecode/plugins/debugger/bussocket.cpp index 51c7ee124..d66be9dc7 100644 --- a/src/tools/ecode/plugins/debugger/bussocket.cpp +++ b/src/tools/ecode/plugins/debugger/bussocket.cpp @@ -6,8 +6,21 @@ namespace ecode { BusSocket::BusSocket( const Connection& connection ) : mConnection( connection ) {} bool BusSocket::start() { - return mSocket.connect( IpAddress( mConnection.host ), mConnection.port ) == - Socket::Status::Done; + bool res = + mSocket.connect( IpAddress( mConnection.host ), mConnection.port ) == Socket::Status::Done; + if ( res ) + setState( State::Running ); + return res; +} + +bool BusSocket::close() { + if ( mState == State::Running ) { + mSocket.disconnect(); + setState( State::Closed ); + return true; + } + + return false; } void BusSocket::startAsyncRead( ReadFn readFn ) { diff --git a/src/tools/ecode/plugins/debugger/bussocket.hpp b/src/tools/ecode/plugins/debugger/bussocket.hpp index cc66f9ff4..26453726e 100644 --- a/src/tools/ecode/plugins/debugger/bussocket.hpp +++ b/src/tools/ecode/plugins/debugger/bussocket.hpp @@ -1,4 +1,5 @@ #include "bus.hpp" +#include "config.hpp" #include using namespace EE::Network; @@ -11,12 +12,14 @@ class BusSocket : public Bus { bool start() override; + bool close() override; + void startAsyncRead( ReadFn readFn ) override; size_t write( const char* buffer, const size_t& size ) override; protected: - Connection mConnection; + Connection mConnection; TcpSocket mSocket; }; diff --git a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp index 156133ca7..a3f099e9a 100644 --- a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp +++ b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp @@ -52,12 +52,22 @@ bool DebuggerClientDap::removeBreakpoint( const std::string& path, size_t line ) return false; } +bool DebuggerClientDap::isServerConnected() const { + return ( mState != State::None ) && ( mState != State::Failed ) && + ( mBus->state() == Bus::State::Running ); +} + bool DebuggerClientDap::start() { bool started = mBus->start(); if ( started ) mBus->startAsyncRead( [this]( const char* bytes, size_t n ) { asyncRead( bytes, n ); } ); mStarted = started; - + mLaunched = false; + mConfigured = false; + if ( mState != State::None ) { + Log::warning( "DebuggerClientDap::start: trying to re-start has no effect" ); + return false; + } return started; } @@ -83,23 +93,12 @@ void DebuggerClientDap::processResponseInitialize( const Response& response, // get server capabilities mAdapterCapabilities = Capabilities( response.body ); - if ( mObserver ) - mObserver->capabilitiesReceived( mAdapterCapabilities ); + for ( auto client : mClients ) + client->capabilitiesReceived( mAdapterCapabilities ); requestLaunchCommand(); } -void DebuggerClientDap::processResponseLaunch( const Response& response, const nlohmann::json& ) { - if ( response.success ) { - mLaunched = true; - if ( mObserver ) - mObserver->launched(); - checkRunning(); - } else { - setState( State::Failed ); - } -} - void DebuggerClientDap::requestLaunchCommand() { if ( mState != State::Initializing ) { @@ -112,7 +111,16 @@ void DebuggerClientDap::requestLaunchCommand() { return; makeRequest( mLaunchCommand, mProtocol.launchRequest, - makeResponseHandler( &DebuggerClientDap::processResponseLaunch, this ) ); + [this]( const auto& response, const auto& body ) { + if ( response.success ) { + mLaunched = true; + for ( auto client : mClients ) + client->launched(); + checkRunning(); + } else { + setState( State::Failed ); + } + } ); } void DebuggerClientDap::requestInitialize() { @@ -212,8 +220,8 @@ void DebuggerClientDap::processResponse( const nlohmann::json& msg ) { void DebuggerClientDap::errorResponse( const std::string& summary, const std::optional& message ) { - if ( mObserver ) - mObserver->errorResponse( summary, message ); + for ( auto client : mClients ) + client->errorResponse( summary, message ); } void DebuggerClientDap::processEvent( const nlohmann::json& msg ) { @@ -259,43 +267,43 @@ void DebuggerClientDap::processEventTerminated() { void DebuggerClientDap::processEventExited( const nlohmann::json& body ) { const int exitCode = body.value( "exitCode", -1 ); - if ( mObserver ) - mObserver->debuggeeExited( exitCode ); + for ( auto client : mClients ) + client->debuggeeExited( exitCode ); } void DebuggerClientDap::processEventOutput( const nlohmann::json& body ) { - if ( mObserver ) - mObserver->outputProduced( Output( body ) ); + for ( auto client : mClients ) + client->outputProduced( Output( body ) ); } void DebuggerClientDap::processEventProcess( const nlohmann::json& body ) { - if ( mObserver ) - mObserver->debuggingProcess( ProcessInfo( body ) ); + for ( auto client : mClients ) + client->debuggingProcess( ProcessInfo( body ) ); } void DebuggerClientDap::processEventThread( const nlohmann::json& body ) { - if ( mObserver ) - mObserver->threadChanged( ThreadEvent( body ) ); + for ( auto client : mClients ) + client->threadChanged( ThreadEvent( body ) ); } void DebuggerClientDap::processEventStopped( const nlohmann::json& body ) { - if ( mObserver ) - mObserver->debuggeeStopped( StoppedEvent( body ) ); + for ( auto client : mClients ) + client->debuggeeStopped( StoppedEvent( body ) ); } void DebuggerClientDap::processEventModule( const nlohmann::json& body ) { - if ( mObserver ) - mObserver->moduleChanged( ModuleEvent( body ) ); + for ( auto client : mClients ) + client->moduleChanged( ModuleEvent( body ) ); } void DebuggerClientDap::processEventContinued( const nlohmann::json& body ) { - if ( mObserver ) - mObserver->debuggeeContinued( ContinuedEvent( body ) ); + for ( auto client : mClients ) + client->debuggeeContinued( ContinuedEvent( body ) ); } void DebuggerClientDap::processEventBreakpoint( const nlohmann::json& body ) { - if ( mObserver ) - mObserver->breakpointChanged( BreakpointEvent( body ) ); + for ( auto client : mClients ) + client->breakpointChanged( BreakpointEvent( body ) ); } std::optional DebuggerClientDap::readHeader() { @@ -364,52 +372,122 @@ bool DebuggerClientDap::attach() { return false; } -bool DebuggerClientDap::started() const { - return mStarted; -} - -bool DebuggerClientDap::cont( int threadId ) { - return false; +bool DebuggerClientDap::cont( int threadId, bool singleThread ) { + nlohmann::json arguments{ { DAP_THREAD_ID, threadId } }; + if ( singleThread ) + arguments[DAP_SINGLE_THREAD] = true; + makeRequest( "continue", arguments, [this]( const auto& response, const auto& request ) { + if ( response.success ) { + for ( auto client : mClients ) { + client->debuggeeContinued( + ContinuedEvent( request.value( DAP_THREAD_ID, 1 ), + response.body.value( DAP_ALL_THREADS_CONTINUED, true ) ) ); + } + } + } ); + return true; } bool DebuggerClientDap::pause( int threadId ) { - return false; + nlohmann::json arguments{ { DAP_THREAD_ID, threadId } }; + makeRequest( "pause", arguments ); + return true; } -bool DebuggerClientDap::next( int threadId ) { - return false; +void DebuggerClientDap::processResponseNext( const Response& response, + const nlohmann::json& request ) { + if ( response.success ) { + for ( auto client : mClients ) + client->debuggeeContinued( + ContinuedEvent( request.value( DAP_THREAD_ID, 1 ), + !response.body.value( DAP_SINGLE_THREAD, false ) ) ); + } +} + +bool DebuggerClientDap::next( int threadId, bool singleThread ) { + nlohmann::json arguments{ { DAP_THREAD_ID, threadId } }; + if ( singleThread ) + arguments[DAP_SINGLE_THREAD] = true; + makeRequest( "next", arguments, + makeResponseHandler( &DebuggerClientDap::processResponseNext, this ) ); + return true; } bool DebuggerClientDap::goTo( int threadId, int targetId ) { - return false; + const nlohmann::json arguments{ { DAP_THREAD_ID, threadId }, { DAP_TARGET_ID, targetId } }; + makeRequest( "goto", arguments, + makeResponseHandler( &DebuggerClientDap::processResponseNext, this ) ); + return true; } -bool DebuggerClientDap::stepInto( int threadId ) { - return false; +bool DebuggerClientDap::stepInto( int threadId, bool singleThread ) { + nlohmann::json arguments{ { DAP_THREAD_ID, threadId } }; + if ( singleThread ) + arguments[DAP_SINGLE_THREAD] = true; + makeRequest( "stepIn", arguments, + makeResponseHandler( &DebuggerClientDap::processResponseNext, this ) ); + return true; } -bool DebuggerClientDap::stepOver( int threadId ) { - return false; +bool DebuggerClientDap::stepOut( int threadId, bool singleThread ) { + nlohmann::json arguments{ { DAP_THREAD_ID, threadId } }; + if ( singleThread ) + arguments[DAP_SINGLE_THREAD] = true; + makeRequest( "stepOut", arguments, + makeResponseHandler( &DebuggerClientDap::processResponseNext, this ) ); + return true; } -bool DebuggerClientDap::stepOut( int threadId ) { - return false; +bool DebuggerClientDap::terminate( bool restart ) { + nlohmann::json arguments; + if ( restart ) + arguments["restart"] = true; + makeRequest( "terminate", arguments ); + return true; } -bool DebuggerClientDap::halt() { - return false; +bool DebuggerClientDap::disconnect( bool restart ) { + nlohmann::json arguments; + if ( restart ) + arguments["restart"] = true; + + makeRequest( "disconnect", arguments, [this]( const auto& response, const auto& request ) { + if ( response.success ) { + for ( auto client : mClients ) + client->serverDisconnected(); + } + } ); + return true; } -bool DebuggerClientDap::terminate() { - return false; +bool DebuggerClientDap::threads() { + makeRequest( DAP_THREADS, {}, [this]( const auto& response, const auto& request ) { + if ( response.success ) { + for ( auto client : mClients ) + client->threads( Thread::parseList( response.body[DAP_THREADS] ) ); + } else { + for ( auto client : mClients ) + client->threads( {} ); + } + } ); + return true; } -bool DebuggerClientDap::stopped() { - return false; -} +bool DebuggerClientDap::stackTrace( int threadId, int startFrame, int levels ) { + const nlohmann::json arguments{ + { DAP_THREAD_ID, threadId }, { "startFrame", startFrame }, { "levels", levels } }; -bool DebuggerClientDap::completed() { - return false; + makeRequest( "stackTrace", arguments, [this]( const auto& response, const auto& request ) { + const int threadId = request.value( DAP_THREAD_ID, 1 ); + if ( response.success ) { + for ( auto client : mClients ) + client->stackTrace( threadId, StackTraceInfo( response.body ) ); + } else { + for ( auto client : mClients ) + client->stackTrace( threadId, StackTraceInfo() ); + } + } ); + return true; } } // namespace ecode::dap diff --git a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp index 25c3ea9dd..4e4cdaad9 100644 --- a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp +++ b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp @@ -1,10 +1,12 @@ #pragma once #include "../bus.hpp" +#include "../config.hpp" #include "../debuggerclient.hpp" #include "protocol.hpp" #include #include +#include using namespace EE; @@ -16,39 +18,37 @@ class DebuggerClientDap : public DebuggerClient { DebuggerClientDap( std::unique_ptr&& bus ); - bool hasBreakpoint( const std::string& path, size_t line ); + bool hasBreakpoint( const std::string& path, size_t line ) override; - bool addBreakpoint( const std::string& path, size_t line ); + bool addBreakpoint( const std::string& path, size_t line ) override; - bool removeBreakpoint( const std::string& path, size_t line ); + bool removeBreakpoint( const std::string& path, size_t line ) override; - bool start(); + bool start() override; - bool attach(); + bool attach() override; - bool started() const; + bool cont( int threadId, bool singleThread = false ) override; - bool cont( int threadId ); + bool pause( int threadId ) override; - bool pause( int threadId ) = 0; + bool next( int threadId, bool singleThread = false ) override; - bool next( int threadId ) = 0; + bool goTo( int threadId, int targetId ) override; - bool goTo( int threadId, int targetId ) = 0; + bool stepInto( int threadId, bool singleThread = false ) override; - bool stepInto( int threadId ); + bool stepOut( int threadId, bool singleThread = false ) override; - bool stepOver( int threadId ); + bool terminate( bool restart ) override; - bool stepOut( int threadId ); + bool disconnect( bool restart = false ) override; - bool halt(); + bool threads() override; - bool terminate(); + bool stackTrace( int threadId, int startFrame, int levels ) override; - bool stopped(); - - bool completed(); + bool isServerConnected() const override; protected: std::unique_ptr mBus; @@ -113,8 +113,7 @@ class DebuggerClientDap : public DebuggerClient { void requestLaunchCommand(); void processResponseInitialize( const Response& response, const nlohmann::json& ); - - void processResponseLaunch( const Response& response, const nlohmann::json& ); + void processResponseNext( const Response& response, const nlohmann::json& request ); }; } // namespace ecode::dap diff --git a/src/tools/ecode/plugins/debugger/debuggerclient.cpp b/src/tools/ecode/plugins/debugger/debuggerclient.cpp index 343587af4..33019fe97 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclient.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerclient.cpp @@ -3,28 +3,28 @@ namespace ecode { void DebuggerClient::stateChanged( State state ) { - if ( mObserver ) - mObserver->stateChanged( state ); + for ( auto client : mClients ) + client->stateChanged( state ); } void DebuggerClient::initialized() { - if ( mObserver ) - mObserver->initialized(); + for ( auto client : mClients ) + client->initialized(); } void DebuggerClient::debuggeeRunning() { - if ( mObserver ) - mObserver->debuggeeRunning(); + for ( auto client : mClients ) + client->debuggeeRunning(); } void DebuggerClient::debuggeeTerminated() { - if ( mObserver ) - mObserver->debuggeeRunning(); + for ( auto client : mClients ) + client->debuggeeRunning(); } void DebuggerClient::failed() { - if ( mObserver ) - mObserver->failed(); + for ( auto client : mClients ) + client->failed(); } void DebuggerClient::setState( const State& state ) { diff --git a/src/tools/ecode/plugins/debugger/debuggerclient.hpp b/src/tools/ecode/plugins/debugger/debuggerclient.hpp index 8bbd8c52f..e1fcc72ad 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclient.hpp +++ b/src/tools/ecode/plugins/debugger/debuggerclient.hpp @@ -11,7 +11,7 @@ class DebuggerClient { public: enum class State { None, Initializing, Initialized, Running, Terminated, Failed }; - class Observer { + class Client { public: virtual void stateChanged( State ) = 0; virtual void initialized() = 0; @@ -60,29 +60,27 @@ class DebuggerClient { virtual bool attach() = 0; - virtual bool started() const = 0; - - virtual bool cont( int threadId ) = 0; + virtual bool cont( int threadId, bool singleThread = false ) = 0; virtual bool pause( int threadId ) = 0; - virtual bool next( int threadId ) = 0; + virtual bool next( int threadId, bool singleThread = false ) = 0; virtual bool goTo( int threadId, int targetId ) = 0; - virtual bool stepInto( int threadId ) = 0; + virtual bool stepInto( int threadId, bool singleThread = false ) = 0; - virtual bool stepOver( int threadId ) = 0; + virtual bool stepOut( int threadId, bool singleThread = false ) = 0; - virtual bool stepOut( int threadId ) = 0; + virtual bool terminate( bool restart ) = 0; - virtual bool halt() = 0; + virtual bool disconnect( bool restart = false ) = 0; - virtual bool terminate() = 0; + virtual bool threads() = 0; - virtual bool stopped() = 0; + virtual bool stackTrace( int threadId, int startFrame, int levels ) = 0; - virtual bool completed() = 0; + virtual bool isServerConnected() const = 0; protected: void setState( const State& state ); @@ -90,7 +88,7 @@ class DebuggerClient { State mState{ State::None }; bool mLaunched{ false }; bool mConfigured{ false }; - Observer* mObserver{ nullptr }; + std::vector mClients; void checkRunning();