diff --git a/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/GdbAdapterFactory.java b/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/GdbAdapterFactory.java index 0936f428377..4e55d701597 100644 --- a/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/GdbAdapterFactory.java +++ b/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/GdbAdapterFactory.java @@ -14,6 +14,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import org.eclipse.cdt.debug.core.model.IRestart; import org.eclipse.core.runtime.IAdapterFactory; import org.eclipse.dd.dsf.concurrent.Immutable; import org.eclipse.dd.dsf.concurrent.ThreadSafe; @@ -28,6 +29,7 @@ import org.eclipse.dd.dsf.service.DsfSession; import org.eclipse.dd.gdb.internal.provisional.launching.GdbLaunch; import org.eclipse.dd.gdb.internal.provisional.launching.GdbLaunchDelegate; import org.eclipse.dd.gdb.internal.ui.actions.DsfTerminateCommand; +import org.eclipse.dd.gdb.internal.ui.actions.GdbRestartCommand; import org.eclipse.dd.gdb.internal.ui.viewmodel.GdbViewModelAdapter; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; @@ -67,6 +69,7 @@ public class GdbAdapterFactory final DsfStepReturnCommand fStepReturnCommand; final DsfSuspendCommand fSuspendCommand; final DsfResumeCommand fResumeCommand; + final GdbRestartCommand fRestartCommand; final DsfTerminateCommand fTerminateCommand; final IDebugModelProvider fDebugModelProvider; final DsfSuspendTrigger fSuspendTrigger; @@ -89,6 +92,7 @@ public class GdbAdapterFactory fStepReturnCommand = new DsfStepReturnCommand(session); fSuspendCommand = new DsfSuspendCommand(session); fResumeCommand = new DsfResumeCommand(session); + fRestartCommand = new GdbRestartCommand(session, fLaunch); fTerminateCommand = new DsfTerminateCommand(session); fSuspendTrigger = new DsfSuspendTrigger(session, fLaunch); session.registerModelAdapter(IStepIntoHandler.class, fStepIntoCommand); @@ -96,6 +100,7 @@ public class GdbAdapterFactory session.registerModelAdapter(IStepReturnHandler.class, fStepReturnCommand); session.registerModelAdapter(ISuspendHandler.class, fSuspendCommand); session.registerModelAdapter(IResumeHandler.class, fResumeCommand); + session.registerModelAdapter(IRestart.class, fRestartCommand); session.registerModelAdapter(ITerminateHandler.class, fTerminateCommand); fDebugModelProvider = new IDebugModelProvider() { @@ -127,12 +132,14 @@ public class GdbAdapterFactory session.unregisterModelAdapter(IStepReturnHandler.class); session.unregisterModelAdapter(ISuspendHandler.class); session.unregisterModelAdapter(IResumeHandler.class); + session.unregisterModelAdapter(IRestart.class); session.unregisterModelAdapter(ITerminateHandler.class); fStepIntoCommand.dispose(); fStepOverCommand.dispose(); fStepReturnCommand.dispose(); fSuspendCommand.dispose(); fResumeCommand.dispose(); + fRestartCommand.dispose(); fTerminateCommand.dispose(); fSuspendTrigger.dispose(); } diff --git a/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/actions/GdbRestartCommand.java b/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/actions/GdbRestartCommand.java new file mode 100644 index 00000000000..d1edb35d70f --- /dev/null +++ b/plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/actions/GdbRestartCommand.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2006 Wind River 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 + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.dd.gdb.internal.ui.actions; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.cdt.debug.core.model.IRestart; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; +import org.eclipse.dd.dsf.concurrent.DsfExecutor; +import org.eclipse.dd.dsf.concurrent.Query; +import org.eclipse.dd.dsf.concurrent.RequestMonitor; +import org.eclipse.dd.dsf.service.DsfServicesTracker; +import org.eclipse.dd.dsf.service.DsfSession; +import org.eclipse.dd.gdb.internal.provisional.launching.GdbLaunch; +import org.eclipse.dd.gdb.internal.provisional.service.command.GDBControl; +import org.eclipse.dd.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.model.IProcess; + +public class GdbRestartCommand implements IRestart { + private final DsfExecutor fExecutor; + private final DsfServicesTracker fTracker; + private final GdbLaunch fLaunch; + + public GdbRestartCommand(DsfSession session, GdbLaunch launch) { + fExecutor = session.getExecutor(); + fLaunch = launch; + fTracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), session.getId()); + } + + public void dispose() { + fTracker.dispose(); + } + + // Run control may not be available after a connection is terminated and shut down. + public boolean canRestart() { + Query canRestart = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + GDBControl gdbControl = fTracker.getService(GDBControl.class); + if (gdbControl != null) { + rm.setData(gdbControl.canRestart()); + } else { + rm.setData(false); + } + + rm.done(); + } + }; + + fExecutor.execute(canRestart); + try { + return canRestart.get(); + } catch (InterruptedException e1) { + } catch (ExecutionException e1) { + } + return false; + } + + + public void restart() throws DebugException + { + final AtomicReference execPathRef = new AtomicReference(); + Query restartQuery = new Query() { + @Override + protected void execute(final DataRequestMonitor rm) { + final GDBControl gdbControl = fTracker.getService(GDBControl.class); + if (gdbControl != null) { + execPathRef.set(gdbControl.getExecutablePath()); + gdbControl.initInferiorInputOutput(new RequestMonitor(fExecutor, rm) { + @Override + protected void handleSuccess() { + gdbControl.createInferiorProcess(); + gdbControl.getCLICommandProcessor().resetInferior(gdbControl.getInferiorProcess()); + gdbControl.restart(fLaunch, rm); + } + }); + } else { + rm.done(); + } + } + }; + + fExecutor.execute(restartQuery); + try { + restartQuery.get(); + } catch (InterruptedException e1) { + } catch (ExecutionException e1) { + } + + // Now that we restarted the inferior, we must add it to our launch + // we must do this here because we cannot do it in the executor, or else + // it deadlocks + // We must first remove the old inferior from our launch (since it uses + // the same name and we use that name to find the old one) + // + // Remove + String inferiorLabel = execPathRef.get().lastSegment(); + + IProcess[] launchProcesses = fLaunch.getProcesses(); + for (IProcess p : launchProcesses) { + if (p.getLabel().equals(inferiorLabel)) { + fLaunch.removeProcess(p); + break; + } + } + // Add + try { + fLaunch.addInferiorProcess(inferiorLabel); + } catch (CoreException e) { + throw new DebugException(e.getStatus()); + } + } +} + diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/FinalLaunchSequence.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/FinalLaunchSequence.java index b6e345bca81..ac4e421aef6 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/FinalLaunchSequence.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/FinalLaunchSequence.java @@ -13,7 +13,6 @@ package org.eclipse.dd.gdb.internal.provisional.launching; import java.util.ArrayList; import java.util.List; -import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector; import org.eclipse.cdt.debug.mi.core.IGDBServerMILaunchConfigurationConstants; import org.eclipse.cdt.debug.mi.core.IMILaunchConfigurationConstants; @@ -25,28 +24,19 @@ import org.eclipse.dd.dsf.concurrent.DataRequestMonitor; import org.eclipse.dd.dsf.concurrent.DsfExecutor; import org.eclipse.dd.dsf.concurrent.RequestMonitor; import org.eclipse.dd.dsf.concurrent.Sequence; -import org.eclipse.dd.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; -import org.eclipse.dd.dsf.debug.service.IRunControl.IContainerDMContext; import org.eclipse.dd.dsf.service.DsfServicesTracker; import org.eclipse.dd.gdb.internal.GdbPlugin; import org.eclipse.dd.gdb.internal.provisional.service.command.GDBControl; import org.eclipse.dd.gdb.internal.provisional.service.command.GDBControl.SessionType; import org.eclipse.dd.mi.service.CSourceLookup; import org.eclipse.dd.mi.service.MIBreakpointsManager; +import org.eclipse.dd.mi.service.command.commands.CLIAttach; import org.eclipse.dd.mi.service.command.commands.CLISource; -import org.eclipse.dd.mi.service.command.commands.MIBreakInsert; -import org.eclipse.dd.mi.service.command.commands.MICommand; -import org.eclipse.dd.mi.service.command.commands.MIExecContinue; -import org.eclipse.dd.mi.service.command.commands.MIExecRun; import org.eclipse.dd.mi.service.command.commands.MIFileExecAndSymbols; import org.eclipse.dd.mi.service.command.commands.MIGDBSetAutoSolib; import org.eclipse.dd.mi.service.command.commands.MIGDBSetSolibSearchPath; -import org.eclipse.dd.mi.service.command.commands.MIInferiorTTYSet; -import org.eclipse.dd.mi.service.command.commands.CLIAttach; import org.eclipse.dd.mi.service.command.commands.MITargetSelect; -import org.eclipse.dd.mi.service.command.output.MIBreakInsertInfo; import org.eclipse.dd.mi.service.command.output.MIInfo; -import org.eclipse.debug.core.DebugException; public class FinalLaunchSequence extends Sequence { @@ -58,41 +48,13 @@ public class FinalLaunchSequence extends Sequence { public void execute(RequestMonitor requestMonitor) { DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fLaunch.getSession().getId()); fCommandControl = tracker.getService(GDBControl.class); + if (fCommandControl == null) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain GDBControl service", null)); //$NON-NLS-1$ + } tracker.dispose(); requestMonitor.done(); }}, - /* - * Specify connection of inferior input/output with a terminal. - */ - new Step() { @Override - public void execute(RequestMonitor requestMonitor) { - if (fSessionType == SessionType.ATTACH) { - requestMonitor.done(); - return; - } - - try { - boolean useTerminal = fLaunch.getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_USE_TERMINAL, true); - - if (useTerminal) { - String pytName = fCommandControl.getPtyName(); - if ( pytName != null ) { - fCommandControl.queueCommand( - new MIInferiorTTYSet(fCommandControl.getControlDMContext(), pytName), - new DataRequestMonitor(getExecutor(), requestMonitor)); - } - else { - requestMonitor.done(); - } - } else { - requestMonitor.done(); - } - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get terminal option", e)); //$NON-NLS-1$ - requestMonitor.done(); - } - }}, /* * Source the gdbinit file specified in the launch */ @@ -278,7 +240,6 @@ public class FinalLaunchSequence extends Sequence { } else { requestMonitor.done(); } - } }, /* @@ -292,74 +253,12 @@ public class FinalLaunchSequence extends Sequence { bpmService.startTrackingBreakpoints(fCommandControl.getGDBDMContext(), requestMonitor); }}, /* - * If needed, insert breakpoint at main and run to it. + * Start the program. */ new Step() { - private boolean fStopInMain = false; - private String fStopSymbol = null; - - /** - * @return The return value actually indicates whether the get operation succeeded, - * not whether to stop. - */ - private boolean readStopAtMain(RequestMonitor requestMonitor) { - try { - fStopInMain = fLaunch.getLaunchConfiguration().getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, false ); - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve the entry point symbol", e)); //$NON-NLS-1$ - requestMonitor.done(); - return false; - } - return true; - } - - private boolean readStopSymbol(RequestMonitor requestMonitor) { - try { - fStopSymbol = fLaunch.getLaunchConfiguration().getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT ); - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.CONFIGURATION_INVALID, "Cannot retrieve the entry point symbol", e)); //$NON-NLS-1$ - requestMonitor.done(); - return false; - } - return true; - } - @Override public void execute(final RequestMonitor requestMonitor) { - if (fSessionType == SessionType.ATTACH) { - requestMonitor.done(); - return; - } - - final MICommand execCommand; - if (fSessionType == SessionType.REMOTE) { - // When doing remote debugging, we use -exec-continue instead of -exec-run - execCommand = new MIExecContinue((IContainerDMContext)fCommandControl.getControlDMContext()); - } else { - execCommand = new MIExecRun((IContainerDMContext)fCommandControl.getControlDMContext(), new String[0]); - } - - if (!readStopAtMain(requestMonitor)) return; - if (!fStopInMain) { - // Just start the program. - fCommandControl.queueCommand(execCommand, new DataRequestMonitor(getExecutor(), requestMonitor)); - } else { - if (!readStopSymbol(requestMonitor)) return; - - // Insert a breakpoint at the requested stop symbol. - fCommandControl.queueCommand( - new MIBreakInsert( - (IBreakpointsTargetDMContext)fCommandControl.getControlDMContext(), - true, false, null, 0, fStopSymbol, 0), - new DataRequestMonitor(getExecutor(), requestMonitor) { - @Override - protected void handleSuccess() { - - // After the break-insert is done, execute the -exec-run or -exec-continue command. - fCommandControl.queueCommand(execCommand, new DataRequestMonitor(getExecutor(), requestMonitor)); - } - }); - } + fCommandControl.start(fLaunch, requestMonitor); } }, }; diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/GdbLaunch.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/GdbLaunch.java index da3885bea29..b5c797a9ae4 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/GdbLaunch.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/GdbLaunch.java @@ -10,7 +10,10 @@ *******************************************************************************/ package org.eclipse.dd.gdb.internal.provisional.launching; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicReference; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; @@ -31,7 +34,10 @@ import org.eclipse.dd.dsf.service.DsfServicesTracker; import org.eclipse.dd.dsf.service.DsfSession; import org.eclipse.dd.gdb.internal.GdbPlugin; import org.eclipse.dd.gdb.internal.provisional.service.command.GDBControl; +import org.eclipse.dd.mi.service.command.AbstractCLIProcess; +import org.eclipse.dd.mi.service.command.MIInferiorProcess; import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.Launch; import org.eclipse.debug.core.model.ISourceLocator; @@ -91,6 +97,54 @@ public class GdbLaunch extends Launch public DsfSession getSession() { return fSession; } + public void addInferiorProcess(String label) throws CoreException { + try { + // Add the "inferior" process object to the launch. + final AtomicReference inferiorProcessRef = new AtomicReference(); + getDsfExecutor().submit( new Callable() { + public Object call() throws CoreException { + GDBControl gdb = fTracker.getService(GDBControl.class); + if (gdb != null) { + inferiorProcessRef.set(gdb.getInferiorProcess()); + } + return null; + } + }).get(); + + DebugPlugin.newProcess(this, inferiorProcessRef.get(), label); + } 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 addCLIProcess(String label) throws CoreException { + try { + // Add the CLI process object to the launch. + final AtomicReference cliProcessRef = new AtomicReference(); + getDsfExecutor().submit( new Callable() { + public Object call() throws CoreException { + GDBControl gdb = fTracker.getService(GDBControl.class); + if (gdb != null) { + cliProcessRef.set(gdb.getCLIProcess()); + } + return null; + } + }).get(); + + DebugPlugin.newProcess(this, cliProcessRef.get(), label); + } 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$ + } + } + /////////////////////////////////////////////////////////////////////////// // IServiceEventListener @DsfServiceEventHandler public void eventDispatched(GDBControl.ExitedEvent event) { diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/GdbLaunchDelegate.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/GdbLaunchDelegate.java index ded518d0d37..e589ee9310a 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/GdbLaunchDelegate.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/GdbLaunchDelegate.java @@ -15,7 +15,6 @@ import java.io.IOException; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.atomic.AtomicReference; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; @@ -38,8 +37,6 @@ import org.eclipse.dd.gdb.internal.GdbPlugin; import org.eclipse.dd.gdb.internal.provisional.IGDBLaunchConfigurationConstants; import org.eclipse.dd.gdb.internal.provisional.service.command.GDBControl; import org.eclipse.dd.gdb.internal.provisional.service.command.GDBControl.SessionType; -import org.eclipse.dd.mi.service.command.AbstractCLIProcess; -import org.eclipse.dd.mi.service.command.MIInferiorProcess; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; @@ -155,41 +152,22 @@ public class GdbLaunchDelegate extends AbstractCLaunchDelegate throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, "Error in services launch sequence", e1.getCause())); //$NON-NLS-1$ } + // The initializeControl method should be called after the GdbControl class has + // be initialized (in the ServicesLaunchSequence above.) This is because it is the + // GdbControl class that will trigger the launch cleanup through a GDBControl.ExitedEvent launch.initializeControl(); // Add the CLI and "inferior" process objects to the launch. - final AtomicReference cliProcessRef = new AtomicReference(); - final AtomicReference inferiorProcessRef = new AtomicReference(); - try { - launch.getDsfExecutor().submit( new Callable() { - public Object call() throws CoreException { - DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), launch.getSession().getId()); - GDBControl gdb = tracker.getService(GDBControl.class); - if (gdb != null) { - cliProcessRef.set(gdb.getCLIProcess()); - inferiorProcessRef.set(gdb.getInferiorProcess()); - } - tracker.dispose(); - return null; - } - }).get(); - launch.addProcess(DebugPlugin.newProcess(launch, cliProcessRef.get(), "gdb")); //$NON-NLS-1$ - launch.addProcess(DebugPlugin.newProcess(launch, inferiorProcessRef.get(), exePath.lastSegment())); - } 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$ - } - + launch.addCLIProcess("gdb"); //$NON-NLS-1$ + launch.addInferiorProcess(exePath.lastSegment()); + // Create and invoke the final launch sequence to setup GDB final FinalLaunchSequence finalLaunchSequence; if (fSessionType == SessionType.ATTACH) { finalLaunchSequence = new FinalLaunchSequence(launch.getSession().getExecutor(), launch, pid); } else { finalLaunchSequence = new FinalLaunchSequence(launch.getSession().getExecutor(), launch, fSessionType); - } + } launch.getSession().getExecutor().execute(finalLaunchSequence); try { finalLaunchSequence.get(); @@ -332,7 +310,7 @@ public class GdbLaunchDelegate extends AbstractCLaunchDelegate // the source lookup adapter. ISourceLocator locator = getSourceLocator(configuration); - return new GdbLaunch(configuration, mode, locator); + return new GdbLaunch(configuration, mode, locator); } private ISourceLocator getSourceLocator(ILaunchConfiguration configuration) throws CoreException { diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/ServicesLaunchSequence.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/ServicesLaunchSequence.java index f267b647282..8dc218b1a26 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/ServicesLaunchSequence.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/launching/ServicesLaunchSequence.java @@ -43,11 +43,9 @@ public class ServicesLaunchSequence extends Sequence { @Override public void execute(RequestMonitor requestMonitor) { String debugMode = ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN; - boolean useTerminal = true; try { debugMode = fLaunch.getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN ); - useTerminal = fLaunch.getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_USE_TERMINAL, true); } catch (CoreException e) { } @@ -66,7 +64,7 @@ public class ServicesLaunchSequence extends Sequence { // // Create the connection. // - fCommandControl = new GDBControl(fSession, getGDBPath(), fExecPath, fSessionType, useTerminal, 30); + fCommandControl = new GDBControl(fSession, getGDBPath(), fExecPath, fSessionType, 30); fCommandControl.initialize(requestMonitor); } }, diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBControl.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBControl.java index 49cd3bcff93..650fb83207a 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBControl.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/command/GDBControl.java @@ -23,9 +23,11 @@ import java.util.List; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.utils.pty.PTY; import org.eclipse.cdt.utils.spawner.ProcessFactory; import org.eclipse.cdt.utils.spawner.Spawner; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; @@ -41,13 +43,20 @@ import org.eclipse.dd.dsf.debug.service.command.ICommandControl; import org.eclipse.dd.dsf.service.DsfServiceEventHandler; import org.eclipse.dd.dsf.service.DsfSession; import org.eclipse.dd.gdb.internal.GdbPlugin; +import org.eclipse.dd.gdb.internal.provisional.launching.GdbLaunch; import org.eclipse.dd.mi.service.command.AbstractCLIProcess; import org.eclipse.dd.mi.service.command.AbstractMIControl; import org.eclipse.dd.mi.service.command.CLIEventProcessor; import org.eclipse.dd.mi.service.command.MIControlDMContext; import org.eclipse.dd.mi.service.command.MIInferiorProcess; import org.eclipse.dd.mi.service.command.MIRunControlEventProcessor; +import org.eclipse.dd.mi.service.command.commands.MIBreakInsert; +import org.eclipse.dd.mi.service.command.commands.MICommand; +import org.eclipse.dd.mi.service.command.commands.MIExecContinue; +import org.eclipse.dd.mi.service.command.commands.MIExecRun; import org.eclipse.dd.mi.service.command.commands.MIGDBExit; +import org.eclipse.dd.mi.service.command.commands.MIInferiorTTYSet; +import org.eclipse.dd.mi.service.command.output.MIBreakInsertInfo; import org.eclipse.dd.mi.service.command.output.MIInfo; import org.eclipse.debug.core.DebugException; import org.osgi.framework.BundleContext; @@ -100,17 +109,15 @@ public class GDBControl extends AbstractMIControl { private AbstractCLIProcess fCLIProcess; private MIInferiorProcess fInferiorProcess = null; - boolean fUseTerminal; private PTY fPty; - public GDBControl(DsfSession session, IPath gdbPath, IPath execPath, SessionType type, boolean useTerminal, int gdbLaunchTimeout) { + public GDBControl(DsfSession session, IPath gdbPath, IPath execPath, SessionType type, int gdbLaunchTimeout) { super(session); fSessionType = type; fGdbPath = gdbPath; fExecPath = execPath; fGDBLaunchTimeout = gdbLaunchTimeout; fControlDmc = new GDBControlDMContext(session.getId(), getClass().getName() + ":" + ++fgInstanceCounter); //$NON-NLS-1$ - fUseTerminal = useTerminal; } @Override @@ -133,6 +140,7 @@ public class GDBControl extends AbstractMIControl { new GDBProcessStep(InitializationShutdownStep.Direction.INITIALIZING), new MonitorJobStep(InitializationShutdownStep.Direction.INITIALIZING), new CommandMonitoringStep(InitializationShutdownStep.Direction.INITIALIZING), + new InferiorInputOutputInitStep(InitializationShutdownStep.Direction.INITIALIZING), new CommandProcessorsStep(InitializationShutdownStep.Direction.INITIALIZING), new RegisterStep(InitializationShutdownStep.Direction.INITIALIZING), }; @@ -148,6 +156,7 @@ public class GDBControl extends AbstractMIControl { final Sequence.Step[] shutdownSteps = new Sequence.Step[] { new RegisterStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), new CommandProcessorsStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), + new InferiorInputOutputInitStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), new CommandMonitoringStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), new MonitorJobStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), new GDBProcessStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), @@ -228,6 +237,136 @@ public class GDBControl extends AbstractMIControl { } ); } + + /* + * This method does the necessary work to setup the input/output streams for the + * inferior process, by either preparing the PTY to be used, to simply leaving + * the PTY null, which indicates that the input/output streams of the CLI shoud + * be used instead; this decision is based on the type of session. + */ + public void initInferiorInputOutput(final RequestMonitor requestMonitor) { + if (fSessionType == SessionType.ATTACH || fSessionType == SessionType.REMOTE) { + // These types do not use a PTY + fPty = null; + requestMonitor.done(); + } else { + // These types always use a PTY + try { + fPty = new PTY(); + + // Tell GDB to use this PTY + queueCommand( + new MIInferiorTTYSet(fControlDmc, fPty.getSlaveName()), + new DataRequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleFailure() { + // We were not able to tell GDB to use the PTY + // so we won't use it at all. + fPty = null; + requestMonitor.done(); + } + }); + } catch (IOException e) { + fPty = null; + requestMonitor.done(); + } + } + } + + + public boolean canRestart() { + if (fSessionType == SessionType.ATTACH) return false; + + // Before GDB6.8, the Linux gdbserver would restart a new + // process when getting a -exec-run but the communication + // with GDB had a bug and everything hung. + // with GDB6.8 the program restarts properly one time, + // but on a second attempt, gdbserver crashes. + // So, lets just turn off the Restart for Remote debugging + if (fSessionType == SessionType.REMOTE) return false; + + return true; + } + + /* + * Start the program. + */ + public void start(GdbLaunch launch, final RequestMonitor requestMonitor) { + startOrRestart(launch, false, requestMonitor); + } + + /* + * Before restarting the inferior, we must re-initialize its input/output streams + * and create a new inferior process object. Then we can restart the inferior. + */ + public void restart(final GdbLaunch launch, final RequestMonitor requestMonitor) { + startOrRestart(launch, true, requestMonitor); + } + + /* + * Insert breakpoint at entry if set, and start or restart the program. + */ + protected void startOrRestart(final GdbLaunch launch, boolean restart, final RequestMonitor requestMonitor) { + if (fSessionType == SessionType.ATTACH) { + // When attaching to a running process, we do not need to set a breakpoint or + // start the program; it is left up to the user. + requestMonitor.done(); + return; + } + + final MICommand execCommand; + if (fSessionType == SessionType.REMOTE) { + // When doing remote debugging, we use -exec-continue instead of -exec-run + execCommand = new MIExecContinue(fControlDmc); + } else { + execCommand = new MIExecRun(fControlDmc, new String[0]); + } + + boolean stopInMain = false; + try { + stopInMain = launch.getLaunchConfiguration().getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, false ); + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve stop at entry point boolean", e)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } + + if (!stopInMain) { + // Just start the program. + queueCommand(execCommand, new DataRequestMonitor(getExecutor(), requestMonitor)); + } else { + String stopSymbol = null; + try { + stopSymbol = launch.getLaunchConfiguration().getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT ); + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.CONFIGURATION_INVALID, "Cannot retrieve the entry point symbol", e)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } + + // Insert a breakpoint at the requested stop symbol. + queueCommand( + new MIBreakInsert(fControlDmc, true, false, null, 0, stopSymbol, 0), + new DataRequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + // After the break-insert is done, execute the -exec-run or -exec-continue command. + queueCommand(execCommand, new DataRequestMonitor(getExecutor(), requestMonitor)); + } + }); + } + } + + /* + * This method creates a new inferior process object based on the current Pty or output stream. + */ + public void createInferiorProcess() { + if (fPty == null) { + fInferiorProcess = new GDBInferiorProcess(GDBControl.this, fProcess.getOutputStream()); + } else { + fInferiorProcess = new GDBInferiorProcess(GDBControl.this, fPty); + } + } public boolean isConnected() { return fInferiorProcess.getState() != MIInferiorProcess.State.TERMINATED && fConnected; @@ -249,6 +388,10 @@ public class GDBControl extends AbstractMIControl { return fInferiorProcess; } + public CLIEventProcessor getCLICommandProcessor() { + return fCLICommandProcessor; + } + public boolean isGDBExited() { return fMonitorJob != null && fMonitorJob.fExited; } @@ -261,15 +404,6 @@ public class GDBControl extends AbstractMIControl { public void getInferiorProcessId(DataRequestMonitor rm) { } - - public String getPtyName() { - if ( fPty != null ) { - return fPty.getSlaveName(); - } - else { - return null; - } - } @DsfServiceEventHandler public void eventDispatched(ExitedEvent e) { @@ -517,6 +651,20 @@ public class GDBControl extends AbstractMIControl { requestMonitor.done(); } } + + protected class InferiorInputOutputInitStep extends InitializationShutdownStep { + InferiorInputOutputInitStep(Direction direction) { super(direction); } + + @Override + protected void initialize(final RequestMonitor requestMonitor) { + initInferiorInputOutput(requestMonitor); + } + + @Override + protected void shutdown(RequestMonitor requestMonitor) { + requestMonitor.done(); + } + } protected class CommandProcessorsStep extends InitializationShutdownStep { CommandProcessorsStep(Direction direction) { super(direction); } @@ -532,18 +680,8 @@ public class GDBControl extends AbstractMIControl { return; } - if (fUseTerminal) { - try { - fPty = new PTY(); - fInferiorProcess = new GDBInferiorProcess(GDBControl.this, fPty); - } catch (IOException e) { - } - } + createInferiorProcess(); - // If !fUseTerminal or IOException was caught - if (fInferiorProcess == null) - fInferiorProcess = new GDBInferiorProcess(GDBControl.this, fProcess.getOutputStream()); - fCLICommandProcessor = new CLIEventProcessor(GDBControl.this, fControlDmc, fInferiorProcess); fMIEventProcessor = new MIRunControlEventProcessor(GDBControl.this, fControlDmc); diff --git a/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/CLIEventProcessor.java b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/CLIEventProcessor.java index 9beca3818fb..ee1ca9735b6 100644 --- a/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/CLIEventProcessor.java +++ b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/CLIEventProcessor.java @@ -50,7 +50,7 @@ public class CLIEventProcessor implements ICommandListener, IEventListener { private final AbstractMIControl fCommandControl; - private final MIInferiorProcess fInferior; + private MIInferiorProcess fInferior; private final IContainerDMContext fContainerDmc; private final List fEventList = new LinkedList(); @@ -73,6 +73,10 @@ public class CLIEventProcessor fCommandControl.removeEventListener(this); } + public void resetInferior(MIInferiorProcess inferior) { + fInferior = inferior; + } + public void commandSent(ICommandToken token) { if (token.getCommand() instanceof CLICommand) { processStateChanges( (CLICommand)token.getCommand() ); diff --git a/plugins/org.eclipse.dd.tests.gdb/src/org/eclipse/dd/tests/gdb/framework/BaseTestCase.java b/plugins/org.eclipse.dd.tests.gdb/src/org/eclipse/dd/tests/gdb/framework/BaseTestCase.java index cc3fe0ab2cc..5cce87ca0a3 100644 --- a/plugins/org.eclipse.dd.tests.gdb/src/org/eclipse/dd/tests/gdb/framework/BaseTestCase.java +++ b/plugins/org.eclipse.dd.tests.gdb/src/org/eclipse/dd/tests/gdb/framework/BaseTestCase.java @@ -54,7 +54,6 @@ public class BaseTestCase { // Setup information for the launcher attrs.put(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, DEFAULT_TEST_APP); - attrs.put(ICDTLaunchConfigurationConstants.ATTR_USE_TERMINAL, true); attrs.put(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, true); attrs.put(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT); attrs.put(IMILaunchConfigurationConstants.ATTR_DEBUG_NAME, "gdb"); diff --git a/plugins/org.eclipse.dd.tests.gdb/src/org/eclipse/dd/tests/gdb/launching/TestLaunchDelegate.java b/plugins/org.eclipse.dd.tests.gdb/src/org/eclipse/dd/tests/gdb/launching/TestLaunchDelegate.java index 19b72fb7cd6..89c266f6f17 100644 --- a/plugins/org.eclipse.dd.tests.gdb/src/org/eclipse/dd/tests/gdb/launching/TestLaunchDelegate.java +++ b/plugins/org.eclipse.dd.tests.gdb/src/org/eclipse/dd/tests/gdb/launching/TestLaunchDelegate.java @@ -17,7 +17,6 @@ import java.io.IOException; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.atomic.AtomicReference; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.launch.AbstractCLaunchDelegate; @@ -41,8 +40,6 @@ import org.eclipse.dd.gdb.internal.provisional.launching.GdbLaunch; import org.eclipse.dd.gdb.internal.provisional.launching.ServicesLaunchSequence; import org.eclipse.dd.gdb.internal.provisional.service.command.GDBControl; import org.eclipse.dd.gdb.internal.provisional.service.command.GDBControl.SessionType; -import org.eclipse.dd.mi.service.command.AbstractCLIProcess; -import org.eclipse.dd.mi.service.command.MIInferiorProcess; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; @@ -123,34 +120,12 @@ public class TestLaunchDelegate extends AbstractCLaunchDelegate launch.initializeControl(); // Add the CLI and "inferior" process objects to the launch. - final AtomicReference cliProcessRef = new AtomicReference(); - final AtomicReference inferiorProcessRef = new AtomicReference(); - try { - launch.getDsfExecutor().submit( new Callable() { - public Object call() throws CoreException { - DsfServicesTracker tracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), launch.getSession().getId()); - GDBControl gdb = tracker.getService(GDBControl.class); - if (gdb != null) { - cliProcessRef.set(gdb.getCLIProcess()); - inferiorProcessRef.set(gdb.getInferiorProcess()); - } - tracker.dispose(); - return null; - } - }).get(); - launch.addProcess(DebugPlugin.newProcess(launch, cliProcessRef.get(), "gdb")); //$NON-NLS-1$ - launch.addProcess(DebugPlugin.newProcess(launch, inferiorProcessRef.get(), exePath.lastSegment())); - } catch (InterruptedException e) { - throw new CoreException(new Status(IStatus.ERROR, TestsPlugin.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, TestsPlugin.PLUGIN_ID, 0, "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$ - } - + launch.addCLIProcess("gdb"); //$NON-NLS-1$ + launch.addInferiorProcess(exePath.lastSegment()); + // Create and invoke the final launch sequence to setup GDB final FinalLaunchSequence finalLaunchSequence = - new FinalLaunchSequence(launch.getSession().getExecutor(), launch, SessionType.RUN); + new FinalLaunchSequence(launch.getSession().getExecutor(), launch, SessionType.RUN); launch.getSession().getExecutor().execute(finalLaunchSequence); try { finalLaunchSequence.get();