From d73f1ea1ede256305f77a16ecdba58eb3eacb340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 5 Jan 2025 11:31:52 -0300 Subject: [PATCH] debugger fixes and improvements for macOS. --- bin/assets/plugins/debugger.json | 11 +- projects/macos/ee.files | 22 ++++ .../debugger/debuggerclientlistener.cpp | 25 ++-- .../ecode/plugins/debugger/debuggerplugin.cpp | 116 +++++++++++++++--- .../ecode/plugins/debugger/debuggerplugin.hpp | 2 + .../ecode/plugins/plugincontextprovider.hpp | 2 +- 6 files changed, 148 insertions(+), 30 deletions(-) diff --git a/bin/assets/plugins/debugger.json b/bin/assets/plugins/debugger.json index 7ae5d63d8..1433284a1 100644 --- a/bin/assets/plugins/debugger.json +++ b/bin/assets/plugins/debugger.json @@ -4,7 +4,8 @@ "name": "gdb", "url": "https://www.gnu.org/software/gdb", "run": { - "command": "gdb --interpreter=dap" + "command": "gdb", + "command_arguments": "--interpreter=dap" }, "languages": [ "cpp", "c", "d", "go", "objectivec", "fortran", "pascal", "rust" ], "configurations": [ @@ -23,10 +24,14 @@ ] }, { - "name": "lldb-dab", + "name": "lldb-dap", "url": "https://github.com/llvm/llvm-project/blob/main/lldb/tools/lldb-dap/README.md", "run": { - "command": "lldb-dap" + "command": "lldb-dap", + "command_fallback": "lldb-vscode" + }, + "find": { + "macos": "xcrun -f ${command}" }, "languages": [ "cpp", "c", "odin", "rust", "zig" ], "configurations": [ diff --git a/projects/macos/ee.files b/projects/macos/ee.files index a43bd8a44..7300b1f83 100644 --- a/projects/macos/ee.files +++ b/projects/macos/ee.files @@ -1563,11 +1563,32 @@ ../../src/tools/ecode/macos/macos.m ../../src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp ../../src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp +../../src/tools/ecode/plugins/debugger/bus.cpp +../../src/tools/ecode/plugins/debugger/bus.hpp +../../src/tools/ecode/plugins/debugger/busprocess.cpp +../../src/tools/ecode/plugins/debugger/busprocess.hpp +../../src/tools/ecode/plugins/debugger/bussocket.cpp +../../src/tools/ecode/plugins/debugger/bussocket.hpp +../../src/tools/ecode/plugins/debugger/config.cpp +../../src/tools/ecode/plugins/debugger/config.hpp +../../src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp +../../src/tools/ecode/plugins/debugger/dap/messages.hpp +../../src/tools/ecode/plugins/debugger/dap/protocol.cpp +../../src/tools/ecode/plugins/debugger/dap/protocol.hpp +../../src/tools/ecode/plugins/debugger/debuggerclient.cpp ../../src/tools/ecode/plugins/formatter/formatterplugin.cpp ../../src/tools/ecode/plugins/formatter/formatterplugin.hpp ../../src/tools/ecode/notificationcenter.cpp ../../src/tools/ecode/notificationcenter.hpp ../../src/tools/ecode/pathhelper.hpp +../../src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp +../../src/tools/ecode/plugins/debugger/debuggerclient.hpp +../../src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp +../../src/tools/ecode/plugins/debugger/debuggerclientlistener.hpp +../../src/tools/ecode/plugins/debugger/debuggerplugin.cpp +../../src/tools/ecode/plugins/debugger/debuggerplugin.hpp +../../src/tools/ecode/plugins/debugger/statusdebuggercontroller.cpp +../../src/tools/ecode/plugins/debugger/statusdebuggercontroller.hpp ../../src/tools/ecode/plugins/git/git.cpp ../../src/tools/ecode/plugins/git/git.hpp ../../src/tools/ecode/plugins/git/gitbranchmodel.cpp @@ -1590,6 +1611,7 @@ ../../src/tools/ecode/plugins/lsp/lspprotocol.hpp ../../src/tools/ecode/plugins/plugin.cpp ../../src/tools/ecode/plugins/plugin.hpp +../../src/tools/ecode/plugins/plugincontextprovider.hpp ../../src/tools/ecode/plugins/pluginmanager.cpp ../../src/tools/ecode/plugins/pluginmanager.hpp ../../src/tools/ecode/plugins/xmltools/xmltoolsplugin.cpp diff --git a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp index 9cc4347c8..666896c24 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp @@ -167,9 +167,12 @@ DebuggerClientListener::DebuggerClientListener( DebuggerClient* client, Debugger DebuggerClientListener::~DebuggerClientListener() { resetState(); - if ( !mPlugin->isShuttingDown() && getStatusDebuggerController() ) { - getStatusDebuggerController()->getUIThreads()->removeEventsOfType( Event::OnModelEvent ); - getStatusDebuggerController()->getUIStack()->removeEventsOfType( Event::OnModelEvent ); + auto sdc = getStatusDebuggerController(); + if ( !mPlugin->isShuttingDown() && sdc ) { + if ( sdc->getUIThreads() ) + sdc->getUIThreads()->removeEventsOfType( Event::OnModelEvent ); + if ( sdc->getUIStack() ) + sdc->getUIStack()->removeEventsOfType( Event::OnModelEvent ); } } @@ -183,17 +186,19 @@ void DebuggerClientListener::stateChanged( DebuggerClient::State state ) { ->getStatusAppOutputController() ->initNewOutput( {}, false ); + UISceneNode* sceneNode = mPlugin->getUISceneNode(); + if ( !mThreadsModel ) { mThreadsModel = std::make_shared( - std::vector{}, [this]( const auto& key, const auto& val ) { - return mPlugin->i18n( key, val ); + std::vector{}, [sceneNode]( const auto& key, const auto& val ) { + return sceneNode->i18n( key, val ); } ); } if ( !mStackModel ) { mStackModel = std::make_shared( - StackTraceInfo{}, [this]( const auto& key, const auto& val ) { - return mPlugin->i18n( key, val ); + StackTraceInfo{}, [sceneNode]( const auto& key, const auto& val ) { + return sceneNode->i18n( key, val ); } ); } @@ -243,8 +248,10 @@ void DebuggerClientListener::capabilitiesReceived( const Capabilities& /*capabil void DebuggerClientListener::resetState() { mStoppedData = {}; mCurrentScopePos = {}; - mThreadsModel->resetThreads(); - mStackModel->resetStack(); + if ( mThreadsModel ) + mThreadsModel->resetThreads(); + if ( mStackModel ) + mStackModel->resetStack(); mScope.clear(); } diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp index 744d7e7da..9fc4e6503 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 "../../notificationcenter.hpp" #include "../../projectbuild.hpp" #include "../../uistatusbar.hpp" #include "busprocess.hpp" @@ -177,9 +178,11 @@ void DebuggerPlugin::loadDAPConfig( const std::string& path, bool updateConfigFi DapTool dapTool; dapTool.name = dap.value( "name", "" ); dapTool.url = dap.value( "url", "" ); + if ( dap.contains( "run" ) ) { auto& run = dap["run"]; dapTool.run.command = run.value( "command", "" ); + dapTool.fallbackCommand = run.value( "command_fallback", "" ); if ( run.contains( "command_arguments" ) && run["command_arguments"].is_array() ) { auto& args = run["command_arguments"]; dapTool.run.args.reserve( args.size() ); @@ -189,6 +192,7 @@ void DebuggerPlugin::loadDAPConfig( const std::string& path, bool updateConfigFi } } } + if ( dap.contains( "configurations" ) ) { auto& configs = dap["configurations"]; dapTool.configurations.reserve( configs.size() ); @@ -212,6 +216,14 @@ void DebuggerPlugin::loadDAPConfig( const std::string& path, bool updateConfigFi } } + if ( dap.contains( "find" ) && dap["find"].is_object() ) { + auto find = dap["find"].items(); + for ( const auto& [key, val] : find ) { + if ( val.is_string() ) + dapTool.findBinary[key] = String::toLower( val.get() ); + } + } + mDaps.emplace_back( std::move( dapTool ) ); } } @@ -693,7 +705,33 @@ bool DebuggerPlugin::isSupportedByAnyDebugger( const std::string& language ) { return false; } +static std::string findCommand( const std::string& findBinary, const std::string& cmd ) { + std::string findCmd = findBinary; + String::replaceAll( findCmd, "${command}", cmd ); + Process p; + p.create( findCmd ); + std::string path; + p.readAllStdOut( path, Seconds( 5 ) ); + int retCode = -1; + p.join( &retCode ); + if ( retCode == 0 && !path.empty() ) { + String::trimInPlace( path, '\n' ); + return path; + } + return ""; +} + void DebuggerPlugin::runConfig( const std::string& debugger, const std::string& configuration ) { + auto runConfig = getPluginContext()->getProjectBuildManager()->getCurrentRunConfig(); + if ( !runConfig ) { + auto msg = + i18n( "no_run_config", + "You must first have a \"Run Target\" configured and selected. Go to \"Build " + "Settings\" and create a new build and run setting (build step is optional)." ); + mManager->getPluginContext()->getNotificationCenter()->addNotification( msg, Seconds( 5 ) ); + return; + } + auto debuggerIt = std::find_if( mDaps.begin(), mDaps.end(), [&debugger]( const DapTool& dap ) { return dap.name == debugger; } ); @@ -720,25 +758,69 @@ void DebuggerPlugin::runConfig( const std::string& debugger, const std::string& 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( mDebugger.get(), this ); - mDebugger->addListener( mListener.get() ); - mRunButton->setEnabled( false ); - mThreadPool->run( [this] { mDebugger->start(); }, - [this]( const Uint64& ) { - if ( !mDebugger || !mDebugger->started() ) { - exitDebugger(); - } else { - mRunButton->runOnMainThread( [this] { - mRunButton->setEnabled( true ); - mRunButton->setText( i18n( "cancel_run", "Cancel Run" ) ); - } ); - } - } ); + std::string findBinary; + auto findBinaryIt = debuggerIt->findBinary.find( String::toLower( Sys::getPlatform() ) ); + if ( findBinaryIt != debuggerIt->findBinary.end() ) + findBinary = findBinaryIt->second; + + std::string fallbackCommand = debuggerIt->fallbackCommand; + + mThreadPool->run( + [this, cmd = std::move( cmd ), protocolSettings = std::move( protocolSettings ), + findBinary = std::move( findBinary ), + fallbackCommand = std::move( fallbackCommand )]() mutable { + if ( !findBinary.empty() && Sys::which( cmd.command ).empty() ) { + auto foundCmd = findCommand( findBinary, cmd.command ); + if ( !foundCmd.empty() ) { + cmd.command = std::move( foundCmd ); + } else if ( !fallbackCommand.empty() ) { + foundCmd = findCommand( findBinary, fallbackCommand ); + if ( !foundCmd.empty() ) + cmd.command = std::move( foundCmd ); + } + } + + if ( !FileSystem::fileExists( cmd.command ) && Sys::which( cmd.command ).empty() ) { + auto args = Process::parseArgs( cmd.command ); + if ( args.size() <= 1 || + ( !FileSystem::fileExists( args[0] ) && Sys::which( args[0] ).empty() ) ) { + if ( fallbackCommand.empty() || ( !FileSystem::fileExists( fallbackCommand ) && + Sys::which( fallbackCommand ).empty() ) ) { + auto msg = String::format( + i18n( "debugger_binary_not_found", + "Debugger binary not found. Binary \"%s\" must be installed." ) + .toUtf8(), + cmd.command ); + + mManager->getPluginContext()->getNotificationCenter()->addNotification( + msg ); + return; + } else { + cmd.command = std::move( fallbackCommand ); + } + } + } + + auto bus = std::make_unique( cmd ); + + mDebugger = std::make_unique( protocolSettings, std::move( bus ) ); + mListener = std::make_unique( mDebugger.get(), this ); + mDebugger->addListener( mListener.get() ); + + mDebugger->start(); + }, + [this]( const Uint64& ) { + if ( !mDebugger || !mDebugger->started() ) { + exitDebugger(); + } else { + mRunButton->runOnMainThread( [this] { + mRunButton->setEnabled( true ); + mRunButton->setText( i18n( "cancel_run", "Cancel Run" ) ); + } ); + } + } ); } void DebuggerPlugin::exitDebugger() { diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp index 6fe7bb89d..46cdd3f88 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp @@ -26,6 +26,8 @@ struct DapTool { std::vector languagesSupported; DapRunConfig run; std::vector configurations; + std::unordered_map findBinary; + std::string fallbackCommand; }; class DebuggerPlugin : public PluginBase { diff --git a/src/tools/ecode/plugins/plugincontextprovider.hpp b/src/tools/ecode/plugins/plugincontextprovider.hpp index c52a5c996..ca0f08835 100644 --- a/src/tools/ecode/plugins/plugincontextprovider.hpp +++ b/src/tools/ecode/plugins/plugincontextprovider.hpp @@ -48,7 +48,7 @@ class StatusAppOutputController; class ProjectBuildManager; class NotificationCenter; class ProjectDirectoryTree; -class TerminalConfig; +struct TerminalConfig; class PluginContextProvider { public: