diff --git a/bin/assets/plugins/debugger.json b/bin/assets/plugins/debugger.json index acfd49de2..b69f0331f 100644 --- a/bin/assets/plugins/debugger.json +++ b/bin/assets/plugins/debugger.json @@ -254,7 +254,7 @@ "languages": ["javascript", "typescript"], "configurations": [ { - "name": "Launch Node.js program", + "name": "Launch script", "request": "launch", "arguments": { "type": "pwa-node", @@ -263,7 +263,39 @@ "cwd": "${cwd}", "env": "${env}", "runtimeExecutable": "node", - "console": "externalConsole" + "autoAttachChildProcesses": true, + "enableDWARF":true, + "enableContentValidation":true, + "outputCapture":"console", + "timeout":10000, + "showAsyncStacks":true, + "smartStep":true, + "sourceMaps":true, + "sourceMapRenames":true, + "pauseForSourceMap":false + } + }, + { + "name": "Launch script in Terminal", + "request": "launch", + "arguments": { + "type": "pwa-node", + "program": "${file}", + "args": "${args}", + "cwd": "${cwd}", + "env": "${env}", + "runtimeExecutable": "node", + "console": "externalConsole", + "autoAttachChildProcesses": true, + "enableDWARF":true, + "enableContentValidation":true, + "outputCapture":"console", + "timeout":10000, + "showAsyncStacks":true, + "smartStep":true, + "sourceMaps":true, + "sourceMapRenames":true, + "pauseForSourceMap":false } }, { diff --git a/projects/linux/ee.creator.user b/projects/linux/ee.creator.user index c90be2ede..68e00f91b 100644 --- a/projects/linux/ee.creator.user +++ b/projects/linux/ee.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -1199,7 +1199,6 @@ ProjectExplorer.CustomExecutableRunConfiguration true - --text-shaper 0 false 1 diff --git a/src/eepp/network/tcpsocket.cpp b/src/eepp/network/tcpsocket.cpp index 9849c44ac..503ffecad 100644 --- a/src/eepp/network/tcpsocket.cpp +++ b/src/eepp/network/tcpsocket.cpp @@ -355,7 +355,8 @@ void TcpSocket::startAsyncRead( ReadFn readFn ) { while ( receive( buffer.data(), buffer.size(), received ) == Status::Done && received > 0 ) readFn( buffer.c_str(), received ); - if ( clock.getElapsedTime().asMilliseconds() < 100.f ) { + if ( mSocket != Private::SocketImpl::invalidSocket() && + clock.getElapsedTime().asMilliseconds() < 100.f ) { auto ms = 100.f - clock.getElapsedTime().asMilliseconds(); Sys::sleep( Milliseconds( ms ) ); } diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 63aa25bc1..d899ed703 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -1204,8 +1204,9 @@ void App::loadFileFromPathOrFocus( } } -void App::focusOrLoadFile( const std::string& path, const TextRange& range ) { - UITab* tab = mSplitter->isDocumentOpen( path, true ); +void App::focusOrLoadFile( const std::string& path, const TextRange& range, + bool searchInSameContext ) { + UITab* tab = mSplitter->isDocumentOpen( path, searchInSameContext ); if ( !tab ) { FileInfo fileInfo( path ); if ( fileInfo.exists() && fileInfo.isRegularFile() ) diff --git a/src/tools/ecode/ecode.hpp b/src/tools/ecode/ecode.hpp index d5d75d603..659704b06 100644 --- a/src/tools/ecode/ecode.hpp +++ b/src/tools/ecode/ecode.hpp @@ -411,7 +411,8 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider { std::function onLoaded = std::function() ); - void focusOrLoadFile( const std::string& path, const TextRange& range = {} ); + void focusOrLoadFile( const std::string& path, const TextRange& range = {}, + bool searchInSameContext = true ); UISceneNode* getUISceneNode() const { return mUISceneNode; } diff --git a/src/tools/ecode/plugins/debugger/bus.hpp b/src/tools/ecode/plugins/debugger/bus.hpp index c68d35143..6a4154f35 100644 --- a/src/tools/ecode/plugins/debugger/bus.hpp +++ b/src/tools/ecode/plugins/debugger/bus.hpp @@ -8,6 +8,8 @@ class Bus { public: enum class State { None, Running, Closed }; + enum class Type { Process, Socket, SocketProcess }; + typedef std::function ReadFn; State state() const; @@ -20,6 +22,8 @@ class Bus { virtual size_t write( const char* buffer, const size_t& size ) = 0; + virtual Type type() const = 0; + virtual bool hasProcess() { return false; } virtual ~Bus() {} diff --git a/src/tools/ecode/plugins/debugger/busprocess.hpp b/src/tools/ecode/plugins/debugger/busprocess.hpp index f5cd6d918..c8d3782d5 100644 --- a/src/tools/ecode/plugins/debugger/busprocess.hpp +++ b/src/tools/ecode/plugins/debugger/busprocess.hpp @@ -22,6 +22,10 @@ class BusProcess : public Bus { bool hasProcess() override { return true; } + Type type() const override { return Bus::Type::Process; } + + const Command& getCommand() const { return mCommand; } + protected: Command mCommand; Process mProcess; diff --git a/src/tools/ecode/plugins/debugger/bussocket.hpp b/src/tools/ecode/plugins/debugger/bussocket.hpp index b789d133b..c9766234a 100644 --- a/src/tools/ecode/plugins/debugger/bussocket.hpp +++ b/src/tools/ecode/plugins/debugger/bussocket.hpp @@ -22,6 +22,10 @@ class BusSocket : public Bus { bool hasProcess() override { return false; } + Type type() const override { return Bus::Type::Socket; } + + const Connection& getConnection() const { return mConnection; } + protected: Connection mConnection; TcpSocket mSocket; diff --git a/src/tools/ecode/plugins/debugger/bussocketprocess.hpp b/src/tools/ecode/plugins/debugger/bussocketprocess.hpp index 424c4e61d..1be05fb69 100644 --- a/src/tools/ecode/plugins/debugger/bussocketprocess.hpp +++ b/src/tools/ecode/plugins/debugger/bussocketprocess.hpp @@ -22,6 +22,12 @@ class BusSocketProcess : public Bus { bool hasProcess() override { return true; } + Type type() const override { return Bus::Type::SocketProcess; } + + const Command& getCommand() const { return mCommand; } + + const Connection& getConnection() const { return mConnection; } + protected: Command mCommand; Connection mConnection; diff --git a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp index 0698db750..1c37d5549 100644 --- a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp +++ b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp @@ -1,8 +1,13 @@ #include "debuggerclientdap.hpp" +#include "../busprocess.hpp" +#include "../bussocket.hpp" +#include "../bussocketprocess.hpp" #include "messages.hpp" #include +#include #include #include +#include using namespace EE::System; @@ -11,26 +16,41 @@ namespace ecode::dap { constexpr int MAX_HEADER_SIZE = 1 << 16; template -inline DebuggerClientDap::ResponseHandler -makeResponseHandler( void ( T::*member )( const Response& response, const nlohmann::json& request ), - T* object ) { - return [object, member]( const Response& response, const nlohmann::json& request ) { - return ( object->*member )( response, request ); +inline DebuggerClientDap::ResponseHandler makeResponseHandler( + void ( T::*member )( const Response&, const nlohmann::json&, const std::string& ), T* object, + const SessionId& sessionId ) { + return [object, member, sessionId]( const Response& response, const nlohmann::json& request ) { + ( object->*member )( response, request, sessionId ); }; } DebuggerClientDap::DebuggerClientDap( const ProtocolSettings& protocolSettings, std::unique_ptr&& bus ) : - mBus( std::move( bus ) ), mProtocol( protocolSettings ) {} + mProtocol( protocolSettings ) { + mCurrentSessionId = "main"; + Lock l( mSessionsMutex ); + mSessions[mCurrentSessionId].bus = std::move( bus ); +} DebuggerClientDap::~DebuggerClientDap() { - mBus.reset(); + mDestroying = true; + mBusesToClose.clear(); + + { + Lock l( mSessionsMutex ); + for ( auto& s : mSessions ) + if ( s.second.bus ) + s.second.bus.reset(); + } } void DebuggerClientDap::makeRequest( const std::string_view& command, - const nlohmann::json& arguments, ResponseHandler onFinish ) { + const nlohmann::json& arguments, ResponseHandler onFinish, + const SessionId& sessionId ) { + auto& session = mSessions[sessionId]; + nlohmann::json jsonCmd = { - { "seq", mIdx.load() }, + { "seq", session.idx.load() }, { "type", "request" }, { "command", command }, { "arguments", arguments.empty() ? nlohmann::json::object() : arguments } }; @@ -39,24 +59,27 @@ void DebuggerClientDap::makeRequest( const std::string_view& command, std::string msg( String::format( "Content-Length: %zu\r\n\r\n%s", cmd.size(), cmd ) ); if ( mDebug ) { - Log::debug( "DebuggerClientDap::makeRequest:" ); + Log::debug( "DebuggerClientDap::makeRequest [Session %s]:", sessionId ); Log::debug( msg ); } - mBus->write( msg.data(), msg.size() ); + if ( !session.bus ) + return; - mRequests[mIdx] = { std::string{ command }, arguments, onFinish }; + session.bus->write( msg.data(), msg.size() ); - mIdx.fetch_add( 1, std::memory_order_relaxed ); + mRequests[session.idx] = { std::string{ command }, arguments, onFinish, sessionId }; + session.idx.fetch_add( 1, std::memory_order_relaxed ); } void DebuggerClientDap::makeResponse( int reqSeq, bool success, const std::string& command, - const nlohmann::json& body ) { - mIdx = reqSeq + 1; + const nlohmann::json& body, const std::string& sessionId ) { + auto& session = mSessions[sessionId]; + session.idx.fetch_add( 1, std::memory_order_relaxed ); nlohmann::json jsonCmd = { { "type", "response" }, { "request_seq", reqSeq }, - { "seq", mIdx.load() }, + { "seq", session.idx.load() }, { "success", success }, { "command", command } }; @@ -71,12 +94,19 @@ void DebuggerClientDap::makeResponse( int reqSeq, bool success, const std::strin Log::debug( msg ); } - mBus->write( msg.data(), msg.size() ); + if ( !mSessions[sessionId].bus ) + return; + + mSessions[sessionId].bus->write( msg.data(), msg.size() ); } bool DebuggerClientDap::isServerConnected() const { - return ( mState != State::None ) && ( mState != State::Failed ) && - ( mBus->state() == Bus::State::Running ); + if ( !mSessions.empty() ) { + auto sessionIt = mSessions.find( mCurrentSessionId ); + if ( sessionIt != mSessions.end() && sessionIt->second.bus ) + return sessionIt->second.bus->state() == Bus::State::Running; + } + return false; } bool DebuggerClientDap::supportsTerminateRequest() const { @@ -89,40 +119,39 @@ bool DebuggerClientDap::supportsTerminateDebuggee() const { bool DebuggerClientDap::start() { bool started = false; - if ( mBus && ( started = mBus->start() ) ) - mBus->startAsyncRead( [this]( const char* bytes, size_t n ) { asyncRead( bytes, n ); } ); - else { + if ( mSessions[mCurrentSessionId].bus && + ( started = mSessions[mCurrentSessionId].bus->start() ) ) { + mSessions[mCurrentSessionId].bus->startAsyncRead( + [this]( const char* bytes, size_t n ) { asyncRead( bytes, n ); } ); + } else { Log::warning( "DebuggerClientDap::start: could not initialize the debugger" ); return false; } mStarted = started; - mLaunched = false; - mConfigured = false; - if ( mState != State::None ) { - Log::warning( "DebuggerClientDap::start: trying to re-start has no effect" ); - return false; - } - requestInitialize(); + requestInitialize( mCurrentSessionId ); return started; } -void DebuggerClientDap::processResponseInitialize( const Response& response, - const nlohmann::json& ) { - if ( mState != State::Initializing ) { - Log::warning( - "DebuggerClientDap::processResponseInitialize: unexpected initialize response" ); - setState( State::None ); +void DebuggerClientDap::processResponseInitialize( const Response& response, const nlohmann::json&, + const SessionId& sessionId, + std::function onLaunch ) { + Session& session = mSessions[sessionId]; + if ( session.state != State::Initializing ) { + Log::warning( "DebuggerClientDap::processResponseInitialize [Session %s]: unexpected " + "initialize response", + sessionId ); + setState( State::None, sessionId ); return; } - if ( !response.success && response.isCancelled() ) { - Log::warning( "DebuggerClientDap::processResponseInitialize: InitializeResponse error: %s", - response.message ); + if ( !response.success ) { + Log::warning( "DebuggerClientDap::processResponseInitialize [Session %s]: error: %s", + sessionId, response.message ); if ( response.errorBody ) { - Log::warning( "DebuggerClientDap::processResponseInitialize: error %ld %s", - response.errorBody->id, response.errorBody->format ); + Log::warning( "DebuggerClientDap::processResponseInitialize [Session %s]: error %ld %s", + sessionId, response.errorBody->id, response.errorBody->format ); } - setState( State::None ); + setState( State::None, sessionId ); return; } @@ -131,47 +160,59 @@ void DebuggerClientDap::processResponseInitialize( const Response& response, for ( auto listener : mListeners ) listener->capabilitiesReceived( mAdapterCapabilities ); - requestLaunchCommand(); + requestLaunchCommand( sessionId, onLaunch ); } -void DebuggerClientDap::requestLaunchCommand( std::function onLaunch ) { - if ( mState != State::Initializing ) { - Log::warning( - "DebuggerClientDap::requestLaunchCommand: trying to launch in an unexpected state" ); +void DebuggerClientDap::requestLaunchCommand( const SessionId& sessionId, + std::function onLaunch ) { + Session& session = mSessions[sessionId]; + if ( session.state != State::Initializing ) { + Log::warning( "DebuggerClientDap::requestLaunchCommand [Session %s]: unexpected state", + sessionId ); return; } - if ( mProtocol.launchRequestType.empty() ) + if ( session.launchRequestType.empty() ) + session.launchRequestType = mProtocol.launchRequestType; + if ( session.launchArgs.empty() ) + session.launchArgs = mProtocol.launchArgs; + + if ( session.launchRequestType.empty() ) return; - makeRequest( mProtocol.launchRequestType, mProtocol.launchArgs, - [this, onLaunch = std::move( onLaunch )]( const Response& response, const auto& ) { - if ( response.success ) { - mLaunched = true; - checkRunning(); - for ( auto listener : mListeners ) - listener->launched(); - } else { - if ( response.errorBody ) { - Log::warning( "DebuggerClientDap::requestLaunchCommand: error %ld %s", - response.errorBody->id, response.errorBody->format ); - } - setState( State::Failed ); - } - - if ( onLaunch ) - onLaunch(); - } ); + makeRequest( + session.launchRequestType, session.launchArgs, + [this, sessionId, onLaunch = std::move( onLaunch )]( const Response& response, + const auto& ) { + Session& session = mSessions[sessionId]; + if ( response.success ) { + session.launched = true; + checkRunning( sessionId ); + for ( auto listener : mListeners ) + listener->launched( sessionId ); + } else { + if ( response.errorBody ) { + Log::warning( + "DebuggerClientDap::requestLaunchCommand [Session %s]: error %ld %s", + sessionId, response.errorBody->id, response.errorBody->format ); + } + setState( State::Failed, sessionId ); + } + if ( onLaunch ) + onLaunch(); + }, + sessionId ); if ( mProtocol.runTarget && runTargetCb ) { - if ( !mProtocol.launchArgs.contains( "waitFor" ) ) + if ( !session.launchArgs.contains( "waitFor" ) ) runTargetCb(); else - mWaitingToAttach = true; + session.waitingToAttach = true; } } -void DebuggerClientDap::requestInitialize() { +void DebuggerClientDap::requestInitialize( const SessionId& sessionId, + std::function onLaunch ) { const nlohmann::json capabilities{ { DAP_CLIENT_ID, "ecode" }, { DAP_CLIENT_NAME, "ecode" }, @@ -188,9 +229,14 @@ void DebuggerClientDap::requestInitialize() { { DAP_SUPPORTS_INVALIDATED_EVENT, false }, { DAP_SUPPORTS_MEMORY_EVENT, false } }; - setState( State::Initializing ); - makeRequest( DAP_INITIALIZE, capabilities, - makeResponseHandler( &DebuggerClientDap::processResponseInitialize, this ) ); + setState( State::Initializing, sessionId ); + makeRequest( + DAP_INITIALIZE, capabilities, + [this, sessionId, onLaunch = std::move( onLaunch )]( const Response& response, + const nlohmann::json& request ) { + processResponseInitialize( response, request, sessionId, onLaunch ); + }, + sessionId ); } void DebuggerClientDap::asyncRead( const char* bytes, size_t n ) { @@ -209,16 +255,14 @@ void DebuggerClientDap::asyncRead( const char* bytes, size_t n ) { #ifndef EE_DEBUG try { #endif - auto message = json::parse( data ); - + auto message = nlohmann::json::parse( data ); if ( mDebug ) { Log::debug( "DebuggerClientDap::asyncRead:" ); Log::debug( message.dump() ); } - processProtocolMessage( message ); #ifndef EE_DEBUG - } catch ( const json::exception& e ) { + } catch ( const nlohmann::json::exception& e ) { Log::error( "DebuggerClientDap::asyncRead: JSON bad format: %s", e.what() ); } #endif @@ -227,7 +271,6 @@ void DebuggerClientDap::asyncRead( const char* bytes, size_t n ) { void DebuggerClientDap::processProtocolMessage( const nlohmann::json& msg ) { const auto type = msg.value( DAP_TYPE, "" ); - if ( DAP_RESPONSE == type ) { processResponse( msg ); } else if ( DAP_EVENT == type ) { @@ -235,9 +278,7 @@ void DebuggerClientDap::processProtocolMessage( const nlohmann::json& msg ) { } else if ( DAP_REQUEST == type ) { processRequest( msg ); } else { - Log::warning( "DebuggerClientDap::processProtocolMessage: unknown, empty or unexpected " - "ProtocolMessage::%s (%s)", - DAP_TYPE, type ); + Log::warning( "DebuggerClientDap::processProtocolMessage: unknown type: %s", type ); } } @@ -254,73 +295,105 @@ void DebuggerClientDap::processRequest( const nlohmann::json& msg ) { bool isIntegrated = args.value( "kind", "integrated" ) == "integrated"; std::vector largs; if ( args.contains( "args" ) && args["args"].is_array() ) { - auto& jargs = args["args"]; - - for ( const auto& jarg : jargs ) { + for ( const auto& jarg : args["args"] ) { if ( jarg.is_string() ) largs.emplace_back( jarg.get() ); } } std::unordered_map lenv; - if ( args.contains( "env" ) && args["env"].is_object() ) { for ( auto& [key, value] : args["env"].items() ) { if ( value.is_string() ) lenv.emplace( key, value.get() ); } } - std::string cwd = args.value( "cwd", "" ); if ( !largs.empty() ) { std::string cmd = std::move( largs.front() ); largs.erase( largs.begin() ); - runInTerminalCb( isIntegrated, cmd, largs, cwd, lenv, - [this, reqSeq, command]( int pid ) { - makeResponse( reqSeq, pid != 0, command, - nlohmann::json{ { "processId", pid } } ); - } ); + runInTerminalCb( + isIntegrated, cmd, largs, cwd, lenv, [this, reqSeq, command]( int pid ) { + makeResponse( reqSeq, pid != 0, command, nlohmann::json{ { "processId", pid } }, + mCurrentSessionId ); + } ); } } else if ( DAP_START_DEBUGGING == command ) { - mProtocol.launchRequestType = msg.value( DAP_REQUEST, "launch" ); - mProtocol.launchArgs = args["configuration"]; + std::string sessionId = + args.contains( "configuration" ) + ? args["configuration"].value( "__pendingTargetId", + "" ) // This is non-standard, we recover the target + // id for node, otherwise we will use an UUID + : ""; + if ( sessionId.empty() ) + sessionId = UUID().toString(); - mState = State::Initializing; + Lock l( mSessionsMutex ); + Session& newSession = mSessions[sessionId]; + newSession.launchRequestType = msg.value( DAP_REQUEST, "launch" ); - makeResponse( reqSeq, true, DAP_START_DEBUGGING, {} ); + if ( args.contains( "configuration" ) ) + newSession.launchArgs = args["configuration"]; - // TODO: Add child sessions support - // Re-launch with the new launch request - // makeRequest( mProtocol.launchRequestType, mProtocol.launchArgs, - // [this, reqSeq]( const Response& response, const auto& ) { - // if ( response.success ) { - // } - // } ); + newSession.state = State::Initializing; + + switch ( mSessions[mCurrentSessionId].bus->type() ) { + case Bus::Type::Process: { + BusProcess* prevBus = + static_cast( mSessions[mCurrentSessionId].bus.get() ); + newSession.bus = std::make_unique( prevBus->getCommand() ); + break; + } + case Bus::Type::Socket: { + BusSocket* prevBus = + static_cast( mSessions[mCurrentSessionId].bus.get() ); + newSession.bus = std::make_unique( prevBus->getConnection() ); + break; + } + case Bus::Type::SocketProcess: { + BusSocketProcess* prevBus = + static_cast( mSessions[mCurrentSessionId].bus.get() ); + newSession.bus = std::make_unique( prevBus->getConnection() ); + break; + } + } + + auto prevSessionId = mCurrentSessionId; + mCurrentSessionId = sessionId; + + if ( newSession.bus && newSession.bus->start() ) { + newSession.bus->startAsyncRead( + [this]( const char* bytes, size_t n ) { asyncRead( bytes, n ); } ); + } else { + Log::warning( "DebuggerClientDap::start: could not initialize the debugger" ); + return; + } + + requestInitialize( sessionId, [reqSeq, this, prevSessionId] { + makeResponse( reqSeq, true, DAP_START_DEBUGGING, {}, prevSessionId ); + } ); } } void DebuggerClientDap::processResponse( const nlohmann::json& msg ) { const Response response( msg ); - - // check sequence - if ( ( response.request_seq < 0 ) || 0 == mRequests.count( response.request_seq ) ) { - Log::error( "DebuggerClientDap::processResponse: unexpected requested seq in response" ); + if ( response.request_seq < 0 || mRequests.count( response.request_seq ) == 0 ) { + Log::error( "DebuggerClientDap::processResponse: unexpected request seq" ); return; } - const auto request = mRequests.extract( response.request_seq ).mapped(); - - // check response + auto request = mRequests.extract( response.request_seq ).mapped(); if ( response.command != request.command ) { - Log::error( - "DebuggerClientDap::processResponse: unexpected command in response: %s (expected: %s)", - response.command, request.command ); + Log::error( "DebuggerClientDap::processResponse: command mismatch: %s (expected: %s)", + response.command, request.command ); } if ( response.isCancelled() ) Log::debug( "DebuggerClientDap::processResponse: request cancelled: %s", response.command ); - if ( !response.success ) - return errorResponse( response.command, response.message, response.errorBody ); + if ( !response.success ) { + errorResponse( response.command, response.message, response.errorBody, request.sessionId ); + return; + } if ( request.handler ) { request.handler( response, request.arguments ); @@ -328,105 +401,167 @@ void DebuggerClientDap::processResponse( const nlohmann::json& msg ) { } void DebuggerClientDap::errorResponse( const std::string& command, const std::string& summary, - const std::optional& message ) { + const std::optional& message, + const SessionId& sessionId ) { for ( auto listener : mListeners ) - listener->errorResponse( command, summary, message ); + listener->errorResponse( command, summary, message, sessionId ); } void DebuggerClientDap::processEvent( const nlohmann::json& msg ) { const std::string event = msg.value( DAP_EVENT, "" ); const auto body = msg.contains( DAP_BODY ) ? msg[DAP_BODY] : nlohmann::json{}; - if ( "initialized"sv == event ) { - processEventInitialized(); - } else if ( "terminated"sv == event ) { - processEventTerminated(); - } else if ( "exited"sv == event ) { - processEventExited( body ); + // For events tied to threads, find the session + std::string sessionId; + if ( body.contains( "threadId" ) ) { + int threadId = body["threadId"].get(); + sessionId = findSessionByThread( threadId ); + } else { + // Default to current session or first initializing session + for ( const auto& [id, session] : mSessions ) { + if ( session.state == State::Initializing || id == mCurrentSessionId ) { + sessionId = id; + break; + } + } + } + + if ( sessionId.empty() ) { + Log::warning( "DebuggerClientDap::processEvent: no session found for event %s", event ); + return; + } + + if ( "initialized" == event ) { + processEventInitialized( sessionId ); + } else if ( "terminated" == event ) { + processEventTerminated( sessionId ); + } else if ( "exited" == event ) { + processEventExited( body, sessionId ); } else if ( DAP_OUTPUT == event ) { - processEventOutput( body ); - } else if ( "process"sv == event ) { - processEventProcess( body ); - } else if ( "thread"sv == event ) { - processEventThread( body ); - } else if ( "stopped"sv == event ) { - processEventStopped( body ); - } else if ( "module"sv == event ) { - processEventModule( body ); - } else if ( "continued"sv == event ) { - processEventContinued( body ); + processEventOutput( body, sessionId ); + } else if ( "process" == event ) { + processEventProcess( body, sessionId ); + } else if ( "thread" == event ) { + processEventThread( body, sessionId ); + } else if ( "stopped" == event ) { + processEventStopped( body, sessionId ); + } else if ( "module" == event ) { + processEventModule( body, sessionId ); + } else if ( "continued" == event ) { + processEventContinued( body, sessionId ); } else if ( DAP_BREAKPOINT == event ) { - processEventBreakpoint( body ); + processEventBreakpoint( body, sessionId ); + } else if ( "loadedSource" == event ) { + // We don't care } else { Log::info( "DebuggerClientDap::processEvent: unsupported event: %s", event ); } } -void DebuggerClientDap::processEventInitialized() { - if ( ( mState != State::Initializing ) ) { - Log::error( "DebuggerClientDap::processEventInitialized: unexpected initialized event" ); +void DebuggerClientDap::processEventInitialized( const SessionId& sessionId ) { + Session& session = mSessions[sessionId]; + if ( session.state != State::Initializing ) { + Log::error( "DebuggerClientDap::processEventInitialized [Session %s]: unexpected event", + sessionId ); return; } - setState( State::Initialized ); - - configurationDone(); + setState( State::Initialized, sessionId ); + configurationDone( sessionId ); } -void DebuggerClientDap::processEventTerminated() { - setState( State::Terminated ); -} +void DebuggerClientDap::processEventTerminated( const SessionId& sessionId ) { + setState( State::Terminated, sessionId ); + + if ( !mDestroying ) { + Lock l( mSessionsMutex ); + + // Clean up terminated session + auto session = mSessions.extract( sessionId ); + + // We cannot destroy the Bus yet, since the async read from the socket might have emmited + // this termination. + if ( session.mapped().bus ) { + session.mapped().bus->close(); + mBusesToClose.emplace_back( std::move( session.mapped().bus ) ); + } + + if ( sessionId == mCurrentSessionId && "main" != sessionId ) + mCurrentSessionId = "main"; + } -void DebuggerClientDap::processEventExited( const nlohmann::json& body ) { - const int exitCode = body.value( "exitCode", -1 ); for ( auto listener : mListeners ) - listener->debuggeeExited( exitCode ); + listener->debuggeeTerminated( sessionId ); } -void DebuggerClientDap::processEventOutput( const nlohmann::json& body ) { +void DebuggerClientDap::processEventExited( const nlohmann::json& body, + const SessionId& sessionId ) { + const int exitCode = body.value( "exitCode", -1 ); + + for ( auto listener : mListeners ) + listener->debuggeeExited( exitCode, sessionId ); +} + +void DebuggerClientDap::processEventOutput( const nlohmann::json& body, + const SessionId& sessionId ) { Output output( body ); for ( auto listener : mListeners ) listener->outputProduced( output ); - if ( mWaitingToAttach && mProtocol.runTarget && runTargetCb ) { + Session& session = mSessions[sessionId]; + if ( session.waitingToAttach && mProtocol.runTarget && runTargetCb ) { runTargetCb(); - mWaitingToAttach = false; + session.waitingToAttach = false; } } -void DebuggerClientDap::processEventProcess( const nlohmann::json& body ) { +void DebuggerClientDap::processEventProcess( const nlohmann::json& body, + const SessionId& sessionId ) { ProcessInfo processInfo( body ); for ( auto listener : mListeners ) - listener->debuggingProcess( processInfo ); + listener->debuggingProcess( processInfo, sessionId ); } -void DebuggerClientDap::processEventThread( const nlohmann::json& body ) { +void DebuggerClientDap::processEventThread( const nlohmann::json& body, + const SessionId& sessionId ) { ThreadEvent threadEvent( body ); + int threadId = threadEvent.threadId; + if ( threadEvent.reason == "started" ) { + mThreadToSession[threadId] = sessionId; + } else if ( threadEvent.reason == "exited" ) { + if ( mThreadToSession.count( threadId ) ) { + mThreadToSession.erase( threadId ); + } + } for ( auto listener : mListeners ) - listener->threadChanged( threadEvent ); + listener->threadChanged( threadEvent, sessionId ); } -void DebuggerClientDap::processEventStopped( const nlohmann::json& body ) { +void DebuggerClientDap::processEventStopped( const nlohmann::json& body, + const SessionId& sessionId ) { StoppedEvent stoppedEvent( body ); for ( auto listener : mListeners ) - listener->debuggeeStopped( stoppedEvent ); + listener->debuggeeStopped( stoppedEvent, sessionId ); } -void DebuggerClientDap::processEventModule( const nlohmann::json& body ) { +void DebuggerClientDap::processEventModule( const nlohmann::json& body, + const SessionId& sessionId ) { ModuleEvent moduleEvent( body ); for ( auto listener : mListeners ) - listener->moduleChanged( moduleEvent ); + listener->moduleChanged( moduleEvent, sessionId ); } -void DebuggerClientDap::processEventContinued( const nlohmann::json& body ) { +void DebuggerClientDap::processEventContinued( const nlohmann::json& body, + const SessionId& sessionId ) { ContinuedEvent continuedEvent( body ); for ( auto listener : mListeners ) - listener->debuggeeContinued( continuedEvent ); + listener->debuggeeContinued( continuedEvent, sessionId ); } -void DebuggerClientDap::processEventBreakpoint( const nlohmann::json& body ) { +void DebuggerClientDap::processEventBreakpoint( const nlohmann::json& body, + const SessionId& sessionId ) { BreakpointEvent breakpointEvent( body ); for ( auto listener : mListeners ) - listener->breakpointChanged( breakpointEvent ); + listener->breakpointChanged( breakpointEvent, sessionId ); } std::optional DebuggerClientDap::readHeader() { @@ -467,7 +602,7 @@ std::optional DebuggerClientDap::readHeader() { // parse field const auto sep = header.find_first_of( ":" ); if ( sep == std::string::npos ) { - Log::error( "DebuggerClientDap::readHeader cannot parse header field: ", header ); + Log::error( "DebuggerClientDap::readHeader cannot parse header field: %s", header ); discardExploredBuffer(); continue; // CONTINUE HEADER } @@ -495,30 +630,41 @@ std::optional DebuggerClientDap::readHeader() { } bool DebuggerClientDap::resume( int threadId, bool singleThread ) { + std::string sessionId = findSessionByThread( threadId ); + if ( sessionId.empty() ) { + Log::warning( "DebuggerClientDap::resume: no session for thread %d", threadId ); + return false; + } nlohmann::json arguments{ { DAP_THREAD_ID, threadId } }; if ( singleThread ) arguments[DAP_SINGLE_THREAD] = true; - makeRequest( "continue", arguments, - [this]( const Response& response, const nlohmann::json& request ) { - if ( response.success ) { - ContinuedEvent continuedEvent( - request.value( DAP_THREAD_ID, 1 ), - response.body.value( DAP_ALL_THREADS_CONTINUED, true ) ); - for ( auto listener : mListeners ) - listener->debuggeeContinued( continuedEvent ); - } - } ); + makeRequest( + "continue", arguments, + [this, sessionId]( const Response& response, const nlohmann::json& request ) { + if ( response.success ) { + ContinuedEvent continuedEvent( + request.value( DAP_THREAD_ID, 1 ), + response.body.value( DAP_ALL_THREADS_CONTINUED, true ) ); + for ( auto listener : mListeners ) + listener->debuggeeContinued( continuedEvent, sessionId ); + } + }, + sessionId ); return true; } bool DebuggerClientDap::pause( int threadId ) { + std::string sessionId = findSessionByThread( threadId ); + if ( sessionId.empty() ) + return false; nlohmann::json arguments{ { DAP_THREAD_ID, threadId } }; - makeRequest( "pause", arguments ); + makeRequest( "pause", arguments, nullptr, sessionId ); return true; } void DebuggerClientDap::processResponseNext( const Response& response, - const nlohmann::json& request ) { + const nlohmann::json& request, + const SessionId& sessionId ) { if ( response.success ) { bool all = false; if ( response.body.is_object() && response.body.contains( DAP_ALL_THREADS_CONTINUED ) ) @@ -526,41 +672,57 @@ void DebuggerClientDap::processResponseNext( const Response& response, ContinuedEvent continuedEvent( request.value( DAP_THREAD_ID, 1 ), all ); for ( auto listener : mListeners ) - listener->debuggeeContinued( continuedEvent ); + listener->debuggeeContinued( continuedEvent, sessionId ); } } bool DebuggerClientDap::stepOver( int threadId, bool singleThread ) { + std::string sessionId = findSessionByThread( threadId ); + if ( sessionId.empty() ) + return false; nlohmann::json arguments{ { DAP_THREAD_ID, threadId } }; if ( singleThread ) arguments[DAP_SINGLE_THREAD] = true; makeRequest( "next", arguments, - makeResponseHandler( &DebuggerClientDap::processResponseNext, this ) ); + makeResponseHandler( &DebuggerClientDap::processResponseNext, this, sessionId ), + sessionId ); return true; } bool DebuggerClientDap::goTo( int threadId, int targetId ) { + std::string sessionId = findSessionByThread( threadId ); + if ( sessionId.empty() ) + return false; const nlohmann::json arguments{ { DAP_THREAD_ID, threadId }, { DAP_TARGET_ID, targetId } }; makeRequest( "goto", arguments, - makeResponseHandler( &DebuggerClientDap::processResponseNext, this ) ); + makeResponseHandler( &DebuggerClientDap::processResponseNext, this, sessionId ), + sessionId ); return true; } bool DebuggerClientDap::stepInto( int threadId, bool singleThread ) { + std::string sessionId = findSessionByThread( threadId ); + if ( sessionId.empty() ) + return false; nlohmann::json arguments{ { DAP_THREAD_ID, threadId } }; if ( singleThread ) arguments[DAP_SINGLE_THREAD] = true; makeRequest( "stepIn", arguments, - makeResponseHandler( &DebuggerClientDap::processResponseNext, this ) ); + makeResponseHandler( &DebuggerClientDap::processResponseNext, this, sessionId ), + sessionId ); return true; } bool DebuggerClientDap::stepOut( int threadId, bool singleThread ) { + std::string sessionId = findSessionByThread( threadId ); + if ( sessionId.empty() ) + return false; nlohmann::json arguments{ { DAP_THREAD_ID, threadId } }; if ( singleThread ) arguments[DAP_SINGLE_THREAD] = true; makeRequest( "stepOut", arguments, - makeResponseHandler( &DebuggerClientDap::processResponseNext, this ) ); + makeResponseHandler( &DebuggerClientDap::processResponseNext, this, sessionId ), + sessionId ); return true; } @@ -568,7 +730,7 @@ bool DebuggerClientDap::terminate( bool restart ) { nlohmann::json arguments; if ( restart ) arguments["restart"] = true; - makeRequest( "terminate", arguments ); + makeRequest( "terminate", arguments, nullptr, mCurrentSessionId ); return true; } @@ -580,27 +742,33 @@ bool DebuggerClientDap::disconnect( bool terminateDebuggee, bool restart ) { if ( restart ) arguments["restart"] = true; - makeRequest( "disconnect", arguments, - [this]( const Response& response, const nlohmann::json& ) { - if ( response.success ) { - for ( auto listener : mListeners ) - listener->serverDisconnected(); - } - } ); + makeRequest( + "disconnect", arguments, + [this]( const Response& response, const nlohmann::json& ) { + if ( response.success ) { + for ( auto listener : mListeners ) + listener->serverDisconnected( mCurrentSessionId ); + } + }, + mCurrentSessionId ); return true; } bool DebuggerClientDap::threads() { - makeRequest( DAP_THREADS, {}, [this]( const Response& response, const nlohmann::json& ) { - if ( response.success ) { - std::vector threads( DapThread::parseList( response.body[DAP_THREADS] ) ); - for ( auto listener : mListeners ) - listener->threads( std::move( threads ) ); - } else { - for ( auto listener : mListeners ) - listener->threads( {} ); - } - } ); + makeRequest( + DAP_THREADS, {}, + [this]( const Response& response, const nlohmann::json& ) { + if ( response.success ) { + std::vector threads( + DapThread::parseList( response.body[DAP_THREADS] ) ); + for ( auto listener : mListeners ) + listener->threads( std::move( threads ), mCurrentSessionId ); + } else { + for ( auto listener : mListeners ) + listener->threads( {}, mCurrentSessionId ); + } + }, + mCurrentSessionId ); return true; } @@ -608,37 +776,43 @@ bool DebuggerClientDap::stackTrace( int threadId, int startFrame, int levels ) { const nlohmann::json arguments{ { DAP_THREAD_ID, threadId }, { "startFrame", startFrame }, { "levels", levels } }; - makeRequest( "stackTrace", arguments, - [this]( const Response& response, const nlohmann::json& request ) { - const int threadId = request.value( DAP_THREAD_ID, 1 ); - if ( response.success ) { - StackTraceInfo stackTraceInfo( response.body ); - for ( auto listener : mListeners ) - listener->stackTrace( threadId, std::move( stackTraceInfo ) ); - } else { - StackTraceInfo stackTraceInfo; - for ( auto listener : mListeners ) - listener->stackTrace( threadId, std::move( stackTraceInfo ) ); - } - } ); + makeRequest( + "stackTrace", arguments, + [this]( const Response& response, const nlohmann::json& request ) { + const int threadId = request.value( DAP_THREAD_ID, 1 ); + if ( response.success ) { + StackTraceInfo stackTraceInfo( response.body ); + for ( auto listener : mListeners ) + listener->stackTrace( threadId, std::move( stackTraceInfo ), + mCurrentSessionId ); + } else { + StackTraceInfo stackTraceInfo; + for ( auto listener : mListeners ) + listener->stackTrace( threadId, std::move( stackTraceInfo ), + mCurrentSessionId ); + } + }, + mCurrentSessionId ); return true; } bool DebuggerClientDap::scopes( int frameId ) { const nlohmann::json arguments{ { DAP_FRAME_ID, frameId } }; - makeRequest( DAP_SCOPES, arguments, - [this]( const Response& response, const nlohmann::json& request ) { - const int frameId = request.value( DAP_FRAME_ID, 1 ); - if ( response.success ) { - auto scopes( Scope::parseList( response.body[DAP_SCOPES] ) ); - for ( auto listener : mListeners ) - listener->scopes( frameId, std::move( scopes ) ); - } else { - std::vector scopes; - for ( auto listener : mListeners ) - listener->scopes( frameId, std::move( scopes ) ); - } - } ); + makeRequest( + DAP_SCOPES, arguments, + [this]( const Response& response, const nlohmann::json& request ) { + const int frameId = request.value( DAP_FRAME_ID, 1 ); + if ( response.success ) { + auto scopes( Scope::parseList( response.body[DAP_SCOPES] ) ); + for ( auto listener : mListeners ) + listener->scopes( frameId, std::move( scopes ), mCurrentSessionId ); + } else { + std::vector scopes; + for ( auto listener : mListeners ) + listener->scopes( frameId, std::move( scopes ), mCurrentSessionId ); + } + }, + mCurrentSessionId ); return true; } @@ -674,7 +848,8 @@ bool DebuggerClientDap::variables( int variablesReference, Variable::Type filter responseCb( variablesReference, std::move( variableList ) ); } else { for ( auto listener : mListeners ) - listener->variables( variablesReference, std::move( variableList ) ); + listener->variables( variablesReference, std::move( variableList ), + mCurrentSessionId ); } } else { std::vector variableList; @@ -682,27 +857,31 @@ bool DebuggerClientDap::variables( int variablesReference, Variable::Type filter responseCb( variablesReference, std::move( variableList ) ); } else { for ( auto listener : mListeners ) - listener->variables( variablesReference, std::move( variableList ) ); + listener->variables( variablesReference, std::move( variableList ), + mCurrentSessionId ); } } - } ); + }, + mCurrentSessionId ); return true; } bool DebuggerClientDap::modules( int start, int count ) { - makeRequest( DAP_MODULES, { { DAP_START, start }, { DAP_COUNT, count } }, - [this]( const auto& response, const auto& ) { - if ( response.success ) { - ModulesInfo info( response.body ); - for ( auto listener : mListeners ) - listener->modules( std::move( info ) ); - } else { - ModulesInfo info; - for ( auto listener : mListeners ) - listener->modules( std::move( info ) ); - } - } ); + makeRequest( + DAP_MODULES, { { DAP_START, start }, { DAP_COUNT, count } }, + [this]( const auto& response, const auto& ) { + if ( response.success ) { + ModulesInfo info( response.body ); + for ( auto listener : mListeners ) + listener->modules( std::move( info ), mCurrentSessionId ); + } else { + ModulesInfo info; + for ( auto listener : mListeners ) + listener->modules( std::move( info ), mCurrentSessionId ); + } + }, + mCurrentSessionId ); return true; } @@ -715,25 +894,27 @@ bool DebuggerClientDap::evaluate( if ( frameId ) arguments[DAP_FRAME_ID] = *frameId; - makeRequest( "evaluate", arguments, - [this, cb = std::move( cb )]( const auto& response, const auto& request ) { - auto expression = request.value( DAP_EXPRESSION, "" ); - if ( response.success ) { - EvaluateInfo info( response.body ); + makeRequest( + "evaluate", arguments, + [this, cb = std::move( cb )]( const auto& response, const auto& request ) { + auto expression = request.value( DAP_EXPRESSION, "" ); + if ( response.success ) { + EvaluateInfo info( response.body ); - for ( auto listener : mListeners ) - listener->expressionEvaluated( expression, info ); + for ( auto listener : mListeners ) + listener->expressionEvaluated( expression, info, mCurrentSessionId ); - if ( cb ) - cb( expression, info ); - } else { - for ( auto listener : mListeners ) - listener->expressionEvaluated( expression, std::nullopt ); + if ( cb ) + cb( expression, info ); + } else { + for ( auto listener : mListeners ) + listener->expressionEvaluated( expression, std::nullopt, mCurrentSessionId ); - if ( cb ) - cb( expression, std::nullopt ); - } - } ); + if ( cb ) + cb( expression, std::nullopt ); + } + }, + mCurrentSessionId ); return true; } @@ -755,38 +936,40 @@ bool DebuggerClientDap::setBreakpoints( const dap::Source& source, { DAP_BREAKPOINTS, bpoints }, { "sourceModified", sourceModified } }; - makeRequest( "setBreakpoints", arguments, - [this]( const Response& response, const nlohmann::json& request ) { - const auto source = Source( request[DAP_SOURCE] ); - if ( response.success ) { - const auto resp = response.body; - if ( resp.contains( DAP_BREAKPOINTS ) ) { - std::vector breakpoints; - breakpoints.reserve( resp[DAP_BREAKPOINTS].size() ); - for ( const auto& item : resp[DAP_BREAKPOINTS] ) - breakpoints.emplace_back( item ); + makeRequest( + "setBreakpoints", arguments, + [this]( const Response& response, const nlohmann::json& request ) { + const auto source = Source( request[DAP_SOURCE] ); + if ( response.success ) { + const auto resp = response.body; + if ( resp.contains( DAP_BREAKPOINTS ) ) { + std::vector breakpoints; + breakpoints.reserve( resp[DAP_BREAKPOINTS].size() ); + for ( const auto& item : resp[DAP_BREAKPOINTS] ) + breakpoints.emplace_back( item ); - for ( auto listener : mListeners ) - listener->sourceBreakpoints( source.path, - source.sourceReference.value_or( 0 ), - breakpoints ); - } else { - std::vector breakpoints; - breakpoints.reserve( resp[DAP_LINES].size() ); - for ( const auto& item : resp[DAP_LINES] ) - breakpoints.emplace_back( item.get() ); + for ( auto listener : mListeners ) + listener->sourceBreakpoints( source.path, + source.sourceReference.value_or( 0 ), + breakpoints, mCurrentSessionId ); + } else { + std::vector breakpoints; + breakpoints.reserve( resp[DAP_LINES].size() ); + for ( const auto& item : resp[DAP_LINES] ) + breakpoints.emplace_back( item.get() ); - for ( auto listener : mListeners ) - listener->sourceBreakpoints( source.path, - source.sourceReference.value_or( 0 ), - breakpoints ); - } - } else { - for ( auto listener : mListeners ) - listener->sourceBreakpoints( - source.path, source.sourceReference.value_or( 0 ), std::nullopt ); - } - } ); + for ( auto listener : mListeners ) + listener->sourceBreakpoints( source.path, + source.sourceReference.value_or( 0 ), + breakpoints, mCurrentSessionId ); + } + } else { + for ( auto listener : mListeners ) + listener->sourceBreakpoints( source.path, source.sourceReference.value_or( 0 ), + std::nullopt, mCurrentSessionId ); + } + }, + mCurrentSessionId ); return true; } @@ -802,19 +985,22 @@ bool DebuggerClientDap::gotoTargets( const Source& source, const int 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 listener : mListeners ) - listener->gotoTargets( source, line, list ); - } else { - std::vector list; - for ( auto listener : mListeners ) - listener->gotoTargets( source, line, list ); - } - } ); + 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 listener : mListeners ) + listener->gotoTargets( source, line, list, mCurrentSessionId ); + } else { + std::vector list; + for ( auto listener : mListeners ) + listener->gotoTargets( source, line, list, mCurrentSessionId ); + } + }, + mCurrentSessionId ); return true; } @@ -823,29 +1009,47 @@ bool DebuggerClientDap::watch( const std::string& expression, std::optional return evaluate( expression, "watch", frameId ); } -bool DebuggerClientDap::configurationDone() { - if ( mState != State::Initialized ) { - Log::warning( "DebuggerClientDap::requestConfigurationDone: trying to configure in an " - "unexpected status" ); +std::string DebuggerClientDap::findSessionByThread( int threadId ) const { + auto it = mThreadToSession.find( threadId ); + return it != mThreadToSession.end() ? it->second : mCurrentSessionId; +} + +bool DebuggerClientDap::configurationDone( const SessionId& sessionId ) { + Session& session = mSessions[sessionId]; + if ( session.state != State::Initialized ) { + Log::warning( "DebuggerClientDap::configurationDone [Session %s]: unexpected state", + sessionId ); return false; } if ( !mAdapterCapabilities.supportsConfigurationDoneRequest ) { + session.configured = true; + if ( session.launched && session.state == State::Initialized ) { + setState( State::Running, sessionId ); + for ( auto listener : mListeners ) + listener->debuggeeRunning( sessionId ); + } for ( auto listener : mListeners ) - listener->configured(); + listener->configured( sessionId ); return true; } - makeRequest( "configurationDone", nlohmann::json{}, - [this]( const auto& response, const auto& ) { - if ( response.success ) { - mConfigured = true; - checkRunning(); - for ( auto listener : mListeners ) - listener->configured(); - } - } ); - + makeRequest( + "configurationDone", nlohmann::json{}, + [this, sessionId]( const auto& response, const auto& ) { + Session& session = mSessions[sessionId]; + if ( response.success ) { + session.configured = true; + if ( session.launched && session.state == State::Initialized ) { + setState( State::Running, sessionId ); + for ( auto listener : mListeners ) + listener->debuggeeRunning( sessionId ); + } + for ( auto listener : mListeners ) + listener->configured( sessionId ); + } + }, + sessionId ); return true; } @@ -853,4 +1057,45 @@ bool DebuggerClientDap::started() const { return mStarted; } +void DebuggerClientDap::setState( const State& state, const SessionId& sessionId ) { + if ( mSessions.count( sessionId ) == 0 ) { + Log::warning( + "DebuggerClientDap::setState [Session %s]: setting state of an invalid session", + sessionId ); + return; // Invalid session + } + Session& session = mSessions[sessionId]; + if ( session.state != state ) { + session.state = state; + stateChanged( state, sessionId ); // Notify listeners + switch ( state ) { + case State::Initialized: + initialized( sessionId ); + checkRunning( sessionId ); // Trigger running check + break; + case State::Running: + // Additional logic for running state + break; + case State::Terminated: + // Cleanup or notify termination + break; + case State::Failed: + // Handle failure + break; + default: + break; + } + } +} + +void DebuggerClientDap::checkRunning( const SessionId& sessionId ) { + if ( mSessions.count( sessionId ) == 0 ) { + return; // Invalid session + } + Session& session = mSessions[sessionId]; + if ( session.launched && session.configured && session.state == State::Initialized ) { + setState( State::Running, sessionId ); // Transition to Running state + } +} + } // namespace ecode::dap diff --git a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp index b72580dea..974d60a48 100644 --- a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp +++ b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp @@ -7,8 +7,10 @@ #include #include #include +#include using namespace EE; +using namespace EE::System; namespace ecode::dap { @@ -45,11 +47,9 @@ class DebuggerClientDap : public DebuggerClient { bool terminate( bool restart ) override; bool disconnect( bool terminateDebuggee, bool restart = false ) override; - bool threads() override; bool stackTrace( int threadId, int startFrame = 0, int levels = 0 ) override; - bool scopes( int frameId ) override; bool variables( int variablesReference, Variable::Type filter = Variable::Type::Both, @@ -58,10 +58,10 @@ class DebuggerClientDap : public DebuggerClient { bool modules( int start, int count ) override; - bool evaluate( - const std::string& expression, const std::string& context, std::optional frameId, - std::function& )> - cb = {} ) override; + bool evaluate( const std::string& expression, const std::string& context, + std::optional frameId, + std::function& )> + cb = {} ) override; bool isServerConnected() const override; @@ -78,89 +78,102 @@ class DebuggerClientDap : public DebuggerClient { bool sourceModified = false ) override; bool gotoTargets( const std::string& path, const int line, - const std::optional column = std::nullopt ) override; + 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; - bool configurationDone() override; + bool configurationDone( const SessionId& sessionId ) override; bool started() const override; void setSilent( bool silent ) override { mDebug = !silent; } + size_t sessionsActive() override { return mSessions.size(); } + protected: - std::unique_ptr mBus; - UnorderedMap> mBreakpoints; - std::atomic mIdx{ 1 }; - Uint64 mThreadId{ 1 }; - bool mDebug{ true }; - bool mStarted{ false }; + // Session structure to hold per-session data + struct Session { + State state = State::None; + std::string launchRequestType; + nlohmann::json launchArgs; + bool launched = false; + bool configured = false; + bool waitingToAttach = false; + std::unique_ptr bus; + std::atomic idx{ 1 }; + }; + struct Request { std::string command; nlohmann::json arguments; ResponseHandler handler; + std::string sessionId; // Associate request with a session }; + + UnorderedMap> mBreakpoints; + bool mDebug{ true }; + bool mStarted{ false }; + bool mDestroying{ false }; std::unordered_map mRequests; std::string mBuffer; ProtocolSettings mProtocol; Capabilities mAdapterCapabilities; + std::vector> mBusesToClose; + + // Session management + mutable Mutex mSessionsMutex; + std::unordered_map mSessions; // Session ID to Session + std::string mCurrentSessionId; // Latest active session + std::unordered_map mThreadToSession; // Thread ID to Session ID + + void setState( const State& state, const SessionId& sessionId ) override; + + void checkRunning( const SessionId& sessionId ) override; void makeRequest( const std::string_view& command, const nlohmann::json& arguments, - ResponseHandler onFinish = nullptr ); - + ResponseHandler onFinish, const SessionId& sessionId ); void makeResponse( int reqSeq, bool success, const std::string& command, - const nlohmann::json& body ); - + const nlohmann::json& body, const std::string& sessionId ); void asyncRead( const char* bytes, size_t n ); - void processProtocolMessage( const nlohmann::json& msg ); - void processResponse( const nlohmann::json& msg ); - void processEvent( const nlohmann::json& msg ); - void processRequest( const nlohmann::json& msg ); - struct HeaderInfo { Uint64 payloadStart; Uint64 payloadLength; }; - std::optional readHeader(); - void errorResponse( const std::string& command, const std::string& summary, - const std::optional& message ); + const std::optional& message, const SessionId& sessionId = "" ); - void processEventInitialized(); + // Event handlers + void processEventInitialized( const SessionId& sessionId ); + void processEventTerminated( const SessionId& sessionId ); + void processEventExited( const nlohmann::json& body, const SessionId& sessionId ); + void processEventOutput( const nlohmann::json& body, const SessionId& sessionId ); + void processEventProcess( const nlohmann::json& body, const SessionId& sessionId ); + void processEventThread( const nlohmann::json& body, const SessionId& sessionId ); + void processEventStopped( const nlohmann::json& body, const SessionId& sessionId ); + void processEventModule( const nlohmann::json& body, const SessionId& sessionId ); + void processEventContinued( const nlohmann::json& body, const SessionId& sessionId ); + void processEventBreakpoint( const nlohmann::json& body, const SessionId& sessionId ); - void processEventTerminated(); + // Session-specific methods + void requestInitialize( const SessionId& sessionId, std::function onLaunch = {} ); + void requestLaunchCommand( const SessionId& sessionId, std::function onLaunch = {} ); + void processResponseInitialize( const Response& response, const nlohmann::json&, + const SessionId& sessionId, + std::function onLaunch = {} ); + void processResponseNext( const Response& response, const nlohmann::json& request, + const SessionId& sessionId ); - void processEventExited( const nlohmann::json& body ); - - void processEventOutput( const nlohmann::json& body ); - - void processEventProcess( const nlohmann::json& body ); - - void processEventThread( const nlohmann::json& body ); - - void processEventStopped( const nlohmann::json& body ); - - void processEventModule( const nlohmann::json& body ); - - void processEventContinued( const nlohmann::json& body ); - - void processEventBreakpoint( const nlohmann::json& body ); - - void requestInitialize(); - - void requestLaunchCommand( std::function onLaunch = {} ); - - void processResponseInitialize( const Response& response, const nlohmann::json& ); - - void processResponseNext( const Response& response, const nlohmann::json& request ); + // Helper to find session by thread ID + std::string findSessionByThread( int threadId ) const; }; } // namespace ecode::dap diff --git a/src/tools/ecode/plugins/debugger/debuggerclient.cpp b/src/tools/ecode/plugins/debugger/debuggerclient.cpp index adc984382..b8f4ce507 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclient.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerclient.cpp @@ -2,59 +2,29 @@ namespace ecode { -void DebuggerClient::stateChanged( State state ) { +void DebuggerClient::stateChanged( State state, const SessionId& sessionId ) { for ( auto listener : mListeners ) - listener->stateChanged( state ); + listener->stateChanged( state, sessionId ); } -void DebuggerClient::initialized() { +void DebuggerClient::initialized( const SessionId& sessionId ) { for ( auto listener : mListeners ) - listener->initialized(); + listener->initialized( sessionId ); } -void DebuggerClient::debuggeeRunning() { +void DebuggerClient::debuggeeRunning( const SessionId& sessionId ) { for ( auto listener : mListeners ) - listener->debuggeeRunning(); + listener->debuggeeRunning( sessionId ); } -void DebuggerClient::debuggeeTerminated() { +void DebuggerClient::debuggeeTerminated( const SessionId& sessionId ) { for ( auto listener : mListeners ) - listener->debuggeeTerminated(); + listener->debuggeeTerminated( sessionId ); } -void DebuggerClient::failed() { +void DebuggerClient::failed( const SessionId& sessionId ) { for ( auto listener : mListeners ) - listener->failed(); -} - -void DebuggerClient::setState( const State& state ) { - if ( state != mState ) { - mState = state; - stateChanged( mState ); - - switch ( mState ) { - case State::Initialized: - initialized(); - checkRunning(); - break; - case State::Running: - debuggeeRunning(); - break; - case State::Terminated: - debuggeeTerminated(); - break; - case State::Failed: - failed(); - break; - default:; - } - } -} - -void DebuggerClient::checkRunning() { - if ( mLaunched && mConfigured && mState == State::Initialized ) { - setState( State::Running ); - } + listener->failed( sessionId ); } void DebuggerClient::addListener( Listener* listener ) { diff --git a/src/tools/ecode/plugins/debugger/debuggerclient.hpp b/src/tools/ecode/plugins/debugger/debuggerclient.hpp index 2a44c1bab..7e0a0183e 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclient.hpp +++ b/src/tools/ecode/plugins/debugger/debuggerclient.hpp @@ -6,6 +6,8 @@ namespace ecode { using namespace dap; +using SessionId = std::string; + class DebuggerClient { public: enum class State { None, Initializing, Initialized, Running, Terminated, Failed }; @@ -15,43 +17,73 @@ class DebuggerClient { class Listener { public: - virtual void stateChanged( State ) = 0; - virtual void initialized() = 0; - virtual void launched() = 0; - virtual void configured() = 0; - virtual void failed() = 0; - virtual void debuggeeRunning() = 0; - virtual void debuggeeTerminated() = 0; + virtual void stateChanged( State, const SessionId& sessionId ) = 0; + + virtual void initialized( const SessionId& sessionId ) = 0; + + virtual void launched( const SessionId& sessionId ) = 0; + + virtual void configured( const SessionId& sessionId ) = 0; + + virtual void failed( const SessionId& sessionId ) = 0; + + virtual void debuggeeRunning( const SessionId& sessionId ) = 0; + + virtual void debuggeeTerminated( const SessionId& sessionId ) = 0; virtual void capabilitiesReceived( const Capabilities& capabilities ) = 0; - virtual void debuggeeExited( int exitCode ) = 0; - virtual void debuggeeStopped( const StoppedEvent& ) = 0; - virtual void debuggeeContinued( const ContinuedEvent& ) = 0; - virtual void outputProduced( const Output& ) = 0; - virtual void debuggingProcess( const ProcessInfo& ) = 0; - virtual void errorResponse( const std::string& command, const std::string& summary, - const std::optional& message ) = 0; - virtual void threadChanged( const ThreadEvent& ) = 0; - virtual void moduleChanged( const ModuleEvent& ) = 0; - virtual void threads( std::vector&& ) = 0; - virtual void stackTrace( const int threadId, StackTraceInfo&& ) = 0; - virtual void scopes( const int frameId, std::vector&& ) = 0; - virtual void variables( const int variablesReference, std::vector&& ) = 0; - virtual void modules( ModulesInfo&& ) = 0; - virtual void serverDisconnected() = 0; - virtual void sourceContent( const std::string& path, int reference = 0, - const SourceContent& content = SourceContent() ) = 0; - virtual void - sourceBreakpoints( const std::string& path, int reference, - const std::optional>& breakpoints ) = 0; - virtual void breakpointChanged( const BreakpointEvent& ) = 0; - virtual void expressionEvaluated( const std::string& expression, - const std::optional& ) = 0; - virtual void gotoTargets( const Source& source, const int line, - const std::vector& targets ) = 0; - }; - State state() const { return mState; } + virtual void debuggeeExited( int exitCode, const SessionId& sessionId ) = 0; + + virtual void debuggeeStopped( const StoppedEvent&, const SessionId& sessionId ) = 0; + + virtual void debuggeeContinued( const ContinuedEvent&, const SessionId& sessionId ) = 0; + + virtual void outputProduced( const Output& ) = 0; + + virtual void debuggingProcess( const ProcessInfo&, const SessionId& sessionId ) = 0; + + virtual void errorResponse( const std::string& command, const std::string& summary, + const std::optional& message, + const SessionId& sessionId ) = 0; + + virtual void threadChanged( const ThreadEvent&, const SessionId& sessionId ) = 0; + + virtual void moduleChanged( const ModuleEvent&, const SessionId& sessionId ) = 0; + + virtual void threads( std::vector&&, const SessionId& sessionId ) = 0; + + virtual void stackTrace( const int threadId, StackTraceInfo&&, + const SessionId& sessionId ) = 0; + + virtual void scopes( const int frameId, std::vector&&, + const SessionId& sessionId ) = 0; + + virtual void variables( const int variablesReference, std::vector&&, + const SessionId& sessionId ) = 0; + + virtual void modules( ModulesInfo&&, const SessionId& sessionId ) = 0; + + virtual void serverDisconnected( const SessionId& sessionId ) = 0; + + virtual void sourceContent( const std::string& path, int reference = 0, + const SourceContent& content = SourceContent(), + const SessionId& sessionId = "" ) = 0; + + virtual void sourceBreakpoints( const std::string& path, int reference, + const std::optional>& breakpoints, + const SessionId& sessionId ) = 0; + + virtual void breakpointChanged( const BreakpointEvent&, const SessionId& sessionId ) = 0; + + virtual void expressionEvaluated( const std::string& expression, + const std::optional&, + const SessionId& sessionId ) = 0; + + virtual void gotoTargets( const Source& source, const int line, + const std::vector& targets, + const SessionId& sessionId ) = 0; + }; virtual bool start() = 0; @@ -113,7 +145,7 @@ class DebuggerClient { virtual bool watch( const std::string& expression, std::optional frameId ) = 0; - virtual bool configurationDone() = 0; + virtual bool configurationDone( const SessionId& sessionId ) = 0; virtual void setSilent( bool silent ) = 0; @@ -123,22 +155,20 @@ class DebuggerClient { virtual ~DebuggerClient() {} - protected: - void setState( const State& state ); + virtual size_t sessionsActive() = 0; + + protected: + virtual void setState( const State& state, const SessionId& sessionId = "" ) = 0; + + virtual void checkRunning( const SessionId& sessionId = "" ) = 0; - State mState{ State::None }; - bool mLaunched{ false }; - bool mConfigured{ false }; - bool mWaitingToAttach{ false }; std::vector mListeners; - void checkRunning(); - - void stateChanged( State ); - void initialized(); - void debuggeeRunning(); - void debuggeeTerminated(); - void failed(); + void stateChanged( State, const SessionId& sessionId = "" ); + void initialized( const SessionId& sessionId = "" ); + void debuggeeRunning( const SessionId& sessionId = "" ); + void debuggeeTerminated( const SessionId& sessionId = "" ); + void failed( const SessionId& sessionId = "" ); }; } // namespace ecode diff --git a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp index 58b214e03..57f101f26 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp @@ -184,7 +184,7 @@ void DebuggerClientListener::initUI() { mPlugin->setUIDebuggingState( StatusDebuggerController::State::Running ); } -void DebuggerClientListener::stateChanged( DebuggerClient::State state ) { +void DebuggerClientListener::stateChanged( DebuggerClient::State state, const SessionId& ) { if ( state == DebuggerClient::State::Initializing ) { mPlugin->getManager()->getUISceneNode()->runOnMainThread( [this] { initUI(); } ); } @@ -225,24 +225,26 @@ void DebuggerClientListener::setRemoteRoot( const std::string& newRemoteRoot ) { mRemoteRoot = newRemoteRoot; } -void DebuggerClientListener::initialized() { +void DebuggerClientListener::initialized( const SessionId& ) { sendBreakpoints(); } -void DebuggerClientListener::launched() {} +void DebuggerClientListener::launched( const SessionId& ) {} -void DebuggerClientListener::configured() {} +void DebuggerClientListener::configured( const SessionId& ) {} -void DebuggerClientListener::failed() { +void DebuggerClientListener::failed( const SessionId& ) { mPlugin->exitDebugger(); resetState(); } -void DebuggerClientListener::debuggeeRunning() {} +void DebuggerClientListener::debuggeeRunning( const SessionId& ) {} -void DebuggerClientListener::debuggeeTerminated() { - mPlugin->exitDebugger(); - resetState(); +void DebuggerClientListener::debuggeeTerminated( const SessionId& ) { + if ( mClient->sessionsActive() == 0 ) { + mPlugin->exitDebugger(); + resetState(); + } } void DebuggerClientListener::capabilitiesReceived( const Capabilities& /*capabilities*/ ) {} @@ -258,12 +260,12 @@ void DebuggerClientListener::resetState() { mVariablesHolder->clear(); } -void DebuggerClientListener::debuggeeExited( int /*exitCode*/ ) { +void DebuggerClientListener::debuggeeExited( int /*exitCode*/, const SessionId& ) { mPlugin->exitDebugger(); resetState(); } -void DebuggerClientListener::debuggeeStopped( const StoppedEvent& event ) { +void DebuggerClientListener::debuggeeStopped( const StoppedEvent& event, const SessionId& ) { Log::debug( "DebuggerClientListener::debuggeeStopped: reason %s", event.reason ); for ( auto& [editor, _] : mPlugin->mEditors ) { @@ -309,7 +311,7 @@ void DebuggerClientListener::debuggeeStopped( const StoppedEvent& event ) { sdc->getWidget()->runOnMainThread( [sdc] { sdc->show(); } ); } -void DebuggerClientListener::debuggeeContinued( const ContinuedEvent& ) { +void DebuggerClientListener::debuggeeContinued( const ContinuedEvent&, const SessionId& ) { resetState(); UISceneNode* sceneNode = mPlugin->getUISceneNode(); @@ -339,27 +341,28 @@ void DebuggerClientListener::outputProduced( const Output& output ) { } } -void DebuggerClientListener::debuggingProcess( const ProcessInfo& info ) { +void DebuggerClientListener::debuggingProcess( const ProcessInfo& info, const SessionId& ) { mProcessInfo = info; } void DebuggerClientListener::errorResponse( const std::string& command, const std::string& summary, - const std::optional& /*message*/ ) { + const std::optional& /*message*/, + const SessionId& sessionId ) { if ( command == "evaluate" ) return; if ( command == "launch" ) - failed(); + failed( sessionId ); mPlugin->getPluginContext()->getNotificationCenter()->addNotification( summary, Seconds( 5 ), true ); } -void DebuggerClientListener::threadChanged( const ThreadEvent& ) {} +void DebuggerClientListener::threadChanged( const ThreadEvent&, const std::string& ) {} -void DebuggerClientListener::moduleChanged( const ModuleEvent& ) {} +void DebuggerClientListener::moduleChanged( const ModuleEvent&, const std::string& ) {} -void DebuggerClientListener::threads( std::vector&& threads ) { +void DebuggerClientListener::threads( std::vector&& threads, const SessionId& ) { std::sort( threads.begin(), threads.end(), []( const DapThread& a, const DapThread& b ) { return a.id < b.id; } ); @@ -379,8 +382,9 @@ void DebuggerClientListener::changeScope( const StackFrame& f ) { TextRange range{ { f.line - 1, f.column - 1 }, { f.line - 1, f.column - 1 } }; std::string path( f.source->path ); - mPlugin->getUISceneNode()->runOnMainThread( - [this, path, range] { mPlugin->getPluginContext()->focusOrLoadFile( path, range ); } ); + mPlugin->getUISceneNode()->runOnMainThread( [this, path, range] { + mPlugin->getPluginContext()->focusOrLoadFile( path, range ); + } ); mCurrentScopePos = { f.source->path, f.line }; @@ -399,7 +403,8 @@ void DebuggerClientListener::changeThread( int id ) { sdc->getUIThreads()->setSelection( mThreadsModel->fromThreadId( id ) ); } -void DebuggerClientListener::stackTrace( const int threadId, StackTraceInfo&& stack ) { +void DebuggerClientListener::stackTrace( const int threadId, StackTraceInfo&& stack, + const SessionId& ) { changeThread( threadId ); for ( const auto& f : stack.stackFrames ) { @@ -436,7 +441,8 @@ void DebuggerClientListener::stackTrace( const int threadId, StackTraceInfo&& st } } -void DebuggerClientListener::scopes( const int /*frameId*/, std::vector&& scopes ) { +void DebuggerClientListener::scopes( const int /*frameId*/, std::vector&& scopes, + const SessionId& ) { if ( scopes.empty() ) return; @@ -480,8 +486,8 @@ void DebuggerClientListener::scopes( const int /*frameId*/, std::vector&& } } -void DebuggerClientListener::variables( const int variablesReference, - std::vector&& vars ) { +void DebuggerClientListener::variables( const int variablesReference, std::vector&& vars, + const SessionId& ) { mVariablesHolder->addVariables( variablesReference, std::move( vars ) ); auto scopeIt = mScopeRef.find( variablesReference ); @@ -493,24 +499,26 @@ void DebuggerClientListener::variables( const int variablesReference, } } -void DebuggerClientListener::modules( ModulesInfo&& ) {} +void DebuggerClientListener::modules( ModulesInfo&&, const SessionId& ) {} -void DebuggerClientListener::serverDisconnected() {} +void DebuggerClientListener::serverDisconnected( const SessionId& ) {} void DebuggerClientListener::sourceContent( const std::string& /*path*/, int /*reference*/, - const SourceContent& /*content*/ ) {} + const SourceContent& /*content*/, const SessionId& ) {} void DebuggerClientListener::sourceBreakpoints( const std::string& /*path*/, int /*reference*/, - const std::optional>& /*breakpoints*/ ) {} + const std::optional>& /*breakpoints*/, const SessionId& ) {} -void DebuggerClientListener::breakpointChanged( const BreakpointEvent& ) {} +void DebuggerClientListener::breakpointChanged( const BreakpointEvent&, const SessionId& ) {} void DebuggerClientListener::expressionEvaluated( const std::string& /*expression*/, - const std::optional& ) {} + const std::optional&, + const SessionId& ) {} void DebuggerClientListener::gotoTargets( const Source& /*source*/, const int /*line*/, - const std::vector& /*targets*/ ) {} + const std::vector& /*targets*/, + const SessionId& ) {} bool DebuggerClientListener::isRemote() const { return mIsRemote; diff --git a/src/tools/ecode/plugins/debugger/debuggerclientlistener.hpp b/src/tools/ecode/plugins/debugger/debuggerclientlistener.hpp index a41d1552c..d8e835da5 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclientlistener.hpp +++ b/src/tools/ecode/plugins/debugger/debuggerclientlistener.hpp @@ -20,38 +20,66 @@ class DebuggerClientListener : public DebuggerClient::Listener { virtual ~DebuggerClientListener(); - void stateChanged( DebuggerClient::State ); - void initialized(); - void launched(); - void configured(); - void failed(); - void debuggeeRunning(); - void debuggeeTerminated(); + void stateChanged( DebuggerClient::State, const SessionId& sessionId ); + + void initialized( const SessionId& sessionId ); + + void launched( const SessionId& sessionId ); + + void configured( const SessionId& sessionId ); + + void failed( const SessionId& sessionId ); + + void debuggeeRunning( const SessionId& sessionId ); + + void debuggeeTerminated( const SessionId& sessionId ); void capabilitiesReceived( const Capabilities& capabilities ); - void debuggeeExited( int exitCode ); - void debuggeeStopped( const StoppedEvent& ); - void debuggeeContinued( const ContinuedEvent& ); + + void debuggeeExited( int exitCode, const SessionId& sessionId ); + + void debuggeeStopped( const StoppedEvent&, const SessionId& sessionId ); + + void debuggeeContinued( const ContinuedEvent&, const SessionId& sessionId ); + void outputProduced( const Output& ); - void debuggingProcess( const ProcessInfo& ); + void debuggingProcess( const ProcessInfo&, const SessionId& sessionId ); + void errorResponse( const std::string& command, const std::string& summary, - const std::optional& message ); - void threadChanged( const ThreadEvent& ); - void moduleChanged( const ModuleEvent& ); - void threads( std::vector&& ); - void stackTrace( const int threadId, StackTraceInfo&& ); - void scopes( const int frameId, std::vector&& ); - void variables( const int variablesReference, std::vector&& ); - void modules( ModulesInfo&& ); - void serverDisconnected(); + const std::optional& message, const SessionId& sessionId ); + + void threadChanged( const ThreadEvent&, const SessionId& sessionId ); + + void moduleChanged( const ModuleEvent&, const SessionId& sessionId ); + + void threads( std::vector&&, const SessionId& sessionId ); + + void stackTrace( const int threadId, StackTraceInfo&&, const SessionId& sessionId ); + + void scopes( const int frameId, std::vector&&, const SessionId& sessionId ); + + void variables( const int variablesReference, std::vector&&, + const SessionId& sessionId ); + + void modules( ModulesInfo&&, const SessionId& sessionId ); + + void serverDisconnected( const SessionId& sessionId ); + void sourceContent( const std::string& path, int reference, - const SourceContent& content = SourceContent() ); + const SourceContent& content = SourceContent(), + const SessionId& sessionId = "" ); + void sourceBreakpoints( const std::string& path, int reference, - const std::optional>& breakpoints ); - void breakpointChanged( const BreakpointEvent& ); - void expressionEvaluated( const std::string& expression, const std::optional& ); - void gotoTargets( const Source& source, const int line, - const std::vector& targets ); + const std::optional>& breakpoints, + const SessionId& sessionId ); + + void breakpointChanged( const BreakpointEvent&, const SessionId& sessionId ); + + void expressionEvaluated( const std::string& expression, const std::optional&, + const SessionId& sessionId ); + + void gotoTargets( const Source& source, const int line, const std::vector& targets, + const SessionId& sessionId ); bool isRemote() const; diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp index 72d06b207..d467cc47c 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp @@ -73,6 +73,9 @@ static constexpr auto KEY_DEFAULT_BUILD_TASK = "${defaultBuildTask}"; static constexpr auto KEY_PATH_SEPARATOR = "${pathSeparator}"; static constexpr auto KEY_PATH_SEPARATOR_ABBR = "${/}"; +static constexpr auto KEY_UUID = "${uuid}"; +static constexpr auto KEY_TIMESTAMP = "${timestamp}"; + static constexpr auto KEY_DEBUG_SERVER = "debugServer"; static void replaceExecutableArgs( std::string& arg ) { @@ -1154,6 +1157,10 @@ void DebuggerPlugin::replaceInVal( std::string& val, String::replaceAll( val, KEY_EXEC_PATH, Sys::getProcessFilePath() ); String::replaceAll( val, KEY_PATH_SEPARATOR, FileSystem::getOSSlash() ); String::replaceAll( val, KEY_PATH_SEPARATOR_ABBR, FileSystem::getOSSlash() ); + String::replaceAll( val, KEY_UUID, UUID().toString() ); + String::replaceAll( val, KEY_TIMESTAMP, + std::to_string( std::chrono::system_clock::to_time_t( + std::chrono::system_clock::now() ) ) ); auto* editor = getPluginContext()->getSplitter()->getCurEditor(); if ( getPluginContext()->getSplitter()->getCurEditor() ) { @@ -1759,7 +1766,7 @@ void DebuggerPlugin::runConfig( const std::string& debugger, const std::string& mDapConfigs.begin(), mDapConfigs.end(), [&configuration]( const DapConfig& conf ) { return conf.name == configuration; } ); - if ( configIt == debuggerIt->configurations.end() ) + if ( configIt == mDapConfigs.end() ) return; usingExternalConfig = true; diff --git a/src/tools/ecode/plugins/plugincontextprovider.hpp b/src/tools/ecode/plugins/plugincontextprovider.hpp index 63e8aa82f..d6fd832ec 100644 --- a/src/tools/ecode/plugins/plugincontextprovider.hpp +++ b/src/tools/ecode/plugins/plugincontextprovider.hpp @@ -119,7 +119,8 @@ class PluginContextProvider { virtual std::string getCurrentWorkingDir() const = 0; - virtual void focusOrLoadFile( const std::string& path, const TextRange& range = {} ) = 0; + virtual void focusOrLoadFile( const std::string& path, const TextRange& range = {}, + bool searchInSameContext = false ) = 0; virtual void runCommand( const std::string& command ) = 0;