diff --git a/bin/assets/plugins/debugger.json b/bin/assets/plugins/debugger.json index 0ee2919bf..370c77104 100644 --- a/bin/assets/plugins/debugger.json +++ b/bin/assets/plugins/debugger.json @@ -15,7 +15,8 @@ "args": "${args}", "cwd": "${cwd}", "env": "${env}", - "stopOnEntry": "${stopOnEntry}" + "stopOnEntry": "${stopOnEntry}", + "stopAtBeginningOfMainSubprogram": false } } ] diff --git a/include/eepp/system/process.hpp b/include/eepp/system/process.hpp index 57ee165ed..7660ec241 100644 --- a/include/eepp/system/process.hpp +++ b/include/eepp/system/process.hpp @@ -44,6 +44,8 @@ class EE_API Process { typedef std::function ReadFn; + static std::vector parseArgs( const std::string& str ); + Process(); /** @brief Create a process. diff --git a/src/eepp/system/process.cpp b/src/eepp/system/process.cpp index 3d9dd7e07..1051cf0b3 100644 --- a/src/eepp/system/process.cpp +++ b/src/eepp/system/process.cpp @@ -42,7 +42,7 @@ static bool isFlatpakEnv() { #define PROCESS_PTR ( static_cast( mProcess ) ) -static std::vector parseArgs( const std::string& str ) { +std::vector Process::parseArgs( const std::string& str ) { bool inquote = false; char quoteChar = 0; std::vector res; diff --git a/src/eepp/ui/uiconsole.cpp b/src/eepp/ui/uiconsole.cpp index 6d05f4080..b9999a755 100644 --- a/src/eepp/ui/uiconsole.cpp +++ b/src/eepp/ui/uiconsole.cpp @@ -396,6 +396,10 @@ void UIConsole::setMaxLogLines( const Uint32& maxLogLines ) { void UIConsole::privPushText( String&& str ) { Lock l( mMutex ); + if ( str.find_first_of( '\r' ) != String::InvalidPos ) + String::replaceAll( str, "\r", "" ); + if ( str.empty() ) + return; mCmdLog.push_back( { std::move( str ), String::hash( str ) } ); if ( mVisible ) invalidateDraw(); diff --git a/src/tools/ecode/plugins/debugger/bus.hpp b/src/tools/ecode/plugins/debugger/bus.hpp index 87981315f..d9ecd021f 100644 --- a/src/tools/ecode/plugins/debugger/bus.hpp +++ b/src/tools/ecode/plugins/debugger/bus.hpp @@ -20,6 +20,8 @@ class Bus { virtual size_t write( const char* buffer, const size_t& size ) = 0; + virtual ~Bus() {} + protected: void setState( State state ); diff --git a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp index 6758a4e83..71fdeecba 100644 --- a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp +++ b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp @@ -22,6 +22,10 @@ DebuggerClientDap::DebuggerClientDap( const ProtocolSettings& protocolSettings, std::unique_ptr&& bus ) : mBus( std::move( bus ) ), mProtocol( protocolSettings ) {} +DebuggerClientDap::~DebuggerClientDap() { + mBus.reset(); +} + void DebuggerClientDap::makeRequest( const std::string_view& command, const nlohmann::json& arguments, ResponseHandler onFinish ) { nlohmann::json jsonCmd = { @@ -31,10 +35,12 @@ void DebuggerClientDap::makeRequest( const std::string_view& command, { "arguments", arguments.empty() ? nlohmann::json::object() : arguments } }; std::string cmd = jsonCmd.dump(); + std::string msg( String::format( "Content-Length: %zu\r\n\r\n%s", cmd.size(), cmd ) ); - Log::instance()->writel( mDebug ? LogLevel::Info : LogLevel::Debug, cmd ); + Log::instance()->writel( mDebug ? LogLevel::Info : LogLevel::Debug, + "DebuggerClientDap::makeRequest:" ); + Log::instance()->writel( mDebug ? LogLevel::Info : LogLevel::Debug, msg ); - std::string msg( String::format( "Content-Length: %zu\r\n\r\n%s", cmd.size(), cmd ) ); mBus->write( msg.data(), msg.size() ); mRequests[mIdx] = { std::string{ command }, arguments, onFinish }; @@ -113,9 +119,9 @@ void DebuggerClientDap::requestLaunchCommand() { [this]( const Response& response, const auto& ) { if ( response.success ) { mLaunched = true; + checkRunning(); for ( auto listener : mListeners ) listener->launched(); - checkRunning(); } else { if ( response.errorBody ) { Log::warning( "DebuggerClientDap::requestLaunchCommand: error %ld %s", @@ -131,7 +137,7 @@ void DebuggerClientDap::requestInitialize() { { DAP_CLIENT_ID, "ecode-dap" }, { DAP_CLIENT_NAME, "ecode dap" }, { "locale", mProtocol.locale }, - { DAP_ADAPTER_ID, "qdap" }, + { DAP_ADAPTER_ID, "ecode-dap" }, { DAP_LINES_START_AT1, mProtocol.linesStartAt1 }, { DAP_COLUMNS_START_AT2, mProtocol.columnsStartAt1 }, { DAP_PATH, ( mProtocol.pathFormatURI ? DAP_URI : DAP_PATH ) }, @@ -166,8 +172,10 @@ void DebuggerClientDap::asyncRead( const char* bytes, size_t n ) { #endif auto message = json::parse( data ); - if ( mDebug ) + if ( mDebug ) { + Log::debug( "DebuggerClientDap::asyncRead:" ); Log::debug( message.dump() ); + } processProtocolMessage( message ); #ifndef EE_DEBUG @@ -229,7 +237,7 @@ void DebuggerClientDap::errorResponse( const std::string& summary, void DebuggerClientDap::processEvent( const nlohmann::json& msg ) { const std::string event = msg.value( DAP_EVENT, "" ); - const auto body = msg[DAP_BODY]; + const auto body = msg.contains( DAP_BODY ) ? msg[DAP_BODY] : nlohmann::json{}; if ( "initialized"sv == event ) { processEventInitialized(); @@ -262,6 +270,8 @@ void DebuggerClientDap::processEventInitialized() { return; } setState( State::Initialized ); + + configurationDone(); } void DebuggerClientDap::processEventTerminated() { @@ -337,7 +347,8 @@ std::optional DebuggerClientDap::readHeader() { } const auto header = mBuffer.substr( start, end - start ); - end += DAP_SEP_SIZE; + while ( std::string_view{ mBuffer }.substr( end, 2 ) == DAP_SEP ) + end += DAP_SEP_SIZE; // header block separator if ( header.size() == 0 ) { @@ -362,18 +373,19 @@ std::optional DebuggerClientDap::readHeader() { if ( String::startsWith( header, DAP_CONTENT_LENGTH ) ) { std::string lengthStr( header.substr( sep + 1, header.size() - sep ) ); String::trimInPlace( lengthStr ); - Uint64 length; if ( !String::fromString( length, lengthStr ) ) { Log::error( "DebuggerClientDap::readHeader invalid value: ", header ); discardExploredBuffer(); continue; // CONTINUE HEADER + } else { + break; } } start = end; } - if ( length < 0 ) + if ( length < 0 || length == std::string::npos ) return std::nullopt; return HeaderInfo{ end, length }; @@ -683,4 +695,30 @@ 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" ); + return false; + } + + if ( !mAdapterCapabilities.supportsConfigurationDoneRequest ) { + for ( auto listener : mListeners ) + listener->configured(); + return true; + } + + makeRequest( "configurationDone", nlohmann::json{}, + [this]( const auto& response, const auto& ) { + if ( response.success ) { + mConfigured = true; + checkRunning(); + for ( auto listener : mListeners ) + listener->configured(); + } + } ); + + 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 035cdfc87..ca9bd2735 100644 --- a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp +++ b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp @@ -18,6 +18,8 @@ class DebuggerClientDap : public DebuggerClient { DebuggerClientDap( const ProtocolSettings& protocolSettings, std::unique_ptr&& bus ); + virtual ~DebuggerClientDap(); + bool start() override; bool resume( int threadId, bool singleThread = false ) override; @@ -38,11 +40,12 @@ class DebuggerClientDap : public DebuggerClient { bool threads() override; - bool stackTrace( int threadId, int startFrame, int levels ) override; + bool stackTrace( int threadId, int startFrame = 0, int levels = 0 ) override; bool scopes( int frameId ) override; - bool variables( int variablesReference, Variable::Type filter, int start, int count ) override; + bool variables( int variablesReference, Variable::Type filter = Variable::Type::Both, + int start = 0, int count = 0 ) override; bool modules( int start, int count ) override; @@ -69,6 +72,8 @@ class DebuggerClientDap : public DebuggerClient { bool watch( const std::string& expression, std::optional frameId ) override; + bool configurationDone() override; + protected: std::unique_ptr mBus; UnorderedMap> mBreakpoints; @@ -131,6 +136,7 @@ class DebuggerClientDap : public DebuggerClient { void requestLaunchCommand(); void processResponseInitialize( const Response& response, const nlohmann::json& ); + void processResponseNext( const Response& response, const nlohmann::json& request ); }; diff --git a/src/tools/ecode/plugins/debugger/dap/protocol.cpp b/src/tools/ecode/plugins/debugger/dap/protocol.cpp index 9df7f9959..ebf7386eb 100644 --- a/src/tools/ecode/plugins/debugger/dap/protocol.cpp +++ b/src/tools/ecode/plugins/debugger/dap/protocol.cpp @@ -1,5 +1,5 @@ -#include "messages.hpp" #include "protocol.hpp" +#include "messages.hpp" #include #include #include @@ -7,40 +7,46 @@ using namespace EE; using namespace EE::System; -std::optional parseOptionalInt( const json& value ) { - if ( value.is_null() || value.empty() || !value.is_number() ) { +std::optional parseOptionalInt( const json& value, const std::string_view& name ) { + if ( !value.contains( name ) || value[name].is_null() || value[name].empty() || + !value[name].is_number() ) { return std::nullopt; } - return value.get(); + return value[name].get(); } -std::optional parseOptionalBool( const json& value ) { - if ( value.is_null() || value.empty() || !value.is_boolean() ) { +std::optional parseOptionalBool( const json& value, const std::string_view& name ) { + if ( !value.contains( name ) || value[name].is_null() || value[name].empty() || + !value[name].is_boolean() ) { return std::nullopt; } - return value.get(); + return value[name].get(); } -std::optional parseOptionalString( const json& value ) { - if ( value.is_null() || value.empty() || !value.is_string() ) { +std::optional parseOptionalString( const json& value, const std::string_view& name ) { + if ( !value.contains( name ) || value[name].is_null() || value[name].empty() || + !value[name].is_string() ) { return std::nullopt; } - return value.get(); + return value[name].get(); } -template std::optional parseOptionalObject( const json& value ) { - if ( value.is_null() || value.empty() || !value.is_object() ) { +template +std::optional parseOptionalObject( const json& value, const std::string_view& name ) { + if ( !value.contains( name ) || value[name].is_null() || value[name].empty() || + !value[name].is_object() ) { return std::nullopt; } - return T( value ); + return T( value[name] ); } std::optional> -parseOptionalStringMap( const json& value ) { - if ( value.is_null() || value.empty() || !value.is_object() ) { +parseOptionalStringMap( const json& value, const std::string_view& name ) { + if ( !value.contains( name ) || value[name].is_null() || value[name].empty() || + !value[name].is_object() ) { return std::nullopt; } - const auto& dict = value; + const auto& dict = value[name]; std::unordered_map map; for ( auto it = dict.begin(); it != dict.end(); ++it ) { map[it.key()] = it.value().get(); @@ -56,12 +62,14 @@ template std::vector parseObjectList( const json& array ) { return out; } -std::optional> parseOptionalIntList( const json& value ) { - if ( value.is_null() || value.empty() || !value.is_array() ) { +std::optional> parseOptionalIntList( const json& value, + const std::string_view& name ) { + if ( !value.contains( name ) || value[name].is_null() || value[name].empty() || + !value[name].is_array() ) { return std::nullopt; } std::vector values; - for ( const auto& item : value ) { + for ( const auto& item : value[name] ) { values.push_back( item.get() ); } return values; @@ -80,19 +88,19 @@ namespace ecode::dap { Message::Message( const json& body ) : id( body[DAP_ID].get() ), format( body["format"].get() ), - variables( parseOptionalStringMap( body["variables"] ) ), - sendTelemetry( parseOptionalBool( body["sendTelemetry"] ) ), - showUser( parseOptionalBool( body["showUser"] ) ), - url( parseOptionalString( body["url"] ) ), - urlLabel( parseOptionalString( body["urlLabel"] ) ) {} + variables( parseOptionalStringMap( body, "variables" ) ), + sendTelemetry( parseOptionalBool( body, "sendTelemetry" ) ), + showUser( parseOptionalBool( body, "showUser" ) ), + url( parseOptionalString( body, "url" ) ), + urlLabel( parseOptionalString( body, "urlLabel" ) ) {} Response::Response( const json& msg ) : request_seq( msg.value( "request_seq", -1 ) ), success( msg.value( "success", false ) ), command( msg.value( DAP_COMMAND, "" ) ), message( msg.value( "message", "" ) ), - body( msg[DAP_BODY] ), - errorBody( success ? std::nullopt : parseOptionalObject( body["error"] ) ) {} + body( msg.contains( DAP_BODY ) ? msg[DAP_BODY] : nlohmann::json{} ), + errorBody( success ? std::nullopt : parseOptionalObject( body, "error" ) ) {} bool Response::isCancelled() const { return message == "cancelled"; @@ -100,20 +108,20 @@ bool Response::isCancelled() const { ProcessInfo::ProcessInfo( const json& body ) : name( body.value( DAP_NAME, "" ) ), - systemProcessId( parseOptionalInt( body[DAP_SYSTEM_PROCESS_ID] ) ), - isLocalProcess( parseOptionalBool( body[DAP_IS_LOCAL_PROCESS] ) ), - startMethod( parseOptionalString( body[DAP_START_METHOD] ) ), - pointerSize( parseOptionalInt( body[DAP_POINTER_SIZE] ) ) {} + systemProcessId( parseOptionalInt( body, DAP_SYSTEM_PROCESS_ID ) ), + isLocalProcess( parseOptionalBool( body, DAP_IS_LOCAL_PROCESS ) ), + startMethod( parseOptionalString( body, DAP_START_METHOD ) ), + pointerSize( parseOptionalInt( body, DAP_POINTER_SIZE ) ) {} Output::Output( const json& body ) : category( Category::Unknown ), output( body.value( DAP_OUTPUT, "" ) ), group( std::nullopt ), - variablesReference( parseOptionalInt( body[DAP_VARIABLES_REFERENCE] ) ), - source( parseOptionalObject( DAP_SOURCE ) ), - line( parseOptionalInt( body[DAP_LINE] ) ), - column( parseOptionalInt( body[DAP_COLUMN] ) ), - data( body[DAP_DATA] ) { + variablesReference( parseOptionalInt( body, DAP_VARIABLES_REFERENCE ) ), + source( parseOptionalObject( body, DAP_SOURCE ) ), + line( parseOptionalInt( body, DAP_LINE ) ), + column( parseOptionalInt( body, DAP_COLUMN ) ), + data( body.contains( DAP_DATA ) ? body[DAP_DATA] : nlohmann::json{} ) { if ( body.contains( DAP_GROUP ) ) { const auto value = body[DAP_GROUP].get(); if ( DAP_START == value ) { @@ -159,12 +167,12 @@ std::string Source::getUnifiedId( const std::string& path, std::optional so } Source::Source( const json& body ) : - name( body[DAP_NAME].get() ), - path( body[DAP_PATH].get() ), - sourceReference( parseOptionalInt( body[DAP_SOURCE_REFERENCE] ) ), - presentationHint( parseOptionalString( body[DAP_PRESENTATION_HINT] ) ), - origin( body[DAP_ORIGIN].get() ), - adapterData( body[DAP_ADAPTER_DATA] ) { + name( body.value( DAP_NAME, "" ) ), + path( body.value( DAP_PATH, "" ) ), + sourceReference( parseOptionalInt( body, DAP_SOURCE_REFERENCE ) ), + presentationHint( parseOptionalString( body, DAP_PRESENTATION_HINT ) ), + origin( body.value( DAP_ORIGIN, "" ) ), + adapterData( body.contains( DAP_ADAPTER_DATA ) ? body[DAP_ADAPTER_DATA] : nlohmann::json{} ) { // sources if ( body.contains( DAP_SOURCES ) ) { const auto values = body[DAP_SOURCES]; @@ -225,27 +233,27 @@ json Checksum::toJson() const { } Capabilities::Capabilities( const json& body ) : - supportsConfigurationDoneRequest( body["supportsConfigurationDoneRequest"].get() ), - supportsFunctionBreakpoints( body["supportsFunctionBreakpoints"].get() ), - supportsConditionalBreakpoints( body["supportsConditionalBreakpoints"].get() ), - supportsHitConditionalBreakpoints( body["supportsHitConditionalBreakpoints"].get() ), - supportsLogPoints( body["supportsLogPoints"].get() ), - supportsModulesRequest( body["supportsModulesRequest"].get() ), - supportsTerminateRequest( body["supportsTerminateRequest"].get() ), - supportTerminateDebuggee( body["supportTerminateDebuggee"].get() ), - supportsGotoTargetsRequest( body["supportsGotoTargetsRequest"].get() ) {} + supportsConfigurationDoneRequest( body.value( "supportsConfigurationDoneRequest", false ) ), + supportsFunctionBreakpoints( body.value( "supportsFunctionBreakpoints", false ) ), + supportsConditionalBreakpoints( body.value( "supportsConditionalBreakpoints", false ) ), + supportsHitConditionalBreakpoints( body.value( "supportsHitConditionalBreakpoints", false ) ), + supportsLogPoints( body.value( "supportsLogPoints", false ) ), + supportsModulesRequest( body.value( "supportsModulesRequest", false ) ), + supportsTerminateRequest( body.value( "supportsTerminateRequest", false ) ), + supportTerminateDebuggee( body.value( "supportTerminateDebuggee", false ) ), + supportsGotoTargetsRequest( body.value( "supportsGotoTargetsRequest", false ) ) {} ThreadEvent::ThreadEvent( const json& body ) : reason( body[DAP_REASON].get() ), threadId( body[DAP_THREAD_ID].get() ) {} StoppedEvent::StoppedEvent( const json& body ) : reason( body[DAP_REASON].get() ), - description( parseOptionalString( body["description"] ) ), + description( parseOptionalString( body, "description" ) ), threadId( body[DAP_THREAD_ID].get() ), - preserveFocusHint( parseOptionalBool( body["preserveFocusHint"] ) ), - text( parseOptionalString( body["text"] ) ), - allThreadsStopped( parseOptionalBool( body["allThreadsStopped"] ) ), - hitBreakpointsIds( parseOptionalIntList( body["hitBreakpointsIds"] ) ) {} + preserveFocusHint( parseOptionalBool( body, "preserveFocusHint" ) ), + text( parseOptionalString( body, "text" ) ), + allThreadsStopped( parseOptionalBool( body, "allThreadsStopped" ) ), + hitBreakpointsIds( parseOptionalIntList( body, "hitBreakpointsIds" ) ) {} Thread::Thread( const json& body ) : id( body[DAP_ID].get() ), name( body[DAP_NAME].get() ) {} @@ -259,48 +267,48 @@ std::vector Thread::parseList( const json& threads ) { StackFrame::StackFrame( const json& body ) : id( body[DAP_ID].get() ), name( body[DAP_NAME].get() ), - source( parseOptionalObject( body[DAP_SOURCE] ) ), + source( parseOptionalObject( body, DAP_SOURCE ) ), line( body[DAP_LINE].get() ), column( body[DAP_COLUMN].get() ), - endLine( parseOptionalInt( body["endLine"] ) ), - canRestart( parseOptionalBool( ( body["canRestart"] ) ) ), - instructionPointerReference( parseOptionalString( body["instructionPointerReference"] ) ), - moduleId_int( parseOptionalInt( body[DAP_MODULE_ID] ) ), - moduleId_str( parseOptionalString( body[DAP_MODULE_ID] ) ), - presentationHint( parseOptionalString( body[DAP_PRESENTATION_HINT] ) ) {} + endLine( parseOptionalInt( body, "endLine" ) ), + canRestart( parseOptionalBool( body, "canRestart" ) ), + instructionPointerReference( parseOptionalString( body, "instructionPointerReference" ) ), + moduleId_int( parseOptionalInt( body, DAP_MODULE_ID ) ), + moduleId_str( parseOptionalString( body, DAP_MODULE_ID ) ), + presentationHint( parseOptionalString( body, DAP_PRESENTATION_HINT ) ) {} StackTraceInfo::StackTraceInfo( const json& body ) : stackFrames( parseObjectList( body["stackFrames"] ) ), - totalFrames( parseOptionalInt( body["totalFrames"] ) ) {} + totalFrames( parseOptionalInt( body, "totalFrames" ) ) {} Module::Module( const json& body ) : - id_int( parseOptionalInt( body[DAP_ID] ) ), - id_str( parseOptionalString( body[DAP_ID] ) ), + id_int( parseOptionalInt( body, DAP_ID ) ), + id_str( parseOptionalString( body, DAP_ID ) ), name( body[DAP_NAME].get() ), - path( parseOptionalString( body[DAP_PATH] ) ), - isOptimized( parseOptionalBool( body["isOptimized"] ) ), - isUserCode( parseOptionalBool( body["isUserCode"] ) ), - version( parseOptionalString( body["version"] ) ), - symbolStatus( parseOptionalString( body["symbolStatus"] ) ), - symbolFilePath( parseOptionalString( body["symbolFilePath"] ) ), - dateTimeStamp( parseOptionalString( body["dateTimeStamp"] ) ), - addressRange( parseOptionalString( body["addressRange"] ) ) {} + path( parseOptionalString( body, DAP_PATH ) ), + isOptimized( parseOptionalBool( body, "isOptimized" ) ), + isUserCode( parseOptionalBool( body, "isUserCode" ) ), + version( parseOptionalString( body, "version" ) ), + symbolStatus( parseOptionalString( body, "symbolStatus" ) ), + symbolFilePath( parseOptionalString( body, "symbolFilePath" ) ), + dateTimeStamp( parseOptionalString( body, "dateTimeStamp" ) ), + addressRange( parseOptionalString( body, "addressRange" ) ) {} ModuleEvent::ModuleEvent( const json& body ) : reason( body[DAP_REASON].get() ), module( Module( body["module"] ) ) {} Scope::Scope( const json& body ) : name( body[DAP_NAME].get() ), - presentationHint( parseOptionalString( body[DAP_PRESENTATION_HINT] ) ), + presentationHint( parseOptionalString( body, DAP_PRESENTATION_HINT ) ), variablesReference( body[DAP_VARIABLES_REFERENCE].get() ), - namedVariables( parseOptionalInt( body["namedVariables"] ) ), - indexedVariables( parseOptionalInt( body["indexedVariables"] ) ), - expensive( parseOptionalBool( body["expensive"] ) ), - source( parseOptionalObject( body["source"] ) ), - line( parseOptionalInt( body["line"] ) ), - column( parseOptionalInt( body["column"] ) ), - endLine( parseOptionalInt( body["endLine"] ) ), - endColumn( parseOptionalInt( body["endColumn"] ) ) {} + namedVariables( parseOptionalInt( body, "namedVariables" ) ), + indexedVariables( parseOptionalInt( body, "indexedVariables" ) ), + expensive( parseOptionalBool( body, "expensive" ) ), + source( parseOptionalObject( body, "source" ) ), + line( parseOptionalInt( body, "line" ) ), + column( parseOptionalInt( body, "column" ) ), + endLine( parseOptionalInt( body, "endLine" ) ), + endColumn( parseOptionalInt( body, "endColumn" ) ) {} Scope::Scope( int variablesReference, std::string name ) : name( name ), variablesReference( variablesReference ) {} @@ -312,12 +320,12 @@ std::vector Scope::parseList( const json& scopes ) { Variable::Variable( const json& body ) : name( body[DAP_NAME].get() ), value( body["value"].get() ), - type( parseOptionalString( body[DAP_TYPE].get() ) ), - evaluateName( parseOptionalString( body["evaluateName"].get() ) ), + type( parseOptionalString( body, DAP_TYPE ) ), + evaluateName( parseOptionalString( body, "evaluateName" ) ), variablesReference( body[DAP_VARIABLES_REFERENCE].get() ), - namedVariables( parseOptionalInt( body["namedVariables"] ) ), - indexedVariables( parseOptionalInt( body["indexedVariables"] ) ), - memoryReference( parseOptionalString( body["memoryReference"] ) ) {} + namedVariables( parseOptionalInt( body, "namedVariables" ) ), + indexedVariables( parseOptionalInt( body, "indexedVariables" ) ), + memoryReference( parseOptionalString( body, "memoryReference" ) ) {} Variable::Variable( const std::string& name, const std::string& value, const int reference ) : name( name ), value( value ), variablesReference( reference ) {} @@ -328,18 +336,18 @@ std::vector Variable::parseList( const json& variables ) { ModulesInfo::ModulesInfo( const json& body ) : modules( parseObjectList( body[DAP_MODULES] ) ), - totalModules( parseOptionalInt( body["totalModules"] ) ) {} + totalModules( parseOptionalInt( body, "totalModules" ) ) {} ContinuedEvent::ContinuedEvent( const json& body ) : threadId( body[DAP_THREAD_ID].get() ), - allThreadsContinued( parseOptionalBool( body[DAP_ALL_THREADS_CONTINUED] ) ) {} + allThreadsContinued( parseOptionalBool( body, DAP_ALL_THREADS_CONTINUED ) ) {} ContinuedEvent::ContinuedEvent( int threadId, bool allThreadsContinued ) : threadId( threadId ), allThreadsContinued( allThreadsContinued ) {} SourceContent::SourceContent( const json& body ) : content( body["content"].get() ), - mimeType( parseOptionalString( body["mimeType"] ) ) {} + mimeType( parseOptionalString( body, "mimeType" ) ) {} SourceContent::SourceContent( const std::string& path ) { const FileInfo file( path ); @@ -351,10 +359,10 @@ SourceContent::SourceContent( const std::string& path ) { SourceBreakpoint::SourceBreakpoint( const json& body ) : line( body[DAP_LINE].get() ), - column( parseOptionalInt( body[DAP_COLUMN] ) ), - condition( parseOptionalString( body[DAP_CONDITION] ) ), - hitCondition( parseOptionalString( body[DAP_HIT_CONDITION] ) ), - logMessage( parseOptionalString( body["logMessage"] ) ) {} + column( parseOptionalInt( body, DAP_COLUMN ) ), + condition( parseOptionalString( body, DAP_CONDITION ) ), + hitCondition( parseOptionalString( body, DAP_HIT_CONDITION ) ), + logMessage( parseOptionalString( body, "logMessage" ) ) {} SourceBreakpoint::SourceBreakpoint( const int line ) : line( line ) {} @@ -377,16 +385,16 @@ json SourceBreakpoint::toJson() const { } Breakpoint::Breakpoint( const json& body ) : - id( parseOptionalInt( body[DAP_ID] ) ), - verified( body["verified"].get() ), - message( parseOptionalString( body["message"] ) ), - source( parseOptionalObject( body[DAP_SOURCE] ) ), - line( parseOptionalInt( body[DAP_LINE] ) ), - column( parseOptionalInt( body[DAP_COLUMN] ) ), - endLine( parseOptionalInt( body[DAP_END_LINE] ) ), - endColumn( parseOptionalInt( body[DAP_END_COLUMN] ) ), - instructionReference( parseOptionalString( body["instructionReference"] ) ), - offset( parseOptionalInt( body["offset"] ) ) {} + id( parseOptionalInt( body, DAP_ID ) ), + verified( body.value( "verified", false ) ), + message( parseOptionalString( body, "message" ) ), + source( parseOptionalObject( body, DAP_SOURCE ) ), + line( parseOptionalInt( body, DAP_LINE ) ), + column( parseOptionalInt( body, DAP_COLUMN ) ), + endLine( parseOptionalInt( body, DAP_END_LINE ) ), + endColumn( parseOptionalInt( body, DAP_END_COLUMN ) ), + instructionReference( parseOptionalString( body, "instructionReference" ) ), + offset( parseOptionalInt( body, "offset" ) ) {} Breakpoint::Breakpoint( const int line ) : line( line ) {} @@ -396,20 +404,20 @@ BreakpointEvent::BreakpointEvent( const json& body ) : EvaluateInfo::EvaluateInfo( const json& body ) : result( body[DAP_RESULT].get() ), - type( parseOptionalString( body[DAP_TYPE] ) ), + type( parseOptionalString( body, DAP_TYPE ) ), variablesReference( body[DAP_VARIABLES_REFERENCE].get() ), - namedVariables( parseOptionalInt( body["namedVariables"] ) ), - indexedVariables( parseOptionalInt( body["indexedVariables"] ) ), - memoryReference( parseOptionalString( body["memoryReference"] ) ) {} + namedVariables( parseOptionalInt( body, "namedVariables" ) ), + indexedVariables( parseOptionalInt( body, "indexedVariables" ) ), + memoryReference( parseOptionalString( body, "memoryReference" ) ) {} GotoTarget::GotoTarget( const json& body ) : id( body[DAP_ID].get() ), label( body["label"].get() ), line( body[DAP_LINE].get() ), - column( parseOptionalInt( body[DAP_COLUMN] ) ), - endLine( parseOptionalInt( body[DAP_END_LINE] ) ), - endColumn( parseOptionalInt( body[DAP_END_COLUMN] ) ), - instructionPointerReference( parseOptionalString( body["instructionPointerReference"] ) ) {} + column( parseOptionalInt( body, DAP_COLUMN ) ), + endLine( parseOptionalInt( body, DAP_END_LINE ) ), + endColumn( parseOptionalInt( body, DAP_END_COLUMN ) ), + instructionPointerReference( parseOptionalString( body, "instructionPointerReference" ) ) {} std::vector GotoTarget::parseList( const json& variables ) { return parseObjectList( variables ); diff --git a/src/tools/ecode/plugins/debugger/debuggerclient.cpp b/src/tools/ecode/plugins/debugger/debuggerclient.cpp index 0bdfa99c1..adc984382 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclient.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerclient.cpp @@ -19,7 +19,7 @@ void DebuggerClient::debuggeeRunning() { void DebuggerClient::debuggeeTerminated() { for ( auto listener : mListeners ) - listener->debuggeeRunning(); + listener->debuggeeTerminated(); } void DebuggerClient::failed() { diff --git a/src/tools/ecode/plugins/debugger/debuggerclient.hpp b/src/tools/ecode/plugins/debugger/debuggerclient.hpp index 05344d93d..90482d624 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclient.hpp +++ b/src/tools/ecode/plugins/debugger/debuggerclient.hpp @@ -15,6 +15,7 @@ class DebuggerClient { 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; @@ -69,14 +70,14 @@ class DebuggerClient { virtual bool threads() = 0; - virtual bool stackTrace( int threadId, int startFrame, int levels ) = 0; + virtual bool stackTrace( int threadId, int startFrame = 0, int levels = 0 ) = 0; 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 variables( int variablesReference, Variable::Type filter = Variable::Type::Both, + int start = 0, int count = 0 ) = 0; virtual bool evaluate( const std::string& expression, const std::string& context, std::optional frameId ) = 0; @@ -101,10 +102,14 @@ class DebuggerClient { virtual bool watch( const std::string& expression, std::optional frameId ) = 0; + virtual bool configurationDone() = 0; + void addListener( Listener* listener ); void removeListener( Listener* listener ); + virtual ~DebuggerClient() {} + protected: void setState( const State& state ); diff --git a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp index 2a01bcd70..a463d51e9 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp @@ -1,24 +1,42 @@ #include "debuggerclientlistener.hpp" +#include "debuggerplugin.hpp" namespace ecode { +DebuggerClientListener::DebuggerClientListener( DebuggerClient* client, DebuggerPlugin* plugin ) : + mClient( client ), mPlugin( plugin ) { + eeASSERT( mClient && mPlugin ); +} + void DebuggerClientListener::stateChanged( DebuggerClient::State ) {} -void DebuggerClientListener::initialized() {} +void DebuggerClientListener::initialized() { + // mClient->setBreakpoints( "/home/programming/eepp/src/tools/ecode/ecode.cpp", + // { SourceBreakpoint( 4116 ) } ); +} void DebuggerClientListener::launched() {} +void DebuggerClientListener::configured() {} + void DebuggerClientListener::failed() {} void DebuggerClientListener::debuggeeRunning() {} void DebuggerClientListener::debuggeeTerminated() {} -void DebuggerClientListener::capabilitiesReceived( const Capabilities& capabilities ) {} +void DebuggerClientListener::capabilitiesReceived( const Capabilities& /*capabilities*/ ) {} -void DebuggerClientListener::debuggeeExited( int exitCode ) {} +void DebuggerClientListener::debuggeeExited( int /*exitCode*/ ) { + mPlugin->exitDebugger(); +} -void DebuggerClientListener::debuggeeStopped( const StoppedEvent& ) {} +void DebuggerClientListener::debuggeeStopped( const StoppedEvent& event ) { + Log::warning( "DebuggerClientListener::debuggeeStopped: reason %s", event.reason ); + // if ( event.threadId ) { + // mClient->stackTrace( *event.threadId ); + // } +} void DebuggerClientListener::debuggeeContinued( const ContinuedEvent& ) {} @@ -26,39 +44,50 @@ void DebuggerClientListener::outputProduced( const Output& ) {} void DebuggerClientListener::debuggingProcess( const ProcessInfo& ) {} -void DebuggerClientListener::errorResponse( const std::string& summary, - const std::optional& message ) {} +void DebuggerClientListener::errorResponse( const std::string& /*summary*/, + const std::optional& /*message*/ ) {} void DebuggerClientListener::threadChanged( const ThreadEvent& ) {} void DebuggerClientListener::moduleChanged( const ModuleEvent& ) {} -void DebuggerClientListener::threads( const std::vector& ) {} +void DebuggerClientListener::threads( const std::vector& /*threads*/ ) {} -void DebuggerClientListener::stackTrace( const int threadId, const StackTraceInfo& ) {} +void DebuggerClientListener::stackTrace( const int /*threadId*/, const StackTraceInfo& stack ) { + // if ( !stack.stackFrames.empty() ) { + // mClient->scopes( stack.stackFrames[0].id ); + // } +} -void DebuggerClientListener::scopes( const int frameId, const std::vector& ) {} +void DebuggerClientListener::scopes( const int /*frameId**/, const std::vector& scopes ) { + // if ( !scopes.empty() ) { + // mClient->variables( scopes[0].variablesReference ); + // } +} -void DebuggerClientListener::variables( const int variablesReference, - const std::vector& ) {} +void DebuggerClientListener::variables( const int /*variablesReference*/, + const std::vector& vars ) { + // if ( !vars.empty() ) { + // mClient->resume( 1 ); + // } +} void DebuggerClientListener::modules( const ModulesInfo& ) {} void DebuggerClientListener::serverDisconnected() {} -void DebuggerClientListener::sourceContent( const std::string& path, int reference, - const SourceContent& content ) {} +void DebuggerClientListener::sourceContent( const std::string& /*path*/, int /*reference*/, + const SourceContent& /*content*/ ) {} void DebuggerClientListener::sourceBreakpoints( - const std::string& path, int reference, - const std::optional>& breakpoints ) {} + const std::string& /*path*/, int /*reference*/, + const std::optional>& /*breakpoints*/ ) {} void DebuggerClientListener::breakpointChanged( const BreakpointEvent& ) {} -void DebuggerClientListener::expressionEvaluated( const std::string& expression, +void DebuggerClientListener::expressionEvaluated( const std::string& /*expression*/, const std::optional& ) {} -void DebuggerClientListener::gotoTargets( const Source& source, const int line, - - const std::vector& targets ) {} +void DebuggerClientListener::gotoTargets( const Source& /*source*/, const int /*line*/, + const std::vector& /*targets*/ ) {} } // namespace ecode diff --git a/src/tools/ecode/plugins/debugger/debuggerclientlistener.hpp b/src/tools/ecode/plugins/debugger/debuggerclientlistener.hpp index 71944c16c..afcbf023a 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclientlistener.hpp +++ b/src/tools/ecode/plugins/debugger/debuggerclientlistener.hpp @@ -3,11 +3,16 @@ namespace ecode { +class DebuggerPlugin; + class DebuggerClientListener : public DebuggerClient::Listener { public: + DebuggerClientListener( DebuggerClient* client, DebuggerPlugin* plugin ); + void stateChanged( DebuggerClient::State ); void initialized(); void launched(); + void configured(); void failed(); void debuggeeRunning(); void debuggeeTerminated(); @@ -27,13 +32,18 @@ class DebuggerClientListener : public DebuggerClient::Listener { void variables( const int variablesReference, const std::vector& ); void modules( const ModulesInfo& ); void serverDisconnected(); - void sourceContent( const std::string& path, int reference, const SourceContent& content = SourceContent() ); - void sourceBreakpoints( const std::string& path, int reference, const std::optional>& breakpoints ); + void sourceContent( const std::string& path, int reference, + const SourceContent& content = SourceContent() ); + 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 ); + void gotoTargets( const Source& source, const int line, + const std::vector& targets ); protected: + DebuggerClient* mClient{ nullptr }; + DebuggerPlugin* mPlugin{ nullptr }; }; } // namespace ecode diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp index 6c7ff8852..a6231019e 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp @@ -1,4 +1,5 @@ #include "debuggerplugin.hpp" +#include "../../projectbuild.hpp" #include "busprocess.hpp" #include "dap/debuggerclientdap.hpp" #include @@ -48,6 +49,9 @@ DebuggerPlugin::~DebuggerPlugin() { if ( mSidePanel && mTab ) mSidePanel->removeTab( mTab ); + + mDebugger.reset(); + mListener.reset(); } void DebuggerPlugin::load( PluginManager* pluginManager ) { @@ -256,10 +260,10 @@ void DebuggerPlugin::updateSidePanelTab() { updateDebuggerConfigurationList(); - UIPushButton* runButton = mTabContents->find( "run_button" ); + mRunButton = mTabContents->find( "run_button" ); - if ( !runButton->hasEventsOfType( Event::MouseClick ) ) { - runButton->onClick( [this]( auto ) { + if ( !mRunButton->hasEventsOfType( Event::MouseClick ) ) { + mRunButton->onClick( [this]( auto ) { runConfig( mUIDebuggerList->getListBox()->getItemSelectedText().toUtf8(), mUIDebuggerConfList->getListBox()->getItemSelectedText().toUtf8() ); } ); @@ -293,6 +297,38 @@ void DebuggerPlugin::updateDebuggerConfigurationList() { mUIDebuggerConfList->getListBox()->setSelected( 0L ); } +void DebuggerPlugin::replaceKeysInJson( nlohmann::json& json ) { + static constexpr auto KEY_FILE = "${file}"; + static constexpr auto KEY_ARGS = "${args}"; + static constexpr auto KEY_CWD = "${cwd}"; + static constexpr auto KEY_ENV = "${env}"; + static constexpr auto KEY_STOPONENTRY = "${stopOnEntry}"; + auto runConfig = getManager()->getProjectBuildManager()->getCurrentRunConfig(); + + for ( auto& j : json ) { + if ( j.is_object() ) { + replaceKeysInJson( j ); + } else if ( j.is_string() ) { + std::string val( j.get() ); + if ( runConfig && !runConfig->cmd.empty() && val == KEY_FILE ) { + j = runConfig->cmd; + } else if ( runConfig && !runConfig->args.empty() && val == KEY_ARGS ) { + auto argsArr = nlohmann::json::array(); + auto args = Process::parseArgs( runConfig->args ); + for ( const auto& arg : args ) + argsArr.push_back( arg ); + j = argsArr; + } else if ( runConfig && val == KEY_CWD ) { + j = runConfig->workingDir; + } else if ( runConfig && val == KEY_ENV ) { + j = nlohmann::json{}; + } else if ( val == KEY_STOPONENTRY ) { + j = false; + } + } + } +} + void DebuggerPlugin::runConfig( const std::string& debugger, const std::string& configuration ) { auto debuggerIt = std::find_if( mDaps.begin(), mDaps.end(), [&debugger]( const DapTool& dap ) { return dap.name == debugger; @@ -312,17 +348,41 @@ void DebuggerPlugin::runConfig( const std::string& debugger, const std::string& ProtocolSettings protocolSettings; protocolSettings.launchCommand = configIt->command; - protocolSettings.launchRequest = configIt->args; + auto args = configIt->args; + replaceKeysInJson( args ); + protocolSettings.launchRequest = args; Command cmd; cmd.command = debuggerIt->run.command; cmd.arguments = debuggerIt->run.args; + auto bus = std::make_unique( cmd ); mDebugger = std::make_unique( protocolSettings, std::move( bus ) ); - mListener = std::make_unique(); + mListener = std::make_unique( mDebugger.get(), this ); mDebugger->addListener( mListener.get() ); - mDebugger->start(); + + mRunButton->setEnabled( false ); + + mThreadPool->run( + [this] { mDebugger->start(); }, + [this]( const Uint64& ) { + if ( !mDebugger || mDebugger->state() != DebuggerClient::State::Running ) { + mRunButton->runOnMainThread( [this] { mRunButton->setEnabled( false ); } ); + } + } ); +} + +void DebuggerPlugin::exitDebugger() { + if ( mDebugger && mListener ) + mDebugger->removeListener( mListener.get() ); + mThreadPool->run( [this] { + mDebugger.reset(); + mListener.reset(); + } ); + if ( getUISceneNode() ) { + getUISceneNode()->runOnMainThread( [this] { mRunButton->setEnabled( true ); } ); + } } void DebuggerPlugin::hideSidePanel() { diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp index 3b7fed8df..2d247952a 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp @@ -38,7 +38,7 @@ class DebuggerPlugin : public PluginBase { static Plugin* NewSync( PluginManager* pluginManager ); - ~DebuggerPlugin(); + virtual ~DebuggerPlugin(); std::string getId() override { return Definition().id; } @@ -47,6 +47,8 @@ class DebuggerPlugin : public PluginBase { std::string getDescription() override { return Definition().description; } protected: + friend class DebuggerClientListener; + std::vector mDaps; std::unique_ptr mDebugger; std::unique_ptr mListener; @@ -57,6 +59,7 @@ class DebuggerPlugin : public PluginBase { UIWidget* mTabContents{ nullptr }; UIDropDownList* mUIDebuggerList{ nullptr }; UIDropDownList* mUIDebuggerConfList{ nullptr }; + UIPushButton* mRunButton{ nullptr }; DebuggerPlugin( PluginManager* pluginManager, bool sync ); @@ -77,6 +80,10 @@ class DebuggerPlugin : public PluginBase { void loadDAPConfig( const std::string& path, bool updateConfigFile ); void runConfig( const std::string& debugger, const std::string& configuration ); + + void exitDebugger(); + + void replaceKeysInJson( nlohmann::json& json ); }; } // namespace ecode diff --git a/src/tools/ecode/projectbuild.cpp b/src/tools/ecode/projectbuild.cpp index 7e828402e..b70141876 100644 --- a/src/tools/ecode/projectbuild.cpp +++ b/src/tools/ecode/projectbuild.cpp @@ -767,7 +767,7 @@ void ProjectBuildManager::runCurrentConfig( StatusAppOutputController* saoc, boo } } -bool ProjectBuildManager::hasBuildConfig() { +bool ProjectBuildManager::hasBuildConfig() const { return !getBuilds().empty() && !mConfig.buildName.empty(); } @@ -779,6 +779,20 @@ bool ProjectBuildManager::hasRunConfig() { return false; } +std::optional ProjectBuildManager::getCurrentRunConfig() { + if ( hasBuildConfig() ) { + auto build = getBuild( mConfig.buildName ); + if ( build != nullptr && build->hasRun() ) { + for ( const auto& crun : build->mRun ) { + if ( crun->name == mConfig.runName || mConfig.runName.empty() ) { + return build->replaceVars( *crun.get() ); + } + } + } + } + return {}; +} + void ProjectBuildManager::runConfig( StatusAppOutputController* saoc ) { if ( !isRunningApp() && !getBuilds().empty() ) { BoolScopedOp op( mRunning, true ); diff --git a/src/tools/ecode/projectbuild.hpp b/src/tools/ecode/projectbuild.hpp index 03269612e..4bf63c419 100644 --- a/src/tools/ecode/projectbuild.hpp +++ b/src/tools/ecode/projectbuild.hpp @@ -333,10 +333,12 @@ class ProjectBuildManager { bool hasRunConfig(); - bool hasBuildConfig(); + bool hasBuildConfig() const; void selectTab(); + std::optional getCurrentRunConfig(); + protected: std::string mProjectRoot; std::string mProjectFile;