From 192bfff688f78729376a5063ae91e387fd04534b Mon Sep 17 00:00:00 2001 From: Doug Schaefer Date: Mon, 25 Jan 2016 15:00:32 -0500 Subject: [PATCH] Bug 486509 - Add support for debugging local Qt apps. We reuse GDBLaunch but need to override some settings that are normally in the launch configuration. These things are calculated at launch time. Note there is also an added dependency to the launch bar core to make GDBLaunch a targeted launch so we can set the target properly. At some point we'll move this launch target stuff lower down, maybe to the debug platform. Change-Id: Ibbf6b794a9ecf25b79d46093cc624ea69dc04641 --- .../internal/CBuildConfigurationManager.java | 10 +- .../META-INF/MANIFEST.MF | 3 +- .../cdt/dsf/gdb/launching/GdbLaunch.java | 1062 ++++++++++---- .../dsf/gdb/launching/GdbLaunchDelegate.java | 7 +- .../cdt/dsf/gdb/launching/LaunchUtils.java | 5 + .../cdt/dsf/gdb/service/GDBBackend.java | 1298 ++++++++--------- .../META-INF/MANIFEST.MF | 4 +- qt/org.eclipse.cdt.qt.core/plugin.xml | 37 +- .../build/QtBuildConfigurationFactory.java | 2 +- .../QtLocalDebugLaunchConfigDelegate.java | 117 ++ .../QtLocalRunLaunchConfigDelegate.java | 78 +- .../cdt/qt/core/QtBuildConfiguration.java | 46 + qt/org.eclipse.cdt.qt.ui/plugin.xml | 7 + 13 files changed, 1615 insertions(+), 1061 deletions(-) create mode 100644 qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalDebugLaunchConfigDelegate.java diff --git a/build/org.eclipse.cdt.build.core/src/org/eclipse/cdt/build/core/internal/CBuildConfigurationManager.java b/build/org.eclipse.cdt.build.core/src/org/eclipse/cdt/build/core/internal/CBuildConfigurationManager.java index 88f2e2678cf..88564bb4cd1 100644 --- a/build/org.eclipse.cdt.build.core/src/org/eclipse/cdt/build/core/internal/CBuildConfigurationManager.java +++ b/build/org.eclipse.cdt.build.core/src/org/eclipse/cdt/build/core/internal/CBuildConfigurationManager.java @@ -18,6 +18,7 @@ import java.util.Map; import org.eclipse.cdt.build.core.CBuildConfiguration; import org.eclipse.cdt.build.core.IBuildConfigurationManager; +import org.eclipse.cdt.core.CProjectNature; import org.eclipse.core.resources.IBuildConfiguration; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; @@ -42,7 +43,7 @@ public class CBuildConfigurationManager // TODO CBuildConfiguration config = null; - //configMap.put(config.getBuildConfiguration(), config); + // configMap.put(config.getBuildConfiguration(), config); return config; } @@ -67,6 +68,13 @@ public class CBuildConfigurationManager if (event.getType() == IResourceChangeEvent.PRE_CLOSE || event.getType() == IResourceChangeEvent.PRE_DELETE) { if (event.getResource().getType() == IResource.PROJECT) { IProject project = event.getResource().getProject(); + try { + if (!project.hasNature(CProjectNature.C_NATURE_ID)) + return; + } catch (CoreException e) { + Activator.log(e.getStatus()); + return; + } // Clean up the configMap try { diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF index ca0ab35ed8d..d224f985e4c 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF @@ -14,7 +14,8 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.core.variables, org.eclipse.cdt.launch;bundle-version="6.1.0", org.eclipse.cdt.gdb;bundle-version="7.0.0", - org.eclipse.core.resources + org.eclipse.core.resources, + org.eclipse.launchbar.core;bundle-version="2.0.0";visibility:=reexport Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Export-Package: org.eclipse.cdt.dsf.gdb, diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java index 6746a98c11f..c89ec00a000 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java @@ -13,12 +13,32 @@ *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.launching; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.RejectedExecutionException; +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.cdtvariables.CdtVariableException; +import org.eclipse.cdt.core.cdtvariables.ICdtVariable; +import org.eclipse.cdt.core.envvar.IEnvironmentVariable; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.parser.util.StringUtil; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICProjectDescription; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.debug.internal.core.CRequest; import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor; @@ -36,7 +56,9 @@ import org.eclipse.cdt.dsf.debug.internal.provisional.model.IMemoryBlockRetrieva import org.eclipse.cdt.dsf.debug.model.DsfLaunch; import org.eclipse.cdt.dsf.debug.service.IDsfDebugServicesFactory; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants; +import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.internal.memory.GdbMemoryBlockRetrievalManager; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; @@ -44,15 +66,28 @@ import org.eclipse.cdt.dsf.mi.service.command.AbstractCLIProcess; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.utils.CommandLineUtil; +import org.eclipse.cdt.utils.spawner.ProcessFactory; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.variables.VariablesPlugin; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.IStatusHandler; import org.eclipse.debug.core.commands.IDebugCommandRequest; import org.eclipse.debug.core.commands.IDisconnectHandler; @@ -60,147 +95,160 @@ import org.eclipse.debug.core.commands.ITerminateHandler; import org.eclipse.debug.core.model.IDisconnect; import org.eclipse.debug.core.model.ISourceLocator; import org.eclipse.debug.core.model.ITerminate; +import org.eclipse.launchbar.core.target.ILaunchTarget; +import org.eclipse.launchbar.core.target.launch.ITargetedLaunch; /** * The only object in the model that implements the traditional interfaces. */ @ThreadSafe -public class GdbLaunch extends DsfLaunch - implements ITerminate, IDisconnect, ITracedLaunch -{ - private DefaultDsfExecutor fExecutor; - private DsfSession fSession; - private DsfServicesTracker fTracker; - private boolean fInitialized = false; - private boolean fShutDown = false; - private IMemoryBlockRetrievalManager fMemRetrievalManager; - private IDsfDebugServicesFactory fServiceFactory; - - public GdbLaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) { - super(launchConfiguration, mode, locator); +public class GdbLaunch extends DsfLaunch implements ITerminate, IDisconnect, ITracedLaunch, ITargetedLaunch { + private DefaultDsfExecutor fExecutor; + private DsfSession fSession; + private DsfServicesTracker fTracker; + private boolean fInitialized = false; + private boolean fShutDown = false; + private IMemoryBlockRetrievalManager fMemRetrievalManager; + private IDsfDebugServicesFactory fServiceFactory; + private ILaunchTarget fLaunchTarget; - // Create the dispatch queue to be used by debugger control and services - // that belong to this launch - final DefaultDsfExecutor dsfExecutor = new DefaultDsfExecutor(GdbLaunchDelegate.GDB_DEBUG_MODEL_ID); - dsfExecutor.prestartCoreThread(); - fExecutor = dsfExecutor; - fSession = DsfSession.startSession(fExecutor, GdbLaunchDelegate.GDB_DEBUG_MODEL_ID); - } + public GdbLaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) { + super(launchConfiguration, mode, locator); - public DsfExecutor getDsfExecutor() { return fExecutor; } - public IDsfDebugServicesFactory getServiceFactory() { return fServiceFactory; } - - public void initialize() - { - /* - * Registering the launch as an adapter. This ensures that this launch - * will be associated with all DMContexts from this session. - * We do this here because we want to have access to the launch even - * if we run headless, but when we run headless, GdbAdapterFactory is - * not initialized. - */ - fSession.registerModelAdapter(ILaunch.class, this); + // Create the dispatch queue to be used by debugger control and services + // that belong to this launch + final DefaultDsfExecutor dsfExecutor = new DefaultDsfExecutor(GdbLaunchDelegate.GDB_DEBUG_MODEL_ID); + dsfExecutor.prestartCoreThread(); + fExecutor = dsfExecutor; + fSession = DsfSession.startSession(fExecutor, GdbLaunchDelegate.GDB_DEBUG_MODEL_ID); + } - Runnable initRunnable = new DsfRunnable() { - @Override - public void run() { - fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSession.getId()); - fSession.addServiceEventListener(GdbLaunch.this, null); - - fInitialized = true; - fireChanged(); - } - }; - - // Invoke the execution code and block waiting for the result. - try { - fExecutor.submit(initRunnable).get(); - } catch (InterruptedException e) { - new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Error initializing launch", e); //$NON-NLS-1$ - } catch (ExecutionException e) { - new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Error initializing launch", e); //$NON-NLS-1$ - } - } + public DsfExecutor getDsfExecutor() { + return fExecutor; + } - public void initializeControl() - throws CoreException - { - // Create a memory retrieval manager and register it with the session - // To maintain a mapping of memory contexts to the corresponding memory retrieval in this session - try { - fExecutor.submit( new Callable() { - @Override - public Object call() throws CoreException { - fMemRetrievalManager = new GdbMemoryBlockRetrievalManager(GdbLaunchDelegate.GDB_DEBUG_MODEL_ID, getLaunchConfiguration(), fSession); - fSession.registerModelAdapter(IMemoryBlockRetrievalManager.class, fMemRetrievalManager); - fSession.addServiceEventListener(fMemRetrievalManager, null); - return null; - } - }).get(); - } catch (InterruptedException e) { - throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$ - } catch (ExecutionException e) { - throw (CoreException)e.getCause(); - } catch (RejectedExecutionException e) { - throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$ - } - } + public IDsfDebugServicesFactory getServiceFactory() { + return fServiceFactory; + } - public DsfSession getSession() { return fSession; } - - @ThreadSafeAndProhibitedFromDsfExecutor("getDsfExecutor()") - public void addCLIProcess(String label) throws CoreException { - try { - // Add the CLI process object to the launch. - AbstractCLIProcess cliProc = - getDsfExecutor().submit( new Callable() { - @Override - public AbstractCLIProcess call() throws CoreException { - IGDBControl gdb = fTracker.getService(IGDBControl.class); - if (gdb != null) { - return gdb.getCLIProcess(); - } - return null; - } - }).get(); + public void initialize() { + /* + * Registering the launch as an adapter. This ensures that this launch + * will be associated with all DMContexts from this session. We do this + * here because we want to have access to the launch even if we run + * headless, but when we run headless, GdbAdapterFactory is not + * initialized. + */ + fSession.registerModelAdapter(ILaunch.class, this); - // Need to go through DebugPlugin.newProcess so that we can use + Runnable initRunnable = new DsfRunnable() { + @Override + public void run() { + fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSession.getId()); + fSession.addServiceEventListener(GdbLaunch.this, null); + + fInitialized = true; + fireChanged(); + } + }; + + // Invoke the execution code and block waiting for the result. + try { + fExecutor.submit(initRunnable).get(); + } catch (InterruptedException e) { + new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, + "Error initializing launch", e); //$NON-NLS-1$ + } catch (ExecutionException e) { + new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, + "Error initializing launch", e); //$NON-NLS-1$ + } + } + + public void initializeControl() throws CoreException { + // Create a memory retrieval manager and register it with the session + // To maintain a mapping of memory contexts to the corresponding memory + // retrieval in this session + try { + fExecutor.submit(new Callable() { + @Override + public Object call() throws CoreException { + fMemRetrievalManager = new GdbMemoryBlockRetrievalManager(GdbLaunchDelegate.GDB_DEBUG_MODEL_ID, + getLaunchConfiguration(), fSession); + fSession.registerModelAdapter(IMemoryBlockRetrievalManager.class, fMemRetrievalManager); + fSession.addServiceEventListener(fMemRetrievalManager, null); + return null; + } + }).get(); + } catch (InterruptedException e) { + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, + "Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$ + } catch (ExecutionException e) { + throw (CoreException) e.getCause(); + } catch (RejectedExecutionException e) { + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, + "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$ + } + } + + public DsfSession getSession() { + return fSession; + } + + @ThreadSafeAndProhibitedFromDsfExecutor("getDsfExecutor()") + public void addCLIProcess(String label) throws CoreException { + try { + // Add the CLI process object to the launch. + AbstractCLIProcess cliProc = getDsfExecutor().submit(new Callable() { + @Override + public AbstractCLIProcess call() throws CoreException { + IGDBControl gdb = fTracker.getService(IGDBControl.class); + if (gdb != null) { + return gdb.getCLIProcess(); + } + return null; + } + }).get(); + + // Need to go through DebugPlugin.newProcess so that we can use // the overrideable process factory to allow others to override. // First set attribute to specify we want to create the gdb process. // Bug 210366 Map attributes = new HashMap(); - attributes.put(IGdbDebugConstants.PROCESS_TYPE_CREATION_ATTR, - IGdbDebugConstants.GDB_PROCESS_CREATION_VALUE); - DebugPlugin.newProcess(this, cliProc, label, attributes); - } catch (InterruptedException e) { - throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$ - } catch (ExecutionException e) { - throw (CoreException)e.getCause(); - } catch (RejectedExecutionException e) { - throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$ - } - } - - public void setServiceFactory(IDsfDebugServicesFactory factory) { - fServiceFactory = factory; - } - - /////////////////////////////////////////////////////////////////////////// - // IServiceEventListener - @DsfServiceEventHandler public void eventDispatched(ICommandControlShutdownDMEvent event) { - shutdownSession(new ImmediateRequestMonitor()); - } + attributes.put(IGdbDebugConstants.PROCESS_TYPE_CREATION_ATTR, + IGdbDebugConstants.GDB_PROCESS_CREATION_VALUE); + DebugPlugin.newProcess(this, cliProc, label, attributes); + } catch (InterruptedException e) { + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, + "Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$ + } catch (ExecutionException e) { + throw (CoreException) e.getCause(); + } catch (RejectedExecutionException e) { + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, + "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$ + } + } + + public void setServiceFactory(IDsfDebugServicesFactory factory) { + fServiceFactory = factory; + } + + /////////////////////////////////////////////////////////////////////////// + // IServiceEventListener + @DsfServiceEventHandler + public void eventDispatched(ICommandControlShutdownDMEvent event) { + shutdownSession(new ImmediateRequestMonitor()); + } + + /////////////////////////////////////////////////////////////////////////// + // ITerminate + + static class LaunchCommandRequest extends CRequest implements IDebugCommandRequest { + private Object[] elements; + + public LaunchCommandRequest(Object[] objects) { + elements = objects; + } - /////////////////////////////////////////////////////////////////////////// - // ITerminate - - static class LaunchCommandRequest extends CRequest implements IDebugCommandRequest { - private Object[] elements; - - public LaunchCommandRequest(Object[] objects) { - elements = objects; - } - @Override public Object[] getElements() { return elements; @@ -222,165 +270,637 @@ public class GdbLaunch extends DsfLaunch } } } - } - - @Override - public boolean canTerminate() { - return fInitialized && super.canTerminate(); - } - - /* (non-Javadoc) - * @see org.eclipse.debug.core.Launch#terminate() - */ - @Override - public void terminate() throws DebugException { - // Execute asynchronously to avoid potential deadlocks - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=434645 + } - ITerminateHandler handler = getAdapter(ITerminateHandler.class); - if (handler == null) { - super.terminate(); - return; - } + @Override + public boolean canTerminate() { + return fInitialized && super.canTerminate(); + } - LaunchCommandRequest req = new LaunchCommandRequest(new Object[] {this}); - handler.execute(req); - } - - // ITerminate - /////////////////////////////////////////////////////////////////////////// + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.core.Launch#terminate() + */ + @Override + public void terminate() throws DebugException { + // Execute asynchronously to avoid potential deadlocks + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=434645 - /////////////////////////////////////////////////////////////////////////// - // IDisconnect - @Override - public boolean canDisconnect() { - return canTerminate(); - } + ITerminateHandler handler = getAdapter(ITerminateHandler.class); + if (handler == null) { + super.terminate(); + return; + } - @Override - public boolean isDisconnected() { - return isTerminated(); - } + LaunchCommandRequest req = new LaunchCommandRequest(new Object[] { this }); + handler.execute(req); + } - @Override - public void disconnect() throws DebugException { - IDisconnectHandler handler = getAdapter(IDisconnectHandler.class); - if (handler == null) { - super.disconnect(); - return; - } + // ITerminate + /////////////////////////////////////////////////////////////////////////// - LaunchCommandRequest req = new LaunchCommandRequest(new Object[] {this}); - handler.execute(req); - } + /////////////////////////////////////////////////////////////////////////// + // IDisconnect + @Override + public boolean canDisconnect() { + return canTerminate(); + } - // IDisconnect - /////////////////////////////////////////////////////////////////////////// - - /** - * Terminates the gdb session, shuts down the services, the session and - * the executor associated with this launch. - *

- * Note: The argument request monitor to this method should NOT use the - * executor that belongs to this launch. By the time the shutdown is - * complete, this executor will not be dispatching anymore and the - * request monitor will never be invoked. Instead callers should use - * the {@link ImmediateExecutor}. - *

- * @param rm The request monitor invoked when the shutdown is complete. - */ - @ConfinedToDsfExecutor("getSession().getExecutor()") - public void shutdownSession(final RequestMonitor rm) { - if (fShutDown) { - rm.done(); - return; - } - fShutDown = true; - - final Sequence shutdownSeq = new ShutdownSequence( - getDsfExecutor(), fSession.getId(), - new RequestMonitor(fSession.getExecutor(), rm) { - @Override - public void handleCompleted() { - if (fMemRetrievalManager != null) { - fSession.removeServiceEventListener(fMemRetrievalManager); - fMemRetrievalManager.dispose(); - } + @Override + public boolean isDisconnected() { + return isTerminated(); + } - fSession.removeServiceEventListener(GdbLaunch.this); - if (!isSuccess()) { - GdbPlugin.getDefault().getLog().log(new MultiStatus( - GdbPlugin.PLUGIN_ID, -1, new IStatus[]{getStatus()}, "Session shutdown failed", null)); //$NON-NLS-1$ - } - // Last order of business, shutdown the dispatch queue. - fTracker.dispose(); - fTracker = null; - DsfSession.endSession(fSession); + @Override + public void disconnect() throws DebugException { + IDisconnectHandler handler = getAdapter(IDisconnectHandler.class); + if (handler == null) { + super.disconnect(); + return; + } - // 'fireTerminate()' removes this launch from the list of 'DebugEvent' - // listeners. The launch may not be terminated at this point: the inferior - // and gdb processes are monitored in separate threads. This will prevent - // updating of some of the Debug view actions. - // 'DebugEvent.TERMINATE' will be fired when each of the corresponding processes - // exits and handled by 'handleDebugEvents()' method. - if (isTerminated()) { - fireTerminate(); - } - - rm.setStatus(getStatus()); - rm.done(); - } - }); - - final Step[] steps = new Step[] { - new Step() { - @Override - public void execute(RequestMonitor rm) { - IGDBControl control = fTracker.getService(IGDBControl.class); - if (control == null) { - rm.done(); - return; - } - control.terminate(rm); - } - }, - - new Step() { - @Override - public void execute(RequestMonitor rm) { - fExecutor.execute(shutdownSeq); - } - } - }; + LaunchCommandRequest req = new LaunchCommandRequest(new Object[] { this }); + handler.execute(req); + } - fExecutor.execute(new Sequence(fExecutor) { + // IDisconnect + /////////////////////////////////////////////////////////////////////////// + + /** + * Terminates the gdb session, shuts down the services, the session and the + * executor associated with this launch. + *

+ * Note: The argument request monitor to this method should NOT use the + * executor that belongs to this launch. By the time the shutdown is + * complete, this executor will not be dispatching anymore and the request + * monitor will never be invoked. Instead callers should use the + * {@link ImmediateExecutor}. + *

+ * + * @param rm + * The request monitor invoked when the shutdown is complete. + */ + @ConfinedToDsfExecutor("getSession().getExecutor()") + public void shutdownSession(final RequestMonitor rm) { + if (fShutDown) { + rm.done(); + return; + } + fShutDown = true; + + final Sequence shutdownSeq = new ShutdownSequence(getDsfExecutor(), fSession.getId(), + new RequestMonitor(fSession.getExecutor(), rm) { + @Override + public void handleCompleted() { + if (fMemRetrievalManager != null) { + fSession.removeServiceEventListener(fMemRetrievalManager); + fMemRetrievalManager.dispose(); + } + + fSession.removeServiceEventListener(GdbLaunch.this); + if (!isSuccess()) { + GdbPlugin.getDefault().getLog().log(new MultiStatus(GdbPlugin.PLUGIN_ID, -1, + new IStatus[] { getStatus() }, "Session shutdown failed", null)); //$NON-NLS-1$ + } + // Last order of business, shutdown the dispatch queue. + fTracker.dispose(); + fTracker = null; + DsfSession.endSession(fSession); + + // 'fireTerminate()' removes this launch from the list + // of 'DebugEvent' + // listeners. The launch may not be terminated at this + // point: the inferior + // and gdb processes are monitored in separate threads. + // This will prevent + // updating of some of the Debug view actions. + // 'DebugEvent.TERMINATE' will be fired when each of the + // corresponding processes + // exits and handled by 'handleDebugEvents()' method. + if (isTerminated()) { + fireTerminate(); + } + + rm.setStatus(getStatus()); + rm.done(); + } + }); + + final Step[] steps = new Step[] { new Step() { + @Override + public void execute(RequestMonitor rm) { + IGDBControl control = fTracker.getService(IGDBControl.class); + if (control == null) { + rm.done(); + return; + } + control.terminate(rm); + } + }, + + new Step() { + @Override + public void execute(RequestMonitor rm) { + fExecutor.execute(shutdownSeq); + } + } }; + + fExecutor.execute(new Sequence(fExecutor) { + + @Override + public Step[] getSteps() { + return steps; + } + }); + } - @Override - public Step[] getSteps() { - return steps; - } - }); - } - @Override public T getAdapter(Class adapter) { - if (!adapter.equals(ITerminateHandler.class)) { - // Must force adapters to be loaded. - // Except in the case of terminate. Terminate can be used - // when running headless (no UI) and therefore we should not - // force the loading of UI plugins in this case. - // This can happen when running JUnit tests for example. - Platform.getAdapterManager().loadAdapter(this, adapter.getName()); - } - return super.getAdapter(adapter); - } - - @Override + if (!adapter.equals(ITerminateHandler.class)) { + // Must force adapters to be loaded. + // Except in the case of terminate. Terminate can be used + // when running headless (no UI) and therefore we should not + // force the loading of UI plugins in this case. + // This can happen when running JUnit tests for example. + Platform.getAdapterManager().loadAdapter(this, adapter.getName()); + } + return super.getAdapter(adapter); + } + + @Override public void launchRemoved(ILaunch launch) { if (this.equals(launch)) { - fExecutor.shutdown(); - fExecutor = null; - } - super.launchRemoved(launch); - } + fExecutor.shutdown(); + fExecutor = null; + } + super.launchRemoved(launch); + } + + /** + * Returns the path to gdb. + * + * @since 5.0 + */ + public IPath getGDBPath() { + String defaultGdbCommand = Platform.getPreferencesService().getString(GdbPlugin.PLUGIN_ID, + IGdbDebugPreferenceConstants.PREF_DEFAULT_GDB_COMMAND, + IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_NAME_DEFAULT, null); + + IPath retVal = new Path(defaultGdbCommand); + try { + String gdb = getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME); + if (gdb == null) { + gdb = getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, + defaultGdbCommand); + } + gdb = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(gdb, false); + retVal = new Path(gdb); + } catch (CoreException e) { + } + return retVal; + } + + /** + * Set the path to gdb + * + * @param path + * the path to gdb + * @since 5.0 + */ + public void setGDBPath(String path) { + setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, path); + } + + /** + * This method actually launches 'gdb --version' to determine the version of + * the GDB that is being used. This method should ideally be called only + * once per session and the resulting version string stored for future uses. + * + * A timeout is scheduled which will kill the process if it takes too long. + * + * @since 5.0 + */ + public String getGDBVersion() throws CoreException { + String cmd = getGDBPath().toOSString() + " --version"; //$NON-NLS-1$ + + // Parse cmd to properly handle spaces and such things (bug 458499) + String[] args = CommandLineUtil.argumentsToArray(cmd); + + Process process = null; + Job timeoutJob = null; + try { + process = ProcessFactory.getFactory().exec(args, getLaunchEnvironment()); + + // Start a timeout job to make sure we don't get stuck waiting for + // an answer from a gdb that is hanging + // Bug 376203 + final Process finalProc = process; + timeoutJob = new Job("GDB version timeout job") { //$NON-NLS-1$ + { + setSystem(true); + } + + @Override + protected IStatus run(IProgressMonitor arg) { + // Took too long. Kill the gdb process and + // let things clean up. + finalProc.destroy(); + return Status.OK_STATUS; + } + }; + timeoutJob.schedule(10000); + + String streamOutput = readStream(process.getInputStream()); + + String gdbVersion = LaunchUtils.getGDBVersionFromText(streamOutput); + if (gdbVersion == null || gdbVersion.isEmpty()) { + Exception detailedException = null; + if (!streamOutput.isEmpty()) { + // We got some output but couldn't parse it. Make that + // output visible to the user in the error dialog. + detailedException = new Exception("Unexpected output format: \n\n" + streamOutput); //$NON-NLS-1$ + } else { + // We got no output. Check if we got something on the error + // stream. + streamOutput = readStream(process.getErrorStream()); + if (!streamOutput.isEmpty()) { + detailedException = new Exception(streamOutput); + } + } + + throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, + "Could not determine GDB version using command: " + StringUtil.join(args, " "), //$NON-NLS-1$ //$NON-NLS-2$ + detailedException)); + } + return gdbVersion; + } catch (IOException e) { + throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, + "Error with command: " + StringUtil.join(args, " "), e));//$NON-NLS-1$ //$NON-NLS-2$ + } finally { + // If we get here we are obviously not stuck reading the stream so + // we can cancel the timeout job. + // Note that it may already have executed, but that is not a + // problem. + if (timeoutJob != null) { + timeoutJob.cancel(); + } + + if (process != null) { + process.destroy(); + } + } + } + + /** + * Read from the specified stream and return what was read. + * + * @param stream + * The input stream to be used to read the data. This method will + * close the stream. + * @return The data read from the stream + * @throws IOException + * If an IOException happens when reading the stream + */ + private static String readStream(InputStream stream) throws IOException { + StringBuilder cmdOutput = new StringBuilder(200); + try { + Reader r = new InputStreamReader(stream); + BufferedReader reader = new BufferedReader(r); + + String line; + while ((line = reader.readLine()) != null) { + cmdOutput.append(line); + cmdOutput.append('\n'); + } + return cmdOutput.toString(); + } finally { + // Cleanup to avoid leaking pipes + // Bug 345164 + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + } + } + } + } + + /** + * Gets the CDT environment from the CDT project's configuration referenced + * by the launch + * + * @since 5.0 + */ + public String[] getLaunchEnvironment() throws CoreException { + // Get the project + String projectName = getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, + (String) null); + IProject project = null; + if (projectName == null) { + IResource[] resources = getLaunchConfiguration().getMappedResources(); + if (resources != null && resources.length > 0 && resources[0] instanceof IProject) { + project = (IProject) resources[0]; + } + } else { + projectName = projectName.trim(); + if (projectName.length() == 0) { + return null; + } + project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + } + + if (project == null || !project.isAccessible()) { + // No project + return null; + } + + HashMap envMap = new HashMap(); + ICProjectDescription projDesc = CoreModel.getDefault().getProjectDescription(project, false); + if (projDesc != null) { + String buildConfigID = getLaunchConfiguration() + .getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_BUILD_CONFIG_ID, ""); //$NON-NLS-1$ + ICConfigurationDescription cfg = null; + if (buildConfigID.length() != 0) { + cfg = projDesc.getConfigurationById(buildConfigID); + } + + // if configuration is null fall-back to active + if (cfg == null) { + cfg = projDesc.getActiveConfiguration(); + } + + // Environment variables and inherited vars + IEnvironmentVariable[] vars = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariables(cfg, true); + for (IEnvironmentVariable var : vars) { + envMap.put(var.getName(), var.getValue()); + } + + // Add variables from build info + ICdtVariable[] buildVars = CCorePlugin.getDefault().getCdtVariableManager().getVariables(cfg); + for (ICdtVariable var : buildVars) { + try { + // The project_classpath variable contributed by JDT is + // useless + // for running C/C++ + // binaries, but it can be lethal if it has a very large + // value + // that exceeds shell + // limit. See + // http://bugs.eclipse.org/bugs/show_bug.cgi?id=408522 + if (!"project_classpath".equals(var.getName())) {//$NON-NLS-1$ + envMap.put(var.getName(), var.getStringValue()); + } + } catch (CdtVariableException e) { + // Some Eclipse dynamic variables can't be resolved + // dynamically... we don't care. + } + } + } + + // Turn it into an envp format + List strings = new ArrayList(envMap.size()); + for (Entry entry : envMap.entrySet()) { + StringBuffer buffer = new StringBuffer(entry.getKey()); + buffer.append('=').append(entry.getValue()); + strings.add(buffer.toString()); + } + + return strings.toArray(new String[strings.size()]); + } + + /** + * Get the location of the gdbinit file. + * + * @return gdbinit file location + * @throws CoreException + * @since 5.0 + */ + public String getGDBInitFile() throws CoreException { + String defaultGdbInit = Platform.getPreferencesService().getString(GdbPlugin.PLUGIN_ID, + IGdbDebugPreferenceConstants.PREF_DEFAULT_GDB_INIT, + IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT, null); + + return getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, defaultGdbInit); + } + + /** + * Get the working directory. + * + * @return the working directory + * @throws CoreException + * @since 5.0 + */ + public IPath getGDBWorkingDirectory() throws CoreException { + // First try to use the user-specified working directory for the + // debugged program. + // This is fine only with local debug. + // For remote debug, the working dir of the debugged program will be + // on remote device + // and hence not applicable. In such case we may just use debugged + // program path on host + // as the working dir for GDB. + // However, we cannot find a standard/common way to distinguish + // remote debug from local + // debug. For instance, a local debug may also use gdbserver+gdb. So + // it's up to each + // debugger implementation to make the distinction. + // + IPath path = null; + String location = getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, + (String) null); + + if (location != null) { + String expandedLocation = VariablesPlugin.getDefault().getStringVariableManager() + .performStringSubstitution(location); + if (!expandedLocation.isEmpty()) { + path = new Path(expandedLocation); + } + } + + if (path != null) { + // Some validity check. Should have been done by UI code. + if (path.isAbsolute()) { + File dir = new File(path.toPortableString()); + if (!dir.isDirectory()) + path = null; + } else { + IResource res = ResourcesPlugin.getWorkspace().getRoot().findMember(path); + if (res instanceof IContainer && res.exists()) { + path = res.getLocation(); + } else + // Relative but not found in workspace. + path = null; + } + } + + if (path == null) { + // default working dir is the project if this config has a + // project + ICProject cp = LaunchUtils.getCProject(getLaunchConfiguration()); + if (cp != null) { + IProject p = cp.getProject(); + path = p.getLocation(); + } else { + // no meaningful value found. Just return null. + } + } + + return path; + } + + /** + * Get the program arguments + * + * @return program arguments + * @throws CoreException + * @since 5.0 + */ + public String getProgramArguments() throws CoreException { + String programArguments = getLaunchConfiguration() + .getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, (String) null); + + if (programArguments != null) { + programArguments = VariablesPlugin.getDefault().getStringVariableManager() + .performStringSubstitution(programArguments); + } + + return programArguments; + } + + /** + * Return the program path + * + * @return the program path + * @since 5.0 + */ + public String getProgramPath() throws CoreException { + String programPath = getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME); + if (programPath == null) { + programPath = getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, + (String) null); + } + return programPath; + } + + /** + * Sets the program path + * + * @param programPath + * the program path + * @throws CoreException + * @since 5.0 + */ + public void setProgramPath(String programPath) throws CoreException { + setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, programPath); + } + + /** + * Return shared library paths + * + * @return shared library paths + * @throws CoreException + * @since 5.0 + */ + public List getSharedLibraryPaths() throws CoreException { + return getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH, + new ArrayList(0)); + } + + /** + * return the environment + * + * @return the environment + * @throws CoreException + * @since 5.0 + */ + public Properties getEnvironmentVariables() throws CoreException { + Properties envVariables = new Properties(); + + // if the attribute ATTR_APPEND_ENVIRONMENT_VARIABLES is set, + // the LaunchManager will return both the new variables and the + // existing ones. + // That would force us to delete all the variables in GDB, and then + // re-create then all + // that is not very efficient. So, let's fool the LaunchManager into + // returning just the + // list of new variables. + + boolean append = getLaunchConfiguration().getAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true); + + String[] properties; + if (append) { + ILaunchConfigurationWorkingCopy wc = getLaunchConfiguration().copy(""); //$NON-NLS-1$ + // Don't save this change, it is just temporary, and in just a + // copy of our launchConfig. + wc.setAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, false); + properties = DebugPlugin.getDefault().getLaunchManager().getEnvironment(wc); + } else { + // We're getting rid of the environment anyway, so this call + // will only yield the new variables. + properties = DebugPlugin.getDefault().getLaunchManager().getEnvironment(getLaunchConfiguration()); + } + + if (properties == null) { + properties = new String[0]; + } + + for (String property : properties) { + int idx = property.indexOf('='); + if (idx != -1) { + String key = property.substring(0, idx); + String value = property.substring(idx + 1); + envVariables.setProperty(key, value); + } else { + envVariables.setProperty(property, ""); //$NON-NLS-1$ + } + } + return envVariables; + } + + /** + * Get whether to clear the environment before applying the variables + * + * @return clear + * @throws CoreException + * @since 5.0 + */ + public boolean getClearEnvironment() throws CoreException { + return !getLaunchConfiguration().getAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true); + } + + /** + * Get whether to update thread list on suspend + * + * @return whether + * @throws CoreException + * @since 5.0 + */ + public boolean getUpdateThreadListOnSuspend() throws CoreException { + return getLaunchConfiguration().getAttribute( + IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND, + IGDBLaunchConfigurationConstants.DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND_DEFAULT); + } + + /** + * Set the launch target + * + * @param launchTarget + * the launch target + * @since 5.0 + */ + public void setLaunchTarget(ILaunchTarget launchTarget) { + this.fLaunchTarget = launchTarget; + } + + /** + * Return the launch target + * + * @since 5.0 + */ + @Override + public ILaunchTarget getLaunchTarget() { + return fLaunchTarget; + } + } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunchDelegate.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunchDelegate.java index b488dfdb6d4..499dc598217 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunchDelegate.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunchDelegate.java @@ -296,11 +296,12 @@ public class GdbLaunchDelegate extends AbstractCLaunchDelegate2 } /** - * Returns the GDB version. - * Subclass can override for special need. - * + * Returns the GDB version. Subclass can override for special need. + * * @since 2.0 + * @deprecated Replaced by GdbLaunch.getGDBVersion() which can also be overridden */ + @Deprecated protected String getGDBVersion(ILaunchConfiguration config) throws CoreException { return LaunchUtils.getGDBVersion(config); } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchUtils.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchUtils.java index 9a388c3c75e..910989549f5 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchUtils.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchUtils.java @@ -273,7 +273,10 @@ public class LaunchUtils { * only once per session and the resulting version string stored for future uses. * * A timeout is scheduled which will kill the process if it takes too long. + * + * @deprecated Replaced with GdbLaunch.getLaunchEnvironment() */ + @Deprecated public static String getGDBVersion(final ILaunchConfiguration configuration) throws CoreException { String cmd = getGDBPath(configuration).toOSString() + " --version"; //$NON-NLS-1$ @@ -476,7 +479,9 @@ public class LaunchUtils { * Gets the CDT environment from the CDT project's configuration referenced by the * launch * @since 3.0 + * @deprecated Replaced with GdbLaunch.getLaunchEnvironment() */ + @Deprecated public static String[] getLaunchEnvironment(ILaunchConfiguration config) throws CoreException { // Get the project String projectName = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String)null); diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java index a6af482a498..39ecc3e4d96 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java @@ -16,29 +16,24 @@ package org.eclipse.cdt.dsf.gdb.service; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; -import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Properties; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.parser.util.StringUtil; -import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.dsf.concurrent.DsfRunnable; import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.concurrent.Sequence; -import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; -import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils; import org.eclipse.cdt.dsf.gdb.service.command.GDBControl.InitializationShutdownStep; import org.eclipse.cdt.dsf.mi.service.IMIBackend; @@ -50,148 +45,130 @@ import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.utils.CommandLineUtil; import org.eclipse.cdt.utils.spawner.ProcessFactory; import org.eclipse.cdt.utils.spawner.Spawner; -import org.eclipse.core.resources.IContainer; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; -import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.core.variables.VariablesPlugin; import org.eclipse.debug.core.DebugException; -import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; -import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; -import org.eclipse.debug.core.ILaunchManager; import org.osgi.framework.BundleContext; /** - * Implementation of {@link IGDBBackend} for the common case where GDB is launched - * in local file system on host PC where Eclipse runs. This also manages some GDB parameters - * from a given launch configuration.
- *
+ * Implementation of {@link IGDBBackend} for the common case where GDB is + * launched in local file system on host PC where Eclipse runs. This also + * manages some GDB parameters from a given launch configuration.
+ *
* You can subclass for you special needs. * * @since 1.1 */ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBackend2 { - + private final ILaunchConfiguration fLaunchConfiguration; - + /* * Parameters for launching GDB. */ - private IPath fProgramPath; - private IPath fGDBWorkingDirectory; - private String fGDBInitFile; - private List fSharedLibPaths; - private String fProgramArguments; - - private Properties fEnvVariables; private SessionType fSessionType; - private Boolean fAttach; - private State fBackendState = State.NOT_INITIALIZED; + private Boolean fAttach; + private State fBackendState = State.NOT_INITIALIZED; + + /* + * Unique ID of this service instance. + */ + private final String fBackendId; + private static int fgInstanceCounter = 0; + + /* + * Service state parameters. + */ + private MonitorJob fMonitorJob; + private Process fProcess; + private int fGDBExitValue; + private int fGDBLaunchTimeout = 30; /** - * Unique ID of this service instance. - */ - private final String fBackendId; - private static int fgInstanceCounter = 0; - - /* - * Service state parameters. - */ - private MonitorJob fMonitorJob; - private Process fProcess; - private int fGDBExitValue; - private int fGDBLaunchTimeout = 30; - - /** - * A Job that will set a failed status - * in the proper request monitor, if the interrupt - * did not succeed after a certain time. - */ - private MonitorInterruptJob fInterruptFailedJob; + * A Job that will set a failed status in the proper request monitor, if the + * interrupt did not succeed after a certain time. + */ + private MonitorInterruptJob fInterruptFailedJob; - public GDBBackend(DsfSession session, ILaunchConfiguration lc) { super(session); - fBackendId = "gdb[" +Integer.toString(fgInstanceCounter++) + "]"; //$NON-NLS-1$//$NON-NLS-2$ - fLaunchConfiguration = lc; - - try { - // Don't call verifyCProject, because the JUnit tests are not setting a project - ICProject cproject = LaunchUtils.getCProject(lc); - fProgramPath = LaunchUtils.verifyProgramPath(lc, cproject); - } catch (CoreException e) { - fProgramPath = new Path(""); //$NON-NLS-1$ - } + this.fLaunchConfiguration = lc; + fBackendId = "gdb[" + Integer.toString(fgInstanceCounter++) + "]"; //$NON-NLS-1$//$NON-NLS-2$ + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize(new ImmediateRequestMonitor(requestMonitor) { + @Override + protected void handleSuccess() { + doInitialize(requestMonitor); + } + }); + } + + private void doInitialize(final RequestMonitor requestMonitor) { + getExecutor().execute(getStartupSequence(requestMonitor)); } - - @Override - public void initialize(final RequestMonitor requestMonitor) { - super.initialize(new ImmediateRequestMonitor(requestMonitor) { - @Override - protected void handleSuccess() { - doInitialize(requestMonitor); - } - }); - } - private void doInitialize(final RequestMonitor requestMonitor) { - getExecutor().execute(getStartupSequence(requestMonitor)); - } - /** @since 5.0 */ protected Sequence getStartupSequence(final RequestMonitor requestMonitor) { - final Sequence.Step[] initializeSteps = new Sequence.Step[] { - new GDBProcessStep(InitializationShutdownStep.Direction.INITIALIZING), - new MonitorJobStep(InitializationShutdownStep.Direction.INITIALIZING), - new RegisterStep(InitializationShutdownStep.Direction.INITIALIZING), - }; + final Sequence.Step[] initializeSteps = new Sequence.Step[] { + new GDBProcessStep(InitializationShutdownStep.Direction.INITIALIZING), + new MonitorJobStep(InitializationShutdownStep.Direction.INITIALIZING), + new RegisterStep(InitializationShutdownStep.Direction.INITIALIZING), }; - return new Sequence(getExecutor(), requestMonitor) { - @Override public Step[] getSteps() { return initializeSteps; } - }; - } + return new Sequence(getExecutor(), requestMonitor) { + @Override + public Step[] getSteps() { + return initializeSteps; + } + }; + } - @Override - public void shutdown(final RequestMonitor requestMonitor) { - getExecutor().execute(getShutdownSequence(new RequestMonitor(getExecutor(), requestMonitor) { - @Override - protected void handleCompleted() { + @Override + public void shutdown(final RequestMonitor requestMonitor) { + getExecutor().execute(getShutdownSequence(new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleCompleted() { GDBBackend.super.shutdown(requestMonitor); - } - })); - } - - /** @since 5.0 */ - protected Sequence getShutdownSequence(RequestMonitor requestMonitor) { - final Sequence.Step[] shutdownSteps = new Sequence.Step[] { - new RegisterStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), - new MonitorJobStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), - new GDBProcessStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), - }; - - return new Sequence(getExecutor(), requestMonitor) { - @Override public Step[] getSteps() { return shutdownSteps; } - }; - } + } + })); + } + + /** @since 5.0 */ + protected Sequence getShutdownSequence(RequestMonitor requestMonitor) { + final Sequence.Step[] shutdownSteps = new Sequence.Step[] { + new RegisterStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), + new MonitorJobStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), + new GDBProcessStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), }; + + return new Sequence(getExecutor(), requestMonitor) { + @Override + public Step[] getSteps() { + return shutdownSteps; + } + }; + } + + private GdbLaunch getGDBLaunch() { + return (GdbLaunch) getSession().getModelAdapter(ILaunch.class); + } - /** @since 4.0 */ - protected IPath getGDBPath() { - return LaunchUtils.getGDBPath(fLaunchConfiguration); - } + protected IPath getGDBPath() { + return getGDBLaunch().getGDBPath(); + } /** - * Options for GDB process. - * Allow subclass to override. + * Options for GDB process. Allow subclass to override. + * * @deprecated Use {@link #getGDBCommandLineArray()} instead */ @Deprecated @@ -201,755 +178,656 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa } /** - * Options for GDB process. - * Returns the GDB command and its arguments as an array. - * Allow subclass to override. + * Options for GDB process. Returns the GDB command and its arguments as an + * array. Allow subclass to override. + * * @since 4.6 */ protected String[] getGDBCommandLineArray() { // The goal here is to keep options to an absolute minimum. // All configuration should be done in the final launch sequence // to allow for more flexibility. - - String cmd = getGDBPath().toOSString() + - " --interpreter" + //$NON-NLS-1$ - // We currently work with MI version 2. Don't use just 'mi' because it - // points to the latest MI version, while we want mi2 specifically. - " mi2" + //$NON-NLS-1$ - // Don't read the gdbinit file here. It is read explicitly in - // the LaunchSequence to make it easier to customize. - " --nx"; //$NON-NLS-1$ - - // Parse to properly handle spaces and such things (bug 458499) + + String cmd = getGDBPath().toOSString() + " --interpreter" + //$NON-NLS-1$ + // We currently work with MI version 2. Don't use just 'mi' because it + // points to the latest MI version, while we want mi2 specifically. + " mi2" + //$NON-NLS-1$ + // Don't read the gdbinit file here. It is read explicitly in + // the LaunchSequence to make it easier to customize. + " --nx"; //$NON-NLS-1$ + + // Parse to properly handle spaces and such things (bug 458499) return CommandLineUtil.argumentsToArray(cmd); } @Override public String getGDBInitFile() throws CoreException { - if (fGDBInitFile == null) { - String defaultGdbInit = Platform.getPreferencesService().getString(GdbPlugin.PLUGIN_ID, - IGdbDebugPreferenceConstants.PREF_DEFAULT_GDB_INIT, - IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT, null); - - fGDBInitFile = fLaunchConfiguration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, defaultGdbInit); - } - - return fGDBInitFile; + return getGDBLaunch().getGDBInitFile(); } @Override public IPath getGDBWorkingDirectory() throws CoreException { - if (fGDBWorkingDirectory == null) { - - // First try to use the user-specified working directory for the debugged program. - // This is fine only with local debug. - // For remote debug, the working dir of the debugged program will be on remote device - // and hence not applicable. In such case we may just use debugged program path on host - // as the working dir for GDB. - // However, we cannot find a standard/common way to distinguish remote debug from local - // debug. For instance, a local debug may also use gdbserver+gdb. So it's up to each - // debugger implementation to make the distinction. - // - IPath path = null; - String location = fLaunchConfiguration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, (String)null); - - if (location != null) { - String expandedLocation = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(location); - if (!expandedLocation.isEmpty()) { - path = new Path(expandedLocation); - } - } - - if (path != null) { - // Some validity check. Should have been done by UI code. - if (path.isAbsolute()) { - File dir = new File(path.toPortableString()); - if (! dir.isDirectory()) - path = null; - } else { - IResource res = ResourcesPlugin.getWorkspace().getRoot().findMember(path); - if (res instanceof IContainer && res.exists()) { - path = res.getLocation(); - } - else - // Relative but not found in workspace. - path = null; - } - } - - if (path == null) { - // default working dir is the project if this config has a project - ICProject cp = LaunchUtils.getCProject(fLaunchConfiguration); - if (cp != null) { - IProject p = cp.getProject(); - path = p.getLocation(); - } - else { - // no meaningful value found. Just return null. - } - } - - fGDBWorkingDirectory = path; - } - - return fGDBWorkingDirectory; + return getGDBLaunch().getGDBWorkingDirectory(); } @Override public String getProgramArguments() throws CoreException { - if (fProgramArguments == null) { - fProgramArguments = fLaunchConfiguration.getAttribute( - ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, - (String)null); - - if (fProgramArguments != null) { - fProgramArguments = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(fProgramArguments); - } - } - - return fProgramArguments; + return getGDBLaunch().getProgramArguments(); } @Override public IPath getProgramPath() { - return fProgramPath; + try { + return new Path(getGDBLaunch().getProgramPath()); + } catch (CoreException e) { + return new Path(""); //$NON-NLS-1$ + } } @Override public List getSharedLibraryPaths() throws CoreException { - if (fSharedLibPaths == null) { - fSharedLibPaths = fLaunchConfiguration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH, - new ArrayList(0)); - } - - return fSharedLibPaths; + return getGDBLaunch().getSharedLibraryPaths(); } /** @since 3.0 */ @Override public Properties getEnvironmentVariables() throws CoreException { - if (fEnvVariables == null) { - fEnvVariables = new Properties(); - - // if the attribute ATTR_APPEND_ENVIRONMENT_VARIABLES is set, - // the LaunchManager will return both the new variables and the existing ones. - // That would force us to delete all the variables in GDB, and then re-create then all - // that is not very efficient. So, let's fool the LaunchManager into returning just the - // list of new variables. - - boolean append = fLaunchConfiguration.getAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true); - - String[] properties; - if (append) { - ILaunchConfigurationWorkingCopy wc = fLaunchConfiguration.copy(""); //$NON-NLS-1$ - // Don't save this change, it is just temporary, and in just a copy of our launchConfig. - wc.setAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, false); - properties = DebugPlugin.getDefault().getLaunchManager().getEnvironment(wc); - } else { - // We're getting rid of the environment anyway, so this call will only yield the new variables. - properties = DebugPlugin.getDefault().getLaunchManager().getEnvironment(fLaunchConfiguration); - } - - if (properties == null) { - properties = new String[0]; - } - - for (String property : properties) { - int idx = property.indexOf('='); - if (idx != -1) { - String key = property.substring(0, idx); - String value = property.substring(idx + 1); - fEnvVariables.setProperty(key, value); - } else { - fEnvVariables.setProperty(property, ""); //$NON-NLS-1$ - } - } - } - - return fEnvVariables; + return getGDBLaunch().getEnvironmentVariables(); } - + /** @since 3.0 */ @Override public boolean getClearEnvironment() throws CoreException { - return !fLaunchConfiguration.getAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true); + return getGDBLaunch().getClearEnvironment(); } - + /** @since 3.0 */ @Override public boolean getUpdateThreadListOnSuspend() throws CoreException { - return fLaunchConfiguration - .getAttribute( - IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND, - IGDBLaunchConfigurationConstants.DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND_DEFAULT); + return getGDBLaunch().getUpdateThreadListOnSuspend(); } private Process launchGDBProcess() throws CoreException { // Keep calling deprecated getGDBCommandLine() in case it was overridden String command = getGDBCommandLine(); - // Keep calling deprecated launchGDBProcess(String) in case it was overridden + // Keep calling deprecated launchGDBProcess(String) in case it was + // overridden return launchGDBProcess(command); } /** - * Launch GDB process. - * Allow subclass to override. + * Launch GDB process. Allow subclass to override. + * * @deprecated Use {@link #launchGDBProcess(String[])} instead */ @Deprecated protected Process launchGDBProcess(String commandLine) throws CoreException { // Backwards-compatibility check - // If the commandLine parameter is not the same as the command line array we provide - // it implies that the commandLine was modified by an extender and should be used as - // is. If it is the same, we can use the command line array instead using the more robust + // If the commandLine parameter is not the same as the command line + // array we provide + // it implies that the commandLine was modified by an extender and + // should be used as + // is. If it is the same, we can use the command line array instead + // using the more robust // non-deprecated call to launchGDBProcess. String unmodifiedCmdLine = StringUtil.join(getGDBCommandLineArray(), " ").trim(); //$NON-NLS-1$ if (unmodifiedCmdLine.equals(commandLine.trim()) == false) { Process proc = null; try { - proc = ProcessFactory.getFactory().exec(commandLine, LaunchUtils.getLaunchEnvironment(fLaunchConfiguration)); + proc = ProcessFactory.getFactory().exec(commandLine, getGDBLaunch().getLaunchEnvironment()); } catch (IOException e) { - String message = "Error while launching command " + commandLine; //$NON-NLS-1$ + String message = "Error while launching command " + commandLine; //$NON-NLS-1$ throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, message, e)); } return proc; - } + } // End of Backwards-compatibility check return launchGDBProcess(getGDBCommandLineArray()); } - + /** - * Launch GDB process with command and arguments. - * Allow subclass to override. + * Launch GDB process with command and arguments. Allow subclass to + * override. + * * @since 4.6 */ protected Process launchGDBProcess(String[] commandLine) throws CoreException { - Process proc = null; + Process proc = null; try { - proc = ProcessFactory.getFactory().exec(commandLine, LaunchUtils.getLaunchEnvironment(fLaunchConfiguration)); + proc = ProcessFactory.getFactory().exec(commandLine, getGDBLaunch().getLaunchEnvironment()); } catch (IOException e) { - String message = "Error while launching command: " + StringUtil.join(commandLine, " "); //$NON-NLS-1$ //$NON-NLS-2$ - throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, message, e)); + String message = "Error while launching command: " + StringUtil.join(commandLine, " "); //$NON-NLS-1$ //$NON-NLS-2$ + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, message, e)); } - + return proc; } - public Process getProcess() { - return fProcess; - } - + public Process getProcess() { + return fProcess; + } + @Override - public OutputStream getMIOutputStream() { - return fProcess.getOutputStream(); - }; - + public OutputStream getMIOutputStream() { + return fProcess.getOutputStream(); + }; + @Override - public InputStream getMIInputStream() { - return fProcess.getInputStream(); - }; + public InputStream getMIInputStream() { + return fProcess.getInputStream(); + }; /** @since 4.1 */ @Override - public InputStream getMIErrorStream() { - return fProcess.getErrorStream(); - }; + public InputStream getMIErrorStream() { + return fProcess.getErrorStream(); + }; @Override - public String getId() { - return fBackendId; - } + public String getId() { + return fBackendId; + } @Override - public void interrupt() { - if (fProcess instanceof Spawner) { - Spawner gdbSpawner = (Spawner) fProcess; - + public void interrupt() { + if (fProcess instanceof Spawner) { + Spawner gdbSpawner = (Spawner) fProcess; + // Cygwin gdb 6.8 is capricious when it comes to interrupting the // target. The same logic here will work with MinGW, though. And on // linux it's irrelevant since interruptCTRLC()==interrupt(). So, // one odd size fits all. // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=304096#c54 - if (getSessionType() == SessionType.REMOTE) { - gdbSpawner.interrupt(); - } - else { - gdbSpawner.interruptCTRLC(); - } - } - } + if (getSessionType() == SessionType.REMOTE) { + gdbSpawner.interrupt(); + } else { + gdbSpawner.interruptCTRLC(); + } + } + } - /** + /** * @since 3.0 */ @Override - public void interruptAndWait(int timeout, RequestMonitor rm) { - if (fProcess instanceof Spawner) { - Spawner gdbSpawner = (Spawner) fProcess; + public void interruptAndWait(int timeout, RequestMonitor rm) { + if (fProcess instanceof Spawner) { + Spawner gdbSpawner = (Spawner) fProcess; // Cygwin gdb 6.8 is capricious when it comes to interrupting the // target. The same logic here will work with MinGW, though. And on // linux it's irrelevant since interruptCTRLC()==interrupt(). So, // one odd size fits all. - // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=304096#c54 - if (getSessionType() == SessionType.REMOTE) { - gdbSpawner.interrupt(); - } - else { - gdbSpawner.interruptCTRLC(); - } - fInterruptFailedJob = new MonitorInterruptJob(timeout, rm); - } else { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Cannot interrupt.", null)); //$NON-NLS-1$ - rm.done(); - } - } + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=304096#c54 + if (getSessionType() == SessionType.REMOTE) { + gdbSpawner.interrupt(); + } else { + gdbSpawner.interruptCTRLC(); + } + fInterruptFailedJob = new MonitorInterruptJob(timeout, rm); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, + "Cannot interrupt.", null)); //$NON-NLS-1$ + rm.done(); + } + } - /** + /** * @since 3.0 */ @Override - public void interruptInferiorAndWait(long pid, int timeout, RequestMonitor rm) { - if (fProcess instanceof Spawner) { - Spawner gdbSpawner = (Spawner) fProcess; - gdbSpawner.raise((int)pid, gdbSpawner.INT); - fInterruptFailedJob = new MonitorInterruptJob(timeout, rm); - } else { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Cannot interrupt.", null)); //$NON-NLS-1$ - rm.done(); - } - } + public void interruptInferiorAndWait(long pid, int timeout, RequestMonitor rm) { + if (fProcess instanceof Spawner) { + Spawner gdbSpawner = (Spawner) fProcess; + gdbSpawner.raise((int) pid, gdbSpawner.INT); + fInterruptFailedJob = new MonitorInterruptJob(timeout, rm); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, + "Cannot interrupt.", null)); //$NON-NLS-1$ + rm.done(); + } + } @Override - public void destroy() { - // Don't close the streams ourselves as it may be too early. - // Wait for the actual user of the streams to close it. - // Bug 339379 - - // destroy() should be supported even if it's not spawner. - if (getState() == State.STARTED) { - fProcess.destroy(); - } - } + public void destroy() { + // Don't close the streams ourselves as it may be too early. + // Wait for the actual user of the streams to close it. + // Bug 339379 + + // destroy() should be supported even if it's not spawner. + if (getState() == State.STARTED) { + fProcess.destroy(); + } + } @Override - public State getState() { - return fBackendState; - } - + public State getState() { + return fBackendState; + } + @Override - public int getExitCode() { - return fGDBExitValue; - } - + public int getExitCode() { + return fGDBExitValue; + } + @Override - public SessionType getSessionType() { - if (fSessionType == null) { - fSessionType = LaunchUtils.getSessionType(fLaunchConfiguration); - } - return fSessionType; - } + public SessionType getSessionType() { + if (fSessionType == null) { + fSessionType = LaunchUtils.getSessionType(fLaunchConfiguration); + } + return fSessionType; + } @Override public boolean getIsAttachSession() { - if (fAttach == null) { - fAttach = LaunchUtils.getIsAttach(fLaunchConfiguration); - } - return fAttach; + if (fAttach == null) { + fAttach = LaunchUtils.getIsAttach(fLaunchConfiguration); + } + return fAttach; } @Override protected BundleContext getBundleContext() { - return GdbPlugin.getBundleContext(); + return GdbPlugin.getBundleContext(); } protected class GDBProcessStep extends InitializationShutdownStep { - GDBProcessStep(Direction direction) { super(direction); } - - @Override - public void initialize(final RequestMonitor requestMonitor) { - doGDBProcessStep(requestMonitor); - } - - @Override - protected void shutdown(final RequestMonitor requestMonitor) { - undoGDBProcessStep(requestMonitor); - } - } + GDBProcessStep(Direction direction) { + super(direction); + } - protected class MonitorJobStep extends InitializationShutdownStep { - MonitorJobStep(Direction direction) { super(direction); } + @Override + public void initialize(final RequestMonitor requestMonitor) { + doGDBProcessStep(requestMonitor); + } - @Override - public void initialize(final RequestMonitor requestMonitor) { - doMonitorJobStep(requestMonitor); - } + @Override + protected void shutdown(final RequestMonitor requestMonitor) { + undoGDBProcessStep(requestMonitor); + } + } - @Override - protected void shutdown(RequestMonitor requestMonitor) { - undoMonitorJobStep(requestMonitor); - } - } - - protected class RegisterStep extends InitializationShutdownStep { - RegisterStep(Direction direction) { super(direction); } - @Override - public void initialize(RequestMonitor requestMonitor) { - doRegisterStep(requestMonitor); - } + protected class MonitorJobStep extends InitializationShutdownStep { + MonitorJobStep(Direction direction) { + super(direction); + } - @Override - protected void shutdown(RequestMonitor requestMonitor) { - undoRegisterStep(requestMonitor); - } - } + @Override + public void initialize(final RequestMonitor requestMonitor) { + doMonitorJobStep(requestMonitor); + } + + @Override + protected void shutdown(RequestMonitor requestMonitor) { + undoMonitorJobStep(requestMonitor); + } + } + + protected class RegisterStep extends InitializationShutdownStep { + RegisterStep(Direction direction) { + super(direction); + } + + @Override + public void initialize(RequestMonitor requestMonitor) { + doRegisterStep(requestMonitor); + } + + @Override + protected void shutdown(RequestMonitor requestMonitor) { + undoRegisterStep(requestMonitor); + } + } /** @since 5.0 */ protected void doGDBProcessStep(final RequestMonitor requestMonitor) { - class GDBLaunchMonitor { - boolean fLaunched = false; - boolean fTimedOut = false; - } - final GDBLaunchMonitor fGDBLaunchMonitor = new GDBLaunchMonitor(); + class GDBLaunchMonitor { + boolean fLaunched = false; + boolean fTimedOut = false; + } + final GDBLaunchMonitor fGDBLaunchMonitor = new GDBLaunchMonitor(); - final RequestMonitor gdbLaunchRequestMonitor = new RequestMonitor(getExecutor(), requestMonitor) { - @Override - protected void handleCompleted() { - if (!fGDBLaunchMonitor.fTimedOut) { - fGDBLaunchMonitor.fLaunched = true; - if (!isSuccess()) { - requestMonitor.setStatus(getStatus()); - } - requestMonitor.done(); - } - } - }; - - final Job startGdbJob = new Job("Start GDB Process Job") { //$NON-NLS-1$ - { - setSystem(true); - } + final RequestMonitor gdbLaunchRequestMonitor = new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleCompleted() { + if (!fGDBLaunchMonitor.fTimedOut) { + fGDBLaunchMonitor.fLaunched = true; + if (!isSuccess()) { + requestMonitor.setStatus(getStatus()); + } + requestMonitor.done(); + } + } + }; - @Override - protected IStatus run(IProgressMonitor monitor) { - if (gdbLaunchRequestMonitor.isCanceled()) { - gdbLaunchRequestMonitor.setStatus(new Status(IStatus.CANCEL, GdbPlugin.PLUGIN_ID, -1, "Canceled starting GDB", null)); //$NON-NLS-1$ - gdbLaunchRequestMonitor.done(); - return Status.OK_STATUS; - } - - try { - fProcess = launchGDBProcess(); - // Need to do this on the executor for thread-safety - getExecutor().submit( - new DsfRunnable() { - @Override - public void run() { fBackendState = State.STARTED; } - }); - // Don't send the backendStarted event yet. We wait until we have registered this service - // so that other services can have access to it. - } catch(CoreException e) { - gdbLaunchRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, e.getMessage(), e)); - gdbLaunchRequestMonitor.done(); - return Status.OK_STATUS; - } - - BufferedReader inputReader = null; - BufferedReader errorReader = null; - boolean success = false; - try { - // Read initial GDB prompt - inputReader = new BufferedReader(new InputStreamReader(getMIInputStream())); - String line; - while ((line = inputReader.readLine()) != null) { - line = line.trim(); - if (line.endsWith("(gdb)")) { //$NON-NLS-1$ - success = true; - break; - } - } - - // Failed to read initial prompt, check for error - if (!success) { - errorReader = new BufferedReader(new InputStreamReader(getMIErrorStream())); - String errorInfo = errorReader.readLine(); - if (errorInfo == null) { - errorInfo = "GDB prompt not read"; //$NON-NLS-1$ - } - gdbLaunchRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, errorInfo, null)); - } - } catch (IOException e) { - success = false; - gdbLaunchRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Error reading GDB output", e)); //$NON-NLS-1$ - } + final Job startGdbJob = new Job("Start GDB Process Job") { //$NON-NLS-1$ + { + setSystem(true); + } - // In the case of failure, close the MI streams so - // they are not leaked. - if (!success) - { - if (inputReader != null) { - try { + @Override + protected IStatus run(IProgressMonitor monitor) { + if (gdbLaunchRequestMonitor.isCanceled()) { + gdbLaunchRequestMonitor.setStatus( + new Status(IStatus.CANCEL, GdbPlugin.PLUGIN_ID, -1, "Canceled starting GDB", null)); //$NON-NLS-1$ + gdbLaunchRequestMonitor.done(); + return Status.OK_STATUS; + } + + try { + fProcess = launchGDBProcess(); + // Need to do this on the executor for thread-safety + getExecutor().submit(new DsfRunnable() { + @Override + public void run() { + fBackendState = State.STARTED; + } + }); + // Don't send the backendStarted event yet. We wait + // until we have registered this service + // so that other services can have access to it. + } catch (CoreException e) { + gdbLaunchRequestMonitor + .setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, e.getMessage(), e)); + gdbLaunchRequestMonitor.done(); + return Status.OK_STATUS; + } + + BufferedReader inputReader = null; + BufferedReader errorReader = null; + boolean success = false; + try { + // Read initial GDB prompt + inputReader = new BufferedReader(new InputStreamReader(getMIInputStream())); + String line; + while ((line = inputReader.readLine()) != null) { + line = line.trim(); + if (line.endsWith("(gdb)")) { //$NON-NLS-1$ + success = true; + break; + } + } + + // Failed to read initial prompt, check for error + if (!success) { + errorReader = new BufferedReader(new InputStreamReader(getMIErrorStream())); + String errorInfo = errorReader.readLine(); + if (errorInfo == null) { + errorInfo = "GDB prompt not read"; //$NON-NLS-1$ + } + gdbLaunchRequestMonitor + .setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, errorInfo, null)); + } + } catch (IOException e) { + success = false; + gdbLaunchRequestMonitor.setStatus( + new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Error reading GDB output", e)); //$NON-NLS-1$ + } + + // In the case of failure, close the MI streams so + // they are not leaked. + if (!success) { + if (inputReader != null) { + try { inputReader.close(); } catch (IOException e) { } - } - if (errorReader != null) { - try { + } + if (errorReader != null) { + try { errorReader.close(); } catch (IOException e) { } - } - } - - gdbLaunchRequestMonitor.done(); - return Status.OK_STATUS; - } - }; - startGdbJob.schedule(); - - getExecutor().schedule(new Runnable() { - @Override - public void run() { - // Only process the event if we have not finished yet (hit the breakpoint). - if (!fGDBLaunchMonitor.fLaunched) { - fGDBLaunchMonitor.fTimedOut = true; - Thread jobThread = startGdbJob.getThread(); - if (jobThread != null) { - jobThread.interrupt(); - } - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, "Timed out trying to launch GDB.", null)); //$NON-NLS-1$ - requestMonitor.done(); - } - }}, - fGDBLaunchTimeout, TimeUnit.SECONDS); + } + } + + gdbLaunchRequestMonitor.done(); + return Status.OK_STATUS; + } + }; + startGdbJob.schedule(); + + getExecutor().schedule(new Runnable() { + @Override + public void run() { + // Only process the event if we have not finished yet (hit + // the breakpoint). + if (!fGDBLaunchMonitor.fLaunched) { + fGDBLaunchMonitor.fTimedOut = true; + Thread jobThread = startGdbJob.getThread(); + if (jobThread != null) { + jobThread.interrupt(); + } + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, + DebugException.TARGET_REQUEST_FAILED, "Timed out trying to launch GDB.", null)); //$NON-NLS-1$ + requestMonitor.done(); + } + } + }, fGDBLaunchTimeout, TimeUnit.SECONDS); } /** @since 5.0 */ protected void undoGDBProcessStep(final RequestMonitor requestMonitor) { - if (getState() != State.STARTED) { - // gdb not started yet or already killed, don't bother starting - // a job to kill it - requestMonitor.done(); - return; - } + if (getState() != State.STARTED) { + // gdb not started yet or already killed, don't bother starting + // a job to kill it + requestMonitor.done(); + return; + } - new Job("Terminating GDB process.") { //$NON-NLS-1$ - { - setSystem(true); - } - - @Override - protected IStatus run(IProgressMonitor monitor) { - try { - // Need to do this on the executor for thread-safety - // And we should wait for it to complete since we then - // check if the killing of GDB worked. - getExecutor().submit( - new DsfRunnable() { - @Override - public void run() { - destroy(); - - if (fMonitorJob.fMonitorExited) { - // Now that we have destroyed the process, - // and that the monitoring thread was killed, - // we need to set our state and send the event - fBackendState = State.TERMINATED; - getSession().dispatchEvent( - new BackendStateChangedEvent(getSession().getId(), getId(), State.TERMINATED), - getProperties()); - } - } - }).get(); + new Job("Terminating GDB process.") { //$NON-NLS-1$ + { + setSystem(true); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + // Need to do this on the executor for thread-safety + // And we should wait for it to complete since we then + // check if the killing of GDB worked. + getExecutor().submit(new DsfRunnable() { + @Override + public void run() { + destroy(); + + if (fMonitorJob.fMonitorExited) { + // Now that we have destroyed the process, and + // that the monitoring thread was killed, we + // need to set our state and send the event + fBackendState = State.TERMINATED; + getSession().dispatchEvent( + new BackendStateChangedEvent(getSession().getId(), getId(), State.TERMINATED), + getProperties()); + } + } + }).get(); } catch (InterruptedException e1) { } catch (ExecutionException e1) { } - - int attempts = 0; - while (attempts < 10) { - try { - // Don't know if we really need the exit value... but what the heck. - fGDBExitValue = fProcess.exitValue(); // throws exception if process not exited - - requestMonitor.done(); - return Status.OK_STATUS; - } catch (IllegalThreadStateException ie) { - } - try { - Thread.sleep(500); - } catch (InterruptedException e) { - } - attempts++; - } - requestMonitor.setStatus(new Status( - IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "GDB terminate failed", null)); //$NON-NLS-1$ - requestMonitor.done(); - return Status.OK_STATUS; - } - }.schedule(); + + int attempts = 0; + while (attempts < 10) { + try { + // Don't know if we really need the exit value... + // but what the heck. + // throws exception if process not exited + fGDBExitValue = fProcess.exitValue(); + + requestMonitor.done(); + return Status.OK_STATUS; + } catch (IllegalThreadStateException ie) { + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + } + attempts++; + } + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, + IDsfStatusConstants.REQUEST_FAILED, "GDB terminate failed", null)); //$NON-NLS-1$ + requestMonitor.done(); + return Status.OK_STATUS; + } + }.schedule(); } - - /** @since 5.0 */ - protected void doMonitorJobStep(final RequestMonitor requestMonitor) { - fMonitorJob = new MonitorJob( - fProcess, - new DsfRunnable() { - @Override - public void run() { - requestMonitor.done(); - } - }); - fMonitorJob.schedule(); - } - - /** @since 5.0 */ - protected void undoMonitorJobStep(RequestMonitor requestMonitor) { - if (fMonitorJob != null) { - fMonitorJob.kill(); - } - requestMonitor.done(); - } /** @since 5.0 */ - protected void doRegisterStep(RequestMonitor requestMonitor) { - register(new String[]{ IMIBackend.class.getName(), - IMIBackend2.class.getName(), - IGDBBackend.class.getName() }, - new Hashtable()); - - getSession().addServiceEventListener(GDBBackend.this, null); - - /* - * This event is not consumed by any one at present, instead it's - * the GDBControlInitializedDMEvent that's used to indicate that GDB - * back end is ready for MI commands. But we still fire the event as - * it does no harm and may be needed sometime.... 09/29/2008 - * - * We send the event in the register step because that is when - * other services have access to it. - */ - getSession().dispatchEvent( - new BackendStateChangedEvent(getSession().getId(), getId(), State.STARTED), - getProperties()); - - requestMonitor.done(); - } + protected void doMonitorJobStep(final RequestMonitor requestMonitor) { + fMonitorJob = new MonitorJob(fProcess, new DsfRunnable() { + @Override + public void run() { + requestMonitor.done(); + } + }); + fMonitorJob.schedule(); + } /** @since 5.0 */ - protected void undoRegisterStep(RequestMonitor requestMonitor) { - unregister(); - getSession().removeServiceEventListener(GDBBackend.this); - requestMonitor.done(); - } - - /** - * Monitors a system process, waiting for it to terminate, and - * then notifies the associated runtime process. - */ - private class MonitorJob extends Job { - boolean fMonitorExited = false; - DsfRunnable fMonitorStarted; - Process fMonProcess; + protected void undoMonitorJobStep(RequestMonitor requestMonitor) { + if (fMonitorJob != null) { + fMonitorJob.kill(); + } + requestMonitor.done(); + } - @Override - protected IStatus run(IProgressMonitor monitor) { - synchronized(fMonProcess) { - getExecutor().submit(fMonitorStarted); - try { - fMonProcess.waitFor(); - fGDBExitValue = fMonProcess.exitValue(); + /** @since 5.0 */ + protected void doRegisterStep(RequestMonitor requestMonitor) { + register(new String[] { IMIBackend.class.getName(), IMIBackend2.class.getName(), IGDBBackend.class.getName() }, + new Hashtable()); - // Need to do this on the executor for thread-safety - getExecutor().submit( - new DsfRunnable() { - @Override - public void run() { - destroy(); - fBackendState = State.TERMINATED; - getSession().dispatchEvent( - new BackendStateChangedEvent(getSession().getId(), getId(), State.TERMINATED), - getProperties()); - } - }); - } catch (InterruptedException ie) { - // clear interrupted state - Thread.interrupted(); - } + getSession().addServiceEventListener(GDBBackend.this, null); - fMonitorExited = true; - } - return Status.OK_STATUS; - } + /* + * This event is not consumed by any one at present, instead it's the + * GDBControlInitializedDMEvent that's used to indicate that GDB back + * end is ready for MI commands. But we still fire the event as it does + * no harm and may be needed sometime.... 09/29/2008 + * + * We send the event in the register step because that is when other + * services have access to it. + */ + getSession().dispatchEvent(new BackendStateChangedEvent(getSession().getId(), getId(), State.STARTED), + getProperties()); - MonitorJob(Process process, DsfRunnable monitorStarted) { - super("GDB process monitor job."); //$NON-NLS-1$ - fMonProcess = process; - fMonitorStarted = monitorStarted; - setSystem(true); - } + requestMonitor.done(); + } - void kill() { - synchronized(fMonProcess) { - if (!fMonitorExited) { - getThread().interrupt(); - } - } - } - } + /** @since 5.0 */ + protected void undoRegisterStep(RequestMonitor requestMonitor) { + unregister(); + getSession().removeServiceEventListener(GDBBackend.this); + requestMonitor.done(); + } - /** - * Stores the request monitor that must be dealt with for - * the result of the interrupt operation. If the interrupt - * successfully suspends the backend, the request monitor can - * be retrieved and completed successfully, and then this job - * should be canceled. If this job is not canceled before - * the time is up, it will imply the interrupt did not - * successfully suspend the backend, and the current job will - * indicate this in the request monitor. - * - * The specified timeout is used to indicate how many milliseconds - * this job should wait for. INTERRUPT_TIMEOUT_DEFAULT indicates - * to use the default of 5 seconds. The default is also use if the - * timeout value is 0 or negative. - * - * @since 3.0 - */ - protected class MonitorInterruptJob extends Job { - // Bug 310274. Until we have a preference to configure timeouts, - // we need a large enough default timeout to accommodate slow - // remote sessions. - private final static int TIMEOUT_DEFAULT_VALUE = 5000; - private final RequestMonitor fRequestMonitor; + /** + * Monitors a system process, waiting for it to terminate, and then notifies + * the associated runtime process. + */ + private class MonitorJob extends Job { + boolean fMonitorExited = false; + DsfRunnable fMonitorStarted; + Process fMonProcess; - public MonitorInterruptJob(int timeout, RequestMonitor rm) { - super("Interrupt monitor job."); //$NON-NLS-1$ - setSystem(true); - fRequestMonitor = rm; - - if (timeout == INTERRUPT_TIMEOUT_DEFAULT || timeout <= 0) { - timeout = TIMEOUT_DEFAULT_VALUE; // default of 5 seconds - } - - schedule(timeout); - } + @Override + protected IStatus run(IProgressMonitor monitor) { + synchronized (fMonProcess) { + getExecutor().submit(fMonitorStarted); + try { + fMonProcess.waitFor(); + fGDBExitValue = fMonProcess.exitValue(); - @Override - protected IStatus run(IProgressMonitor monitor) { - getExecutor().submit( - new DsfRunnable() { - @Override - public void run() { - fInterruptFailedJob = null; - fRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Interrupt failed.", null)); //$NON-NLS-1$ - fRequestMonitor.done(); - } - }); - return Status.OK_STATUS; - } + // Need to do this on the executor for thread-safety + getExecutor().submit(new DsfRunnable() { + @Override + public void run() { + destroy(); + fBackendState = State.TERMINATED; + getSession().dispatchEvent( + new BackendStateChangedEvent(getSession().getId(), getId(), State.TERMINATED), + getProperties()); + } + }); + } catch (InterruptedException ie) { + // clear interrupted state + Thread.interrupted(); + } - public RequestMonitor getRequestMonitor() { return fRequestMonitor; } - } + fMonitorExited = true; + } + return Status.OK_STATUS; + } + + MonitorJob(Process process, DsfRunnable monitorStarted) { + super("GDB process monitor job."); //$NON-NLS-1$ + fMonProcess = process; + fMonitorStarted = monitorStarted; + setSystem(true); + } + + void kill() { + synchronized (fMonProcess) { + if (!fMonitorExited) { + getThread().interrupt(); + } + } + } + } + + /** + * Stores the request monitor that must be dealt with for the result of the + * interrupt operation. If the interrupt successfully suspends the backend, + * the request monitor can be retrieved and completed successfully, and then + * this job should be canceled. If this job is not canceled before the time + * is up, it will imply the interrupt did not successfully suspend the + * backend, and the current job will indicate this in the request monitor. + * + * The specified timeout is used to indicate how many milliseconds this job + * should wait for. INTERRUPT_TIMEOUT_DEFAULT indicates to use the default + * of 5 seconds. The default is also use if the timeout value is 0 or + * negative. + * + * @since 3.0 + */ + protected class MonitorInterruptJob extends Job { + // Bug 310274. Until we have a preference to configure timeouts, + // we need a large enough default timeout to accommodate slow + // remote sessions. + private final static int TIMEOUT_DEFAULT_VALUE = 5000; + private final RequestMonitor fRequestMonitor; + + public MonitorInterruptJob(int timeout, RequestMonitor rm) { + super("Interrupt monitor job."); //$NON-NLS-1$ + setSystem(true); + fRequestMonitor = rm; + + if (timeout == INTERRUPT_TIMEOUT_DEFAULT || timeout <= 0) { + timeout = TIMEOUT_DEFAULT_VALUE; // default of 5 seconds + } + + schedule(timeout); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + getExecutor().submit(new DsfRunnable() { + @Override + public void run() { + fInterruptFailedJob = null; + fRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, + IDsfStatusConstants.REQUEST_FAILED, "Interrupt failed.", null)); //$NON-NLS-1$ + fRequestMonitor.done(); + } + }); + return Status.OK_STATUS; + } + + public RequestMonitor getRequestMonitor() { + return fRequestMonitor; + } + + } /** * We use this handler to determine if the SIGINT we sent to GDB has been @@ -967,13 +845,13 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa * @since 3.0 * */ - @DsfServiceEventHandler - public void eventDispatched(final MIStoppedEvent e) { - if (fInterruptFailedJob != null) { - if (fInterruptFailedJob.cancel()) { - fInterruptFailedJob.getRequestMonitor().done(); - } - fInterruptFailedJob = null; - } - } + @DsfServiceEventHandler + public void eventDispatched(final MIStoppedEvent e) { + if (fInterruptFailedJob != null) { + if (fInterruptFailedJob.cancel()) { + fInterruptFailedJob.getRequestMonitor().done(); + } + fInterruptFailedJob = null; + } + } } diff --git a/qt/org.eclipse.cdt.qt.core/META-INF/MANIFEST.MF b/qt/org.eclipse.cdt.qt.core/META-INF/MANIFEST.MF index ac5d70d56a1..b84b9e14d67 100644 --- a/qt/org.eclipse.cdt.qt.core/META-INF/MANIFEST.MF +++ b/qt/org.eclipse.cdt.qt.core/META-INF/MANIFEST.MF @@ -18,7 +18,9 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.cdt.debug.core, org.freemarker;bundle-version="2.3.22", org.eclipse.cdt.build.core;bundle-version="1.0.0", - org.eclipse.cdt.build.gcc.core;bundle-version="1.0.0" + org.eclipse.cdt.build.gcc.core;bundle-version="1.0.0", + org.eclipse.cdt.dsf.gdb;bundle-version="5.0.0", + org.eclipse.cdt.dsf;bundle-version="2.6.0" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Localization: plugin diff --git a/qt/org.eclipse.cdt.qt.core/plugin.xml b/qt/org.eclipse.cdt.qt.core/plugin.xml index f868e7ce665..0e6de1a7d7d 100644 --- a/qt/org.eclipse.cdt.qt.core/plugin.xml +++ b/qt/org.eclipse.cdt.qt.core/plugin.xml @@ -165,16 +165,6 @@ class="org.eclipse.cdt.internal.qt.core.build.QtScannerInfoProvider"> - - - - + + + + + + + + + + diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/build/QtBuildConfigurationFactory.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/build/QtBuildConfigurationFactory.java index 22c4fe143a6..6e9a589f483 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/build/QtBuildConfigurationFactory.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/build/QtBuildConfigurationFactory.java @@ -124,7 +124,7 @@ public class QtBuildConfigurationFactory implements IAdapterFactory { for (IBuildConfiguration config : project.getBuildConfigs()) { configNames.add(config.getName()); } - String baseName = qtInstall.getSpec() + ":" + launchMode; //$NON-NLS-1$ + String baseName = qtInstall.getSpec() + "." + launchMode; //$NON-NLS-1$ String newName = baseName; int n = 0; while (configNames.contains(newName)) { diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalDebugLaunchConfigDelegate.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalDebugLaunchConfigDelegate.java new file mode 100644 index 00000000000..e360dcd0b35 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalDebugLaunchConfigDelegate.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2016 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.cdt.internal.qt.core.launch; + +import java.nio.file.Path; +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.concurrent.RequestMonitorWithProgress; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.debug.sourcelookup.DsfSourceLookupDirector; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.cdt.dsf.gdb.launching.ServicesLaunchSequence; +import org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactory; +import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.internal.qt.core.Activator; +import org.eclipse.cdt.qt.core.QtBuildConfiguration; +import org.eclipse.cdt.qt.core.QtLaunchConfigurationDelegate; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.launchbar.core.target.ILaunchTarget; +import org.eclipse.launchbar.core.target.launch.ITargetedLaunch; + +public class QtLocalDebugLaunchConfigDelegate extends QtLaunchConfigurationDelegate { + + @Override + public ITargetedLaunch getLaunch(ILaunchConfiguration configuration, String mode, ILaunchTarget target) + throws CoreException { + GdbLaunch launch = new GdbLaunch(configuration, mode, null); + launch.setLaunchTarget(target); + launch.initialize(); + + DsfSourceLookupDirector locator = new DsfSourceLookupDirector(launch.getSession()); + String memento = configuration.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO, (String) null); + if (memento == null) { + locator.initializeDefaults(configuration); + } else { + locator.initializeFromMemento(memento, configuration); + } + + launch.setSourceLocator(locator); + return launch; + } + + @Override + public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) + throws CoreException { + GdbLaunch gdbLaunch = (GdbLaunch) launch; + ILaunchTarget target = ((ITargetedLaunch) launch).getLaunchTarget(); + QtBuildConfiguration qtBuildConfig = getQtBuildConfiguration(configuration, mode, target, monitor); + + // TODO get it from the toolchain + gdbLaunch.setGDBPath("/usr/local/bin/gdb"); + String gdbVersion = gdbLaunch.getGDBVersion(); + + Path exeFile = qtBuildConfig.getProgramPath(); + gdbLaunch.setProgramPath(exeFile.toString()); + + gdbLaunch.setServiceFactory(new GdbDebugServicesFactory(gdbVersion)); + + Sequence servicesLaunchSequence = new ServicesLaunchSequence(gdbLaunch.getSession(), gdbLaunch, monitor); + gdbLaunch.getSession().getExecutor().execute(servicesLaunchSequence); + try { + servicesLaunchSequence.get(); + } catch (InterruptedException | ExecutionException e) { + throw new DebugException(new Status(IStatus.ERROR, Activator.ID, "Failure launching with gdb", e)); + } + + gdbLaunch.initializeControl(); + + gdbLaunch.addCLIProcess(gdbLaunch.getGDBPath().toOSString() + " (" + gdbVersion + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + + Query ready = new Query() { + @Override + protected void execute(final DataRequestMonitor rm) { + DsfServicesTracker tracker = new DsfServicesTracker( + Activator.getDefault().getBundle().getBundleContext(), gdbLaunch.getSession().getId()); + IGDBControl control = tracker.getService(IGDBControl.class); + tracker.dispose(); + control.completeInitialization( + new RequestMonitorWithProgress(ImmediateExecutor.getInstance(), monitor) { + @Override + protected void handleCompleted() { + if (isCanceled()) { + rm.cancel(); + } else { + rm.setStatus(getStatus()); + } + rm.done(); + } + }); + } + }; + + // Start it up + gdbLaunch.getSession().getExecutor().execute(ready); + try { + ready.get(); + } catch (ExecutionException | InterruptedException e) { + throw new DebugException(new Status(IStatus.ERROR, Activator.ID, "Failure to start debug session", e)); + } + } + +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalRunLaunchConfigDelegate.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalRunLaunchConfigDelegate.java index 238b40cac47..37049e0b434 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalRunLaunchConfigDelegate.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalRunLaunchConfigDelegate.java @@ -7,7 +7,6 @@ *******************************************************************************/ package org.eclipse.cdt.internal.qt.core.launch; -import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.Map; @@ -18,9 +17,7 @@ import org.eclipse.cdt.qt.core.QtLaunchConfigurationDelegate; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; @@ -34,70 +31,25 @@ public class QtLocalRunLaunchConfigDelegate extends QtLaunchConfigurationDelegat @Override public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException { - new Job("Running Qt App") { - @Override - protected IStatus run(IProgressMonitor monitor) { - try { - ILaunchTarget target = ((ITargetedLaunch) launch).getLaunchTarget(); - QtBuildConfiguration qtBuildConfig = getQtBuildConfiguration(configuration, mode, target, monitor); + ILaunchTarget target = ((ITargetedLaunch) launch).getLaunchTarget(); + QtBuildConfiguration qtBuildConfig = getQtBuildConfiguration(configuration, mode, target, monitor); - // get the executable - Path buildFolder = qtBuildConfig.getBuildDirectory(); - Path exeFile; - switch (Platform.getOS()) { - case Platform.OS_MACOSX: - // TODO this is mac local specific and really should be - // in the config - // TODO also need to pull the app name out of the pro - // file name - Path appFolder = buildFolder.resolve("main.app"); - Path contentsFolder = appFolder.resolve("Contents"); - Path macosFolder = contentsFolder.resolve("MacOS"); - exeFile = macosFolder.resolve("main"); - break; - case Platform.OS_WIN32: - Path releaseFolder = buildFolder.resolve("release"); - exeFile = releaseFolder.resolve("main.exe"); - break; - default: - return new Status(IStatus.ERROR, Activator.ID, "platform not supported: " + Platform.getOS()); - } + // get the executable + Path exeFile = qtBuildConfig.getProgramPath(); - ProcessBuilder builder = new ProcessBuilder(exeFile.toString()) - .directory(qtBuildConfig.getProject().getLocation().toFile()); + ProcessBuilder builder = new ProcessBuilder(exeFile.toString()) + .directory(qtBuildConfig.getProject().getLocation().toFile()); - // need to add the Qt libraries to the env - Map env = builder.environment(); - Path libPath = qtBuildConfig.getQtInstall().getLibPath(); - switch (Platform.getOS()) { - case Platform.OS_MACOSX: - String libPathEnv = env.get("DYLD_LIBRARY_PATH"); - if (libPathEnv == null) { - libPathEnv = libPath.toString(); - } else { - libPathEnv = libPath.toString() + File.pathSeparator + libPathEnv; - } - env.put("DYLD_LIBRARY_PATH", libPathEnv); - break; - case Platform.OS_WIN32: - String path = env.get("PATH"); - // TODO really need a bin path - // and resolve doesn't work properly on Windows - path = "C:/Qt/5.5/mingw492_32/bin;" + path; - env.put("PATH", path); - break; - } + // set up the environment + Map env = builder.environment(); + qtBuildConfig.setProgramEnvironment(env); - Process process = builder.start(); - DebugPlugin.newProcess(launch, process, "main"); - } catch (IOException e) { - return new Status(IStatus.ERROR, Activator.ID, "running", e); - } catch (CoreException e) { - return e.getStatus(); - } - return Status.OK_STATUS; - } - }.schedule(); + try { + Process process = builder.start(); + DebugPlugin.newProcess(launch, process, "main"); + } catch (IOException e) { + throw new CoreException(new Status(IStatus.ERROR, Activator.ID, "Failed to start", e)); + } } } diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtBuildConfiguration.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtBuildConfiguration.java index 0cc6762955a..fff397ac7c9 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtBuildConfiguration.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtBuildConfiguration.java @@ -29,6 +29,10 @@ import org.eclipse.cdt.core.parser.IScannerInfo; import org.eclipse.cdt.internal.qt.core.Activator; import org.eclipse.core.resources.IBuildConfiguration; import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; import org.osgi.service.prefs.BackingStoreException; import org.osgi.service.prefs.Preferences; @@ -107,6 +111,48 @@ public class QtBuildConfiguration extends CBuildConfiguration { } } + public Path getProgramPath() throws CoreException { + switch (Platform.getOS()) { + case Platform.OS_MACOSX: + // TODO this is mac local specific and really should be + // in the config + // TODO also need to pull the app name out of the pro + // file name + Path appFolder = getBuildDirectory().resolve("main.app"); + Path contentsFolder = appFolder.resolve("Contents"); + Path macosFolder = contentsFolder.resolve("MacOS"); + return macosFolder.resolve("main"); + case Platform.OS_WIN32: + Path releaseFolder = getBuildDirectory().resolve("release"); + return releaseFolder.resolve("main.exe"); + default: + throw new CoreException( + new Status(IStatus.ERROR, Activator.ID, "platform not supported: " + Platform.getOS())); + } + } + + public void setProgramEnvironment(Map env) { + Path libPath = getQtInstall().getLibPath(); + switch (Platform.getOS()) { + case Platform.OS_MACOSX: + String libPathEnv = env.get("DYLD_LIBRARY_PATH"); + if (libPathEnv == null) { + libPathEnv = libPath.toString(); + } else { + libPathEnv = libPath.toString() + File.pathSeparator + libPathEnv; + } + env.put("DYLD_LIBRARY_PATH", libPathEnv); + break; + case Platform.OS_WIN32: + String path = env.get("PATH"); + // TODO really need a bin path + // and resolve doesn't work properly on Windows + path = "C:/Qt/5.5/mingw492_32/bin;" + path; + env.put("PATH", path); + break; + } + } + public String getProperty(String key) { if (properties == null) { List cmd = new ArrayList<>(); diff --git a/qt/org.eclipse.cdt.qt.ui/plugin.xml b/qt/org.eclipse.cdt.qt.ui/plugin.xml index 327cb18b55b..476e89cc8db 100644 --- a/qt/org.eclipse.cdt.qt.ui/plugin.xml +++ b/qt/org.eclipse.cdt.qt.ui/plugin.xml @@ -86,6 +86,13 @@ class="org.eclipse.cdt.internal.qt.ui.launch.QtLocalLaunchConfigurationTabGroup" id="org.eclipse.cdt.qt.ui.launchConfigurationTabGroup" type="org.eclipse.cdt.qt.core.launchConfigurationType"> + + + +