From 241d941438362e87f6363b90a12e2346aa762208 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Mon, 21 Apr 2008 18:08:53 +0000 Subject: [PATCH] Bug 226931 Support for Restart button. The steps to restarting the inferior are the following: 1- Create a new PTY and tell GDB to use it 2- Create a new MIInferiorProcess object which uses the new PTY 2.5- Have the CLIEventProcessor use the new MIInferiorProcess 3- Restart the inferior using -exec-run 4- Remove the previous inferior Process from the launch 5- Add the new inferior Process to the launch (which will trigger the use of the new PTY streams) This change supports the Restart function, including the above steps to perform the proper cleanup. The code to start the inferior has been extracted from the FinalLaunchSequence and put in GDBControl to allow sharing between start and restart. Also, the code to create the CLI and inferior process objects has been extracted from the GdbLaunchDelegate and put in GDBControl to to allow sharing between start and restart. There only interface change that is not in a provisional interface is the addition of resetInferior() to CLIEventprocessor which is backwards compatible. --- .../dd/gdb/internal/ui/GdbAdapterFactory.java | 7 + .../ui/actions/GdbRestartCommand.java | 126 ++++++++++++ .../launching/FinalLaunchSequence.java | 113 +---------- .../provisional/launching/GdbLaunch.java | 54 +++++ .../launching/GdbLaunchDelegate.java | 38 +--- .../launching/ServicesLaunchSequence.java | 4 +- .../service/command/GDBControl.java | 184 +++++++++++++++--- .../mi/service/command/CLIEventProcessor.java | 6 +- .../dd/tests/gdb/framework/BaseTestCase.java | 1 - .../gdb/launching/TestLaunchDelegate.java | 33 +--- 10 files changed, 372 insertions(+), 194 deletions(-) create mode 100644 plugins/org.eclipse.dd.gdb.ui/src/org/eclipse/dd/gdb/internal/ui/actions/GdbRestartCommand.java 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();