diff --git a/src/tools/ecode/plugins/debugger/config.cpp b/src/tools/ecode/plugins/debugger/config.cpp index 4b842a6fe..f33136ec4 100644 --- a/src/tools/ecode/plugins/debugger/config.cpp +++ b/src/tools/ecode/plugins/debugger/config.cpp @@ -2,6 +2,34 @@ namespace ecode { - - +bool Command::isValid() const { + return !command.empty(); } + +bool Connection::isValid() const { + return ( port > 0 ) && !host.empty(); +} + +bool BusSettings::isValid() const { + return hasCommand() || hasConnection(); +} + +bool BusSettings::hasCommand() const { + return command && command->isValid(); +} + +bool BusSettings::hasConnection() const { + return connection && connection->isValid(); +} + +ProtocolSettings::ProtocolSettings( const nlohmann::json& configuration ) : + linesStartAt1( true ), + columnsStartAt1( true ), + pathFormatURI( false ), + redirectStderr( configuration.value( REDIRECT_STDERR, false ) ), + redirectStdout( configuration.value( REDIRECT_STDOUT, false ) ), + supportsSourceRequest( configuration.value( "supportsSourceRequest", true ) ), + launchRequest( configuration[REQUEST] ), + locale( "en-US" ) {} + +} // namespace ecode diff --git a/src/tools/ecode/plugins/debugger/config.hpp b/src/tools/ecode/plugins/debugger/config.hpp index c81f30e6b..76db1f096 100644 --- a/src/tools/ecode/plugins/debugger/config.hpp +++ b/src/tools/ecode/plugins/debugger/config.hpp @@ -15,15 +15,21 @@ using namespace EE; namespace ecode { +static const auto REQUEST = "request"sv; +static const auto REDIRECT_STDERR = "redirectStderr"sv; +static const auto REDIRECT_STDOUT = "redirectStdout"sv; + struct Command { std::string command; std::vector arguments; std::unordered_map environment; + bool isValid() const; }; struct Connection { int port; std::string host; + bool isValid() const; }; struct BusSettings { @@ -35,38 +41,19 @@ struct BusSettings { bool hasConnection() const; }; -static const auto REQUEST = "request"sv; -static const auto REDIRECT_STDERR = "redirectStderr"sv; -static const auto REDIRECT_STDOUT = "redirectStdout"sv; - struct ProtocolSettings { - bool linesStartAt1; - bool columnsStartAt1; - bool pathFormatURI; - bool redirectStderr; - bool redirectStdout; - bool supportsSourceRequest; + bool linesStartAt1{ true }; + bool columnsStartAt1{ true }; + bool pathFormatURI{ false }; + bool redirectStderr{ false }; + bool redirectStdout{ false }; + bool supportsSourceRequest{ true }; json launchRequest; - std::string locale; + std::string locale{ "en-US" }; - ProtocolSettings() : - linesStartAt1( true ), - columnsStartAt1( true ), - pathFormatURI( false ), - redirectStderr( false ), - redirectStdout( false ), - supportsSourceRequest( true ), - locale( "en-US" ) {} + ProtocolSettings() = default; - ProtocolSettings( const nlohmann::json& configuration ) : - linesStartAt1( true ), - columnsStartAt1( true ), - pathFormatURI( false ), - redirectStderr( configuration.value( REDIRECT_STDERR, false ) ), - redirectStdout( configuration.value( REDIRECT_STDOUT, false ) ), - supportsSourceRequest( configuration.value( "supportsSourceRequest", true ) ), - launchRequest( configuration[REQUEST] ), - locale( "en-US" ) {} + ProtocolSettings( const nlohmann::json& configuration ); }; } // namespace ecode diff --git a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp index 45fd0b90c..7ffaef232 100644 --- a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp +++ b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp @@ -18,7 +18,21 @@ makeResponseHandler( void ( T::*member )( const Response& response, const nlohma }; } -DebuggerClientDap::DebuggerClientDap( std::unique_ptr&& bus ) : mBus( std::move( bus ) ) {} +static std::string extractCommand( const nlohmann::json& launchRequest ) { + auto command = launchRequest.value( DAP_COMMAND, "" ); + if ( ( command != DAP_LAUNCH ) && ( command != DAP_ATTACH ) ) { + Log::warning( "DebuggerClientDap::extractCommand unsupported request command: %s", + command ); + return ""; + } + return command; +} + +DebuggerClientDap::DebuggerClientDap( const ProtocolSettings& protocolSettings, + std::unique_ptr&& bus ) : + mBus( std::move( bus ) ), + mProtocol( protocolSettings ), + mLaunchCommand( extractCommand( protocolSettings.launchRequest ) ) {} void DebuggerClientDap::makeRequest( const std::string_view& command, const nlohmann::json& arguments, ResponseHandler onFinish ) { @@ -61,6 +75,7 @@ bool DebuggerClientDap::start() { Log::warning( "DebuggerClientDap::start: trying to re-start has no effect" ); return false; } + requestInitialize(); return started; } @@ -93,7 +108,6 @@ void DebuggerClientDap::processResponseInitialize( const Response& response, } void DebuggerClientDap::requestLaunchCommand() { - if ( mState != State::Initializing ) { Log::warning( "DebuggerClientDap::requestLaunchCommand: trying to launch in an unexpected state" ); @@ -111,6 +125,10 @@ void DebuggerClientDap::requestLaunchCommand() { client->launched(); checkRunning(); } else { + if ( response.errorBody ) { + Log::warning( "DebuggerClientDap::requestLaunchCommand: error %ld %s", + response.errorBody->id, response.errorBody->format ); + } setState( State::Failed ); } } ); @@ -368,11 +386,7 @@ std::optional DebuggerClientDap::readHeader() { return HeaderInfo{ end, length }; } -bool DebuggerClientDap::attach() { - return false; -} - -bool DebuggerClientDap::cont( int threadId, bool singleThread ) { +bool DebuggerClientDap::resume( int threadId, bool singleThread ) { nlohmann::json arguments{ { DAP_THREAD_ID, threadId } }; if ( singleThread ) arguments[DAP_SINGLE_THREAD] = true; @@ -405,7 +419,7 @@ void DebuggerClientDap::processResponseNext( const Response& response, } } -bool DebuggerClientDap::next( int threadId, bool singleThread ) { +bool DebuggerClientDap::stepOver( int threadId, bool singleThread ) { nlohmann::json arguments{ { DAP_THREAD_ID, threadId } }; if ( singleThread ) arguments[DAP_SINGLE_THREAD] = true; @@ -552,6 +566,46 @@ bool DebuggerClientDap::variables( int variablesReference, Variable::Type filter return true; } +bool DebuggerClientDap::modules( int start, int count ) { + makeRequest( DAP_MODULES, { { DAP_START, start }, { DAP_COUNT, count } }, + [this]( const auto& response, const auto& request ) { + if ( response.success ) { + ModulesInfo info( response.body ); + for ( auto client : mClients ) + client->modules( info ); + } else { + ModulesInfo info; + for ( auto client : mClients ) + client->modules( info ); + } + } ); + return true; +} + +bool DebuggerClientDap::evaluate( const std::string& expression, const std::string& context, + std::optional frameId ) { + nlohmann::json arguments{ { DAP_EXPRESSION, expression } }; + if ( !context.empty() ) + arguments[DAP_CONTEXT] = context; + if ( frameId ) + arguments[DAP_FRAME_ID] = *frameId; + + makeRequest( "evaluate", arguments, [this]( const auto& response, const auto& request ) { + auto expression = request.value( DAP_EXPRESSION, "" ); + if ( response.success ) { + EvaluateInfo info( response.body ); + + for ( auto client : mClients ) + client->expressionEvaluated( expression, info ); + } else { + for ( auto client : mClients ) + client->expressionEvaluated( expression, std::nullopt ); + } + } ); + + return true; +} + bool DebuggerClientDap::setBreakpoints( const std::string& path, const std::vector breakpoints, bool sourceModified ) { @@ -605,4 +659,36 @@ bool DebuggerClientDap::setBreakpoints( const dap::Source& source, return true; } +bool DebuggerClientDap::gotoTargets( const std::string& path, const int line, + const std::optional column ) { + return gotoTargets( Source( path ), line, column ); +} + +bool DebuggerClientDap::gotoTargets( const Source& source, const int line, + const std::optional column ) { + nlohmann::json arguments{ { DAP_SOURCE, source.toJson() }, { DAP_LINE, line } }; + if ( column ) + arguments[DAP_COLUMN] = *column; + + makeRequest( "gotoTargets", arguments, [this]( const auto& response, const auto& req ) { + const auto source = Source( req[DAP_SOURCE] ); + const int line = req.value( DAP_LINE, 1 ); + if ( response.success ) { + auto list = GotoTarget::parseList( response.body["targets"] ); + for ( auto client : mClients ) + client->gotoTargets( source, line, list ); + } else { + std::vector list; + for ( auto client : mClients ) + client->gotoTargets( source, line, list ); + } + } ); + + return true; +} + +bool DebuggerClientDap::watch( const std::string& expression, std::optional frameId ) { + return evaluate( expression, "watch", frameId ); +} + } // namespace ecode::dap diff --git a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp index 0ca017c66..0270af4c7 100644 --- a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp +++ b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp @@ -16,17 +16,15 @@ class DebuggerClientDap : public DebuggerClient { public: typedef std::function ResponseHandler; - DebuggerClientDap( std::unique_ptr&& bus ); + DebuggerClientDap( const ProtocolSettings& protocolSettings, std::unique_ptr&& bus ); bool start() override; - bool attach() override; - - bool cont( int threadId, bool singleThread = false ) override; + bool resume( int threadId, bool singleThread = false ) override; bool pause( int threadId ) override; - bool next( int threadId, bool singleThread = false ) override; + bool stepOver( int threadId, bool singleThread = false ) override; bool goTo( int threadId, int targetId ) override; @@ -44,19 +42,32 @@ class DebuggerClientDap : public DebuggerClient { bool scopes( int frameId ) override; - bool variables( int variablesReference, Variable::Type filter, int start, int count ); + bool variables( int variablesReference, Variable::Type filter, int start, int count ) override; + + bool modules( int start, int count ) override; + + bool evaluate( const std::string& expression, const std::string& context, + std::optional frameId ) override; bool isServerConnected() const override; bool supportsTerminate() const override; bool setBreakpoints( const std::string& path, - const std::vector breakpoints, - bool sourceModified = false ) override; + const std::vector breakpoints, + bool sourceModified = false ) override; bool setBreakpoints( const dap::Source& source, - const std::vector breakpoints, - bool sourceModified = false ) override; + const std::vector breakpoints, + bool sourceModified = false ) override; + + bool gotoTargets( const std::string& path, const int line, + const std::optional column = std::nullopt ) override; + + bool gotoTargets( const dap::Source& source, const int line, + const std::optional column = std::nullopt ) override; + + bool watch( const std::string& expression, std::optional frameId ) override; protected: std::unique_ptr mBus; diff --git a/src/tools/ecode/plugins/debugger/debuggerclient.hpp b/src/tools/ecode/plugins/debugger/debuggerclient.hpp index 20e6f5f87..6351270d9 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclient.hpp +++ b/src/tools/ecode/plugins/debugger/debuggerclient.hpp @@ -53,11 +53,11 @@ class DebuggerClient { virtual bool attach() = 0; - virtual bool cont( int threadId, bool singleThread = false ) = 0; + virtual bool resume( int threadId, bool singleThread = false ) = 0; virtual bool pause( int threadId ) = 0; - virtual bool next( int threadId, bool singleThread = false ) = 0; + virtual bool stepOver( int threadId, bool singleThread = false ) = 0; virtual bool goTo( int threadId, int targetId ) = 0; @@ -75,6 +75,14 @@ class DebuggerClient { virtual bool scopes( int frameId ) = 0; + virtual bool modules( int start, int count ) = 0; + + virtual bool variables( int variablesReference, Variable::Type filter, int start, + int count ) = 0; + + virtual bool evaluate( const std::string& expression, const std::string& context, + std::optional frameId ) = 0; + virtual bool isServerConnected() const = 0; virtual bool supportsTerminate() const = 0; @@ -87,6 +95,14 @@ class DebuggerClient { const std::vector breakpoints, bool sourceModified = false ) = 0; + virtual bool gotoTargets( const std::string& path, const int line, + const std::optional column = std::nullopt ) = 0; + + virtual bool gotoTargets( const dap::Source& source, const int line, + const std::optional column = std::nullopt ) = 0; + + virtual bool watch( const std::string& expression, std::optional frameId ) = 0; + protected: void setState( const State& state );