diff options
author | Marc Khouzam | 2009-06-04 14:53:17 +0000 |
---|---|---|
committer | Marc Khouzam | 2009-06-04 14:53:17 +0000 |
commit | e9ab9678011e9a52836d8c3f455493e72dbbb972 (patch) | |
tree | 2883de48c1d45712dd6ca019f318fc88eb8a2d6a /dsf-gdb | |
parent | 4f7831b525c085064f23026d101b2d7174d13e0f (diff) | |
download | org.eclipse.cdt-e9ab9678011e9a52836d8c3f455493e72dbbb972.tar.gz org.eclipse.cdt-e9ab9678011e9a52836d8c3f455493e72dbbb972.tar.xz org.eclipse.cdt-e9ab9678011e9a52836d8c3f455493e72dbbb972.zip |
Missing @since tags
Diffstat (limited to 'dsf-gdb')
2 files changed, 1218 insertions, 1186 deletions
diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl.java index 005bf5983fd..c6360bc632c 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl.java @@ -1,524 +1,538 @@ -/******************************************************************************* - * Copyright (c) 2006, 2008 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 - * Ericsson - Modified for additional features in DSF Reference implementation - * Nokia - create and use backend service. - *******************************************************************************/ -package org.eclipse.cdt.dsf.gdb.service.command; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.Hashtable; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; -import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; -import org.eclipse.cdt.dsf.concurrent.DsfRunnable; -import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; -import org.eclipse.cdt.dsf.concurrent.RequestMonitor; -import org.eclipse.cdt.dsf.concurrent.Sequence; -import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; -import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; -import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; -import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; -import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; -import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; -import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; -import org.eclipse.cdt.dsf.gdb.service.IGDBBackend; -import org.eclipse.cdt.dsf.gdb.service.SessionType; -import org.eclipse.cdt.dsf.mi.service.IMIBackend; -import org.eclipse.cdt.dsf.mi.service.IMIProcesses; -import org.eclipse.cdt.dsf.mi.service.MIProcesses; -import org.eclipse.cdt.dsf.mi.service.IMIBackend.BackendStateChangedEvent; -import org.eclipse.cdt.dsf.mi.service.MIProcesses.ContainerStartedDMEvent; -import org.eclipse.cdt.dsf.mi.service.command.AbstractCLIProcess; -import org.eclipse.cdt.dsf.mi.service.command.AbstractMIControl; -import org.eclipse.cdt.dsf.mi.service.command.CLIEventProcessor; -import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; -import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess; -import org.eclipse.cdt.dsf.mi.service.command.MIRunControlEventProcessor; -import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess.State; -import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakInsert; -import org.eclipse.cdt.dsf.mi.service.command.commands.MICommand; -import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecContinue; -import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecRun; -import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBExit; -import org.eclipse.cdt.dsf.mi.service.command.commands.MIInferiorTTYSet; -import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo; -import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; -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.pty.PTY; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.debug.core.DebugException; -import org.eclipse.debug.core.ILaunchConfiguration; -import org.osgi.framework.BundleContext; - -/** - * GDB Debugger control implementation. This implementation extends the - * base MI control implementation to provide the GDB-specific debugger - * features. This includes:<br> - * - CLI console support,<br> - * - inferior process status tracking.<br> - */ -public class GDBControl extends AbstractMIControl implements IGDBControl { - - /** - * Event indicating that the back end process has started. - */ - private static class GDBControlInitializedDMEvent extends AbstractDMEvent<ICommandControlDMContext> - implements ICommandControlInitializedDMEvent - { - public GDBControlInitializedDMEvent(ICommandControlDMContext context) { - super(context); - } - } - - /** - * Event indicating that the CommandControl (back end process) has terminated. - */ - private static class GDBControlShutdownDMEvent extends AbstractDMEvent<ICommandControlDMContext> - implements ICommandControlShutdownDMEvent - { - public GDBControlShutdownDMEvent(ICommandControlDMContext context) { - super(context); - } - } - - private GDBControlDMContext fControlDmc; - - private IGDBBackend fMIBackend; - - private boolean fConnected = false; - - private MIRunControlEventProcessor fMIEventProcessor; - private CLIEventProcessor fCLICommandProcessor; - private AbstractCLIProcess fCLIProcess; - private MIInferiorProcess fInferiorProcess = null; - - private PTY fPty; - - public GDBControl(DsfSession session, ILaunchConfiguration config) { - super(session, false); - } - - @Override - protected BundleContext getBundleContext() { - return GdbPlugin.getBundleContext(); - } - - @Override - public void initialize(final RequestMonitor requestMonitor) { - super.initialize( new RequestMonitor(getExecutor(), requestMonitor) { - @Override - protected void handleSuccess() { - doInitialize(requestMonitor); - } - }); - } - - public void doInitialize(final RequestMonitor requestMonitor) { - - fMIBackend = getServicesTracker().getService(IGDBBackend.class); - - // getId uses the MIBackend service, which is why we must wait until we - // have it, before we can create this context. - fControlDmc = new GDBControlDMContext(getSession().getId(), getId()); - - final Sequence.Step[] initializeSteps = new Sequence.Step[] { - new CommandMonitoringStep(InitializationShutdownStep.Direction.INITIALIZING), - new InferiorInputOutputInitStep(InitializationShutdownStep.Direction.INITIALIZING), - new CommandProcessorsStep(InitializationShutdownStep.Direction.INITIALIZING), - new RegisterStep(InitializationShutdownStep.Direction.INITIALIZING), - }; - - Sequence startupSequence = new Sequence(getExecutor(), requestMonitor) { - @Override public Step[] getSteps() { return initializeSteps; } - }; - getExecutor().execute(startupSequence); - } - - @Override - public void shutdown(final RequestMonitor requestMonitor) { - 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), - }; - Sequence shutdownSequence = new Sequence(getExecutor(), requestMonitor) { - @Override public Step[] getSteps() { return shutdownSteps; } - }; - getExecutor().execute(shutdownSequence); - - } - - public String getId() { - return fMIBackend.getId(); - } - - @Override - public MIControlDMContext getControlDMContext() { - return fControlDmc; - } - - public ICommandControlDMContext getContext() { - return fControlDmc; - } - - public void terminate(final RequestMonitor rm) { - // To fix bug 234467: - // Interrupt GDB in case the inferior is running. - // That way, the inferior will also be killed when we exit GDB. - // - if (fInferiorProcess.getState() == State.RUNNING) { - fMIBackend.interrupt(); - } - - // Schedule a runnable to be executed 2 seconds from now. - // If we don't get a response to the quit command, this - // runnable will kill the task. - final Future<?> forceQuitTask = getExecutor().schedule( - new DsfRunnable() { - public void run() { - fMIBackend.destroy(); - rm.done(); - } - - @Override - protected boolean isExecutionRequired() { - return false; - } - }, - 2, TimeUnit.SECONDS); - - queueCommand( - new MIGDBExit(fControlDmc), - new DataRequestMonitor<MIInfo>(getExecutor(), rm) { - @Override - public void handleCompleted() { - if (isSuccess()) { - // Cancel the time out runnable (if it hasn't run yet). - forceQuitTask.cancel(false); - rm.done(); - } - // else: the forceQuitTask has or will handle it. - // It is good to wait for the forceQuitTask to trigger - // to leave enough time for the interrupt() to complete. - } - } - ); - } - - /* - * 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 should - * be used instead; this decision is based on the type of session. - */ - public void initInferiorInputOutput(final RequestMonitor requestMonitor) { - if (fMIBackend.getSessionType() == SessionType.REMOTE || fMIBackend.getIsAttachSession()) { - // 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<MIInfo>(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 (fMIBackend.getIsAttachSession() || fMIBackend.getSessionType() == SessionType.CORE) { - 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 (fMIBackend.getSessionType() == 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 (fMIBackend.getIsAttachSession()) { - // 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; - } - - DsfServicesTracker servicesTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), getSession().getId()); - IMIProcesses procService = servicesTracker.getService(IMIProcesses.class); - servicesTracker.dispose(); - IProcessDMContext procDmc = procService.createProcessContext(fControlDmc, MIProcesses.UNIQUE_GROUP_ID); - final IContainerDMContext containerDmc = procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID); - - final MICommand<MIInfo> execCommand; - if (fMIBackend.getSessionType() == SessionType.REMOTE) { - // When doing remote debugging, we use -exec-continue instead of -exec-run - execCommand = new MIExecContinue(containerDmc); - } else { - execCommand = new MIExecRun(containerDmc, 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; - } - - final DataRequestMonitor<MIInfo> execMonitor = new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor) { - @Override - protected void handleSuccess() { - getSession().dispatchEvent(new ContainerStartedDMEvent(containerDmc), getProperties()); - super.handleSuccess(); - } - }; - - if (!stopInMain) { - // Just start the program. - queueCommand(execCommand, execMonitor); - } 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<MIBreakInsertInfo>(getExecutor(), requestMonitor) { - @Override - protected void handleSuccess() { - // After the break-insert is done, execute the -exec-run or -exec-continue command. - queueCommand(execCommand, execMonitor); - } - }); - } - } - - /* - * 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, fMIBackend, fMIBackend.getMIOutputStream()); - } else { - fInferiorProcess = new GDBInferiorProcess(GDBControl.this, fMIBackend, fPty); - } - } - - public boolean isConnected() { - return fInferiorProcess.getState() != MIInferiorProcess.State.TERMINATED && - (!fMIBackend.getIsAttachSession() || fConnected); - } - - public void setConnected(boolean connected) { - fConnected = connected; - } - - public AbstractCLIProcess getCLIProcess() { - return fCLIProcess; - } - - public MIInferiorProcess getInferiorProcess() { - return fInferiorProcess; - } - - /** - * @since 2.0 - */ - public void setTracingStream(OutputStream tracingStream) { - setMITracingStream(tracingStream); - } - - @DsfServiceEventHandler - public void eventDispatched(ICommandControlShutdownDMEvent e) { - // Handle our "GDB Exited" event and stop processing commands. - stopCommandProcessing(); - } - - @DsfServiceEventHandler - public void eventDispatched(BackendStateChangedEvent e) { - if (e.getState() == IMIBackend.State.TERMINATED && e.getBackendId().equals(fMIBackend.getId())) { - // Handle "GDB Exited" event, just relay to following event. - getSession().dispatchEvent(new GDBControlShutdownDMEvent(fControlDmc), getProperties()); - } - } - - public static class InitializationShutdownStep extends Sequence.Step { - public enum Direction { INITIALIZING, SHUTTING_DOWN } - - private Direction fDirection; - public InitializationShutdownStep(Direction direction) { fDirection = direction; } - - @Override - final public void execute(RequestMonitor requestMonitor) { - if (fDirection == Direction.INITIALIZING) { - initialize(requestMonitor); - } else { - shutdown(requestMonitor); - } - } - - @Override - final public void rollBack(RequestMonitor requestMonitor) { - if (fDirection == Direction.INITIALIZING) { - shutdown(requestMonitor); - } else { - super.rollBack(requestMonitor); - } - } - - protected void initialize(RequestMonitor requestMonitor) { - requestMonitor.done(); - } - protected void shutdown(RequestMonitor requestMonitor) { - requestMonitor.done(); - } - } - - protected class CommandMonitoringStep extends InitializationShutdownStep { - CommandMonitoringStep(Direction direction) { super(direction); } - - @Override - protected void initialize(final RequestMonitor requestMonitor) { - startCommandProcessing(fMIBackend.getMIInputStream(), fMIBackend.getMIOutputStream()); - requestMonitor.done(); - } - - @Override - protected void shutdown(RequestMonitor requestMonitor) { - stopCommandProcessing(); - 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); } - - @Override - public void initialize(final RequestMonitor requestMonitor) { - try { - fCLIProcess = new GDBBackendCLIProcess(GDBControl.this, fMIBackend); - } - catch(IOException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Failed to create CLI Process", e)); //$NON-NLS-1$ - requestMonitor.done(); - return; - } - - createInferiorProcess(); - - fCLICommandProcessor = new CLIEventProcessor(GDBControl.this, fControlDmc); - fMIEventProcessor = new MIRunControlEventProcessor(GDBControl.this, fControlDmc); - - requestMonitor.done(); - } - - @Override - protected void shutdown(RequestMonitor requestMonitor) { - fCLICommandProcessor.dispose(); - fMIEventProcessor.dispose(); - fCLIProcess.dispose(); - fInferiorProcess.dispose(); - - requestMonitor.done(); - } - } - - protected class RegisterStep extends InitializationShutdownStep { - RegisterStep(Direction direction) { super(direction); } - @Override - public void initialize(final RequestMonitor requestMonitor) { - getSession().addServiceEventListener(GDBControl.this, null); - register( - new String[]{ ICommandControl.class.getName(), - ICommandControlService.class.getName(), - AbstractMIControl.class.getName(), - IGDBControl.class.getName() }, - new Hashtable<String,String>()); - getSession().dispatchEvent(new GDBControlInitializedDMEvent(fControlDmc), getProperties()); - requestMonitor.done(); - } - - @Override - protected void shutdown(RequestMonitor requestMonitor) { - unregister(); - getSession().removeServiceEventListener(GDBControl.this); - requestMonitor.done(); - } - } -} +/*******************************************************************************
+ * Copyright (c) 2006, 2008 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
+ * Ericsson - Modified for additional features in DSF Reference implementation
+ * Nokia - create and use backend service.
+ *******************************************************************************/
+package org.eclipse.cdt.dsf.gdb.service.command;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Hashtable;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
+import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.Sequence;
+import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent;
+import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext;
+import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
+import org.eclipse.cdt.dsf.debug.service.command.ICommandControl;
+import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
+import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants;
+import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
+import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
+import org.eclipse.cdt.dsf.gdb.service.IGDBBackend;
+import org.eclipse.cdt.dsf.gdb.service.SessionType;
+import org.eclipse.cdt.dsf.mi.service.IMIBackend;
+import org.eclipse.cdt.dsf.mi.service.IMIProcesses;
+import org.eclipse.cdt.dsf.mi.service.MIProcesses;
+import org.eclipse.cdt.dsf.mi.service.IMIBackend.BackendStateChangedEvent;
+import org.eclipse.cdt.dsf.mi.service.MIProcesses.ContainerExitedDMEvent;
+import org.eclipse.cdt.dsf.mi.service.MIProcesses.ContainerStartedDMEvent;
+import org.eclipse.cdt.dsf.mi.service.command.AbstractCLIProcess;
+import org.eclipse.cdt.dsf.mi.service.command.AbstractMIControl;
+import org.eclipse.cdt.dsf.mi.service.command.CLIEventProcessor;
+import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext;
+import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess;
+import org.eclipse.cdt.dsf.mi.service.command.MIRunControlEventProcessor;
+import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess.State;
+import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakInsert;
+import org.eclipse.cdt.dsf.mi.service.command.commands.MICommand;
+import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecContinue;
+import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecRun;
+import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBExit;
+import org.eclipse.cdt.dsf.mi.service.command.commands.MIInferiorTTYSet;
+import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo;
+import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo;
+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.pty.PTY;
+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.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.osgi.framework.BundleContext;
+
+/**
+ * GDB Debugger control implementation. This implementation extends the
+ * base MI control implementation to provide the GDB-specific debugger
+ * features. This includes:<br>
+ * - CLI console support,<br>
+ * - inferior process status tracking.<br>
+ */
+public class GDBControl extends AbstractMIControl implements IGDBControl {
+
+ /**
+ * Event indicating that the back end process has started.
+ */
+ private static class GDBControlInitializedDMEvent extends AbstractDMEvent<ICommandControlDMContext>
+ implements ICommandControlInitializedDMEvent
+ {
+ public GDBControlInitializedDMEvent(ICommandControlDMContext context) {
+ super(context);
+ }
+ }
+
+ /**
+ * Event indicating that the CommandControl (back end process) has terminated.
+ */
+ private static class GDBControlShutdownDMEvent extends AbstractDMEvent<ICommandControlDMContext>
+ implements ICommandControlShutdownDMEvent
+ {
+ public GDBControlShutdownDMEvent(ICommandControlDMContext context) {
+ super(context);
+ }
+ }
+
+ private GDBControlDMContext fControlDmc;
+
+ private IGDBBackend fMIBackend;
+
+ private boolean fConnected = false;
+
+ private MIRunControlEventProcessor fMIEventProcessor;
+ private CLIEventProcessor fCLICommandProcessor;
+ private AbstractCLIProcess fCLIProcess;
+ private MIInferiorProcess fInferiorProcess = null;
+
+ private PTY fPty;
+
+ public GDBControl(DsfSession session, ILaunchConfiguration config) {
+ super(session, false);
+ }
+
+ @Override
+ protected BundleContext getBundleContext() {
+ return GdbPlugin.getBundleContext();
+ }
+
+ @Override
+ public void initialize(final RequestMonitor requestMonitor) {
+ super.initialize( new RequestMonitor(getExecutor(), requestMonitor) {
+ @Override
+ protected void handleSuccess() {
+ doInitialize(requestMonitor);
+ }
+ });
+ }
+
+ public void doInitialize(final RequestMonitor requestMonitor) {
+
+ fMIBackend = getServicesTracker().getService(IGDBBackend.class);
+
+ // getId uses the MIBackend service, which is why we must wait until we
+ // have it, before we can create this context.
+ fControlDmc = new GDBControlDMContext(getSession().getId(), getId());
+
+ final Sequence.Step[] initializeSteps = new Sequence.Step[] {
+ new CommandMonitoringStep(InitializationShutdownStep.Direction.INITIALIZING),
+ new InferiorInputOutputInitStep(InitializationShutdownStep.Direction.INITIALIZING),
+ new CommandProcessorsStep(InitializationShutdownStep.Direction.INITIALIZING),
+ new RegisterStep(InitializationShutdownStep.Direction.INITIALIZING),
+ };
+
+ Sequence startupSequence = new Sequence(getExecutor(), requestMonitor) {
+ @Override public Step[] getSteps() { return initializeSteps; }
+ };
+ getExecutor().execute(startupSequence);
+ }
+
+ @Override
+ public void shutdown(final RequestMonitor requestMonitor) {
+ 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),
+ };
+ Sequence shutdownSequence = new Sequence(getExecutor(), requestMonitor) {
+ @Override public Step[] getSteps() { return shutdownSteps; }
+ };
+ getExecutor().execute(shutdownSequence);
+
+ }
+
+ public String getId() {
+ return fMIBackend.getId();
+ }
+
+ @Override
+ public MIControlDMContext getControlDMContext() {
+ return fControlDmc;
+ }
+
+ public ICommandControlDMContext getContext() {
+ return fControlDmc;
+ }
+
+ public void terminate(final RequestMonitor rm) {
+ // To fix bug 234467:
+ // Interrupt GDB in case the inferior is running.
+ // That way, the inferior will also be killed when we exit GDB.
+ //
+ if (fInferiorProcess.getState() == State.RUNNING) {
+ fMIBackend.interrupt();
+ }
+
+ // Schedule a runnable to be executed 2 seconds from now.
+ // If we don't get a response to the quit command, this
+ // runnable will kill the task.
+ final Future<?> forceQuitTask = getExecutor().schedule(
+ new DsfRunnable() {
+ public void run() {
+ fMIBackend.destroy();
+ rm.done();
+ }
+
+ @Override
+ protected boolean isExecutionRequired() {
+ return false;
+ }
+ },
+ 2, TimeUnit.SECONDS);
+
+ queueCommand(
+ new MIGDBExit(fControlDmc),
+ new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
+ @Override
+ public void handleCompleted() {
+ if (isSuccess()) {
+ // Cancel the time out runnable (if it hasn't run yet).
+ forceQuitTask.cancel(false);
+ rm.done();
+ }
+ // else: the forceQuitTask has or will handle it.
+ // It is good to wait for the forceQuitTask to trigger
+ // to leave enough time for the interrupt() to complete.
+ }
+ }
+ );
+ }
+
+ /*
+ * 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 should
+ * be used instead; this decision is based on the type of session.
+ */
+ public void initInferiorInputOutput(final RequestMonitor requestMonitor) {
+ if (fMIBackend.getSessionType() == SessionType.REMOTE || fMIBackend.getIsAttachSession()) {
+ // 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<MIInfo>(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 (fMIBackend.getIsAttachSession() || fMIBackend.getSessionType() == SessionType.CORE) {
+ 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 (fMIBackend.getSessionType() == 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 (fMIBackend.getIsAttachSession()) {
+ // 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;
+ }
+
+ DsfServicesTracker servicesTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), getSession().getId());
+ IMIProcesses procService = servicesTracker.getService(IMIProcesses.class);
+ servicesTracker.dispose();
+ IProcessDMContext procDmc = procService.createProcessContext(fControlDmc, MIProcesses.UNIQUE_GROUP_ID);
+ final IContainerDMContext containerDmc = procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID);
+
+ final MICommand<MIInfo> execCommand;
+ if (fMIBackend.getSessionType() == SessionType.REMOTE) {
+ // When doing remote debugging, we use -exec-continue instead of -exec-run
+ execCommand = new MIExecContinue(containerDmc);
+ } else {
+ execCommand = new MIExecRun(containerDmc, 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;
+ }
+
+ final DataRequestMonitor<MIInfo> execMonitor = new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor) {
+ @Override
+ protected void handleSuccess() {
+ getSession().dispatchEvent(new ContainerStartedDMEvent(containerDmc), getProperties());
+ super.handleSuccess();
+ }
+ };
+
+ if (!stopInMain) {
+ // Just start the program.
+ queueCommand(execCommand, execMonitor);
+ } 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<MIBreakInsertInfo>(getExecutor(), requestMonitor) {
+ @Override
+ protected void handleSuccess() {
+ // After the break-insert is done, execute the -exec-run or -exec-continue command.
+ queueCommand(execCommand, execMonitor);
+ }
+ });
+ }
+ }
+
+ /*
+ * 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, fMIBackend, fMIBackend.getMIOutputStream());
+ } else {
+ fInferiorProcess = new GDBInferiorProcess(GDBControl.this, fMIBackend, fPty);
+ }
+ }
+
+ public boolean isConnected() {
+ return fInferiorProcess.getState() != MIInferiorProcess.State.TERMINATED &&
+ (!fMIBackend.getIsAttachSession() || fConnected);
+ }
+
+ public void setConnected(boolean connected) {
+ fConnected = connected;
+ }
+
+ public AbstractCLIProcess getCLIProcess() {
+ return fCLIProcess;
+ }
+
+ public MIInferiorProcess getInferiorProcess() {
+ return fInferiorProcess;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void setTracingStream(OutputStream tracingStream) {
+ setMITracingStream(tracingStream);
+ }
+
+ @DsfServiceEventHandler
+ public void eventDispatched(ICommandControlShutdownDMEvent e) {
+ // Handle our "GDB Exited" event and stop processing commands.
+ stopCommandProcessing();
+ }
+
+ @DsfServiceEventHandler
+ public void eventDispatched(BackendStateChangedEvent e) {
+ if (e.getState() == IMIBackend.State.TERMINATED && e.getBackendId().equals(fMIBackend.getId())) {
+ // Handle "GDB Exited" event, just relay to following event.
+ getSession().dispatchEvent(new GDBControlShutdownDMEvent(fControlDmc), getProperties());
+ }
+ }
+
+ /** @since 2.0 */
+ @DsfServiceEventHandler
+ public void eventDispatched(ContainerExitedDMEvent e) {
+ if (Platform.getPreferencesService().getBoolean("org.eclipse.cdt.dsf.gdb.ui", //$NON-NLS-1$
+ IGdbDebugConstants.PREF_AUTO_TERMINATE_GDB,
+ true, null)) {
+ // If the inferior finishes, let's terminate GDB
+ terminate(new RequestMonitor(getExecutor(), null));
+ }
+ }
+
+ public static class InitializationShutdownStep extends Sequence.Step {
+ public enum Direction { INITIALIZING, SHUTTING_DOWN }
+
+ private Direction fDirection;
+ public InitializationShutdownStep(Direction direction) { fDirection = direction; }
+
+ @Override
+ final public void execute(RequestMonitor requestMonitor) {
+ if (fDirection == Direction.INITIALIZING) {
+ initialize(requestMonitor);
+ } else {
+ shutdown(requestMonitor);
+ }
+ }
+
+ @Override
+ final public void rollBack(RequestMonitor requestMonitor) {
+ if (fDirection == Direction.INITIALIZING) {
+ shutdown(requestMonitor);
+ } else {
+ super.rollBack(requestMonitor);
+ }
+ }
+
+ protected void initialize(RequestMonitor requestMonitor) {
+ requestMonitor.done();
+ }
+ protected void shutdown(RequestMonitor requestMonitor) {
+ requestMonitor.done();
+ }
+ }
+
+ protected class CommandMonitoringStep extends InitializationShutdownStep {
+ CommandMonitoringStep(Direction direction) { super(direction); }
+
+ @Override
+ protected void initialize(final RequestMonitor requestMonitor) {
+ startCommandProcessing(fMIBackend.getMIInputStream(), fMIBackend.getMIOutputStream());
+ requestMonitor.done();
+ }
+
+ @Override
+ protected void shutdown(RequestMonitor requestMonitor) {
+ stopCommandProcessing();
+ 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); }
+
+ @Override
+ public void initialize(final RequestMonitor requestMonitor) {
+ try {
+ fCLIProcess = new GDBBackendCLIProcess(GDBControl.this, fMIBackend);
+ }
+ catch(IOException e) {
+ requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Failed to create CLI Process", e)); //$NON-NLS-1$
+ requestMonitor.done();
+ return;
+ }
+
+ createInferiorProcess();
+
+ fCLICommandProcessor = new CLIEventProcessor(GDBControl.this, fControlDmc);
+ fMIEventProcessor = new MIRunControlEventProcessor(GDBControl.this, fControlDmc);
+
+ requestMonitor.done();
+ }
+
+ @Override
+ protected void shutdown(RequestMonitor requestMonitor) {
+ fCLICommandProcessor.dispose();
+ fMIEventProcessor.dispose();
+ fCLIProcess.dispose();
+ fInferiorProcess.dispose();
+
+ requestMonitor.done();
+ }
+ }
+
+ protected class RegisterStep extends InitializationShutdownStep {
+ RegisterStep(Direction direction) { super(direction); }
+ @Override
+ public void initialize(final RequestMonitor requestMonitor) {
+ getSession().addServiceEventListener(GDBControl.this, null);
+ register(
+ new String[]{ ICommandControl.class.getName(),
+ ICommandControlService.class.getName(),
+ AbstractMIControl.class.getName(),
+ IGDBControl.class.getName() },
+ new Hashtable<String,String>());
+ getSession().dispatchEvent(new GDBControlInitializedDMEvent(fControlDmc), getProperties());
+ requestMonitor.done();
+ }
+
+ @Override
+ protected void shutdown(RequestMonitor requestMonitor) {
+ unregister();
+ getSession().removeServiceEventListener(GDBControl.this);
+ requestMonitor.done();
+ }
+ }
+}
diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_0.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_0.java index 83170ec69c4..efd9e0f0b41 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_0.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_0.java @@ -1,662 +1,680 @@ -/******************************************************************************* - * Copyright (c) 2006, 2008 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 - * Ericsson - Modified for additional features in DSF Reference implementation - * Ericsson - New version for 7_0 - *******************************************************************************/ -package org.eclipse.cdt.dsf.gdb.service.command; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.Hashtable; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; -import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; -import org.eclipse.cdt.dsf.concurrent.DsfRunnable; -import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; -import org.eclipse.cdt.dsf.concurrent.RequestMonitor; -import org.eclipse.cdt.dsf.concurrent.Sequence; -import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; -import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; -import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; -import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; -import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; -import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; -import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; -import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; -import org.eclipse.cdt.dsf.gdb.service.GDBRunControl_7_0; -import org.eclipse.cdt.dsf.gdb.service.IGDBBackend; -import org.eclipse.cdt.dsf.gdb.service.IReverseRunControl; -import org.eclipse.cdt.dsf.gdb.service.SessionType; -import org.eclipse.cdt.dsf.mi.service.IMIBackend; -import org.eclipse.cdt.dsf.mi.service.IMIProcesses; -import org.eclipse.cdt.dsf.mi.service.MIProcesses; -import org.eclipse.cdt.dsf.mi.service.IMIBackend.BackendStateChangedEvent; -import org.eclipse.cdt.dsf.mi.service.command.AbstractCLIProcess; -import org.eclipse.cdt.dsf.mi.service.command.AbstractMIControl; -import org.eclipse.cdt.dsf.mi.service.command.CLIEventProcessor_7_0; -import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; -import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess; -import org.eclipse.cdt.dsf.mi.service.command.MIRunControlEventProcessor_7_0; -import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess.State; -import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakInsert; -import org.eclipse.cdt.dsf.mi.service.command.commands.MICommand; -import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecContinue; -import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecRun; -import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBExit; -import org.eclipse.cdt.dsf.mi.service.command.commands.MIInferiorTTYSet; -import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo; -import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakpoint; -import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; -import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; -import org.eclipse.cdt.dsf.service.DsfSession; -import org.eclipse.cdt.utils.pty.PTY; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.debug.core.DebugException; -import org.eclipse.debug.core.ILaunchConfiguration; -import org.osgi.framework.BundleContext; - -/** - * GDB Debugger control implementation. This implementation extends the - * base MI control implementation to provide the GDB-specific debugger - * features. This includes:<br> - * - CLI console support,<br> - * - inferior process status tracking.<br> - */ -public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl { - - /** - * Event indicating that the back end process has started. - */ - private static class GDBControlInitializedDMEvent extends AbstractDMEvent<ICommandControlDMContext> - implements ICommandControlInitializedDMEvent - { - public GDBControlInitializedDMEvent(ICommandControlDMContext context) { - super(context); - } - } - - /** - * Event indicating that the CommandControl (back end process) has terminated. - */ - private static class GDBControlShutdownDMEvent extends AbstractDMEvent<ICommandControlDMContext> - implements ICommandControlShutdownDMEvent - { - public GDBControlShutdownDMEvent(ICommandControlDMContext context) { - super(context); - } - } - - private GDBControlDMContext fControlDmc; - - private IGDBBackend fMIBackend; - - private int fConnected = 0; - - private MIRunControlEventProcessor_7_0 fMIEventProcessor; - private CLIEventProcessor_7_0 fCLICommandProcessor; - private AbstractCLIProcess fCLIProcess; - private MIInferiorProcess fInferiorProcess = null; - - private PTY fPty; - - public GDBControl_7_0(DsfSession session, ILaunchConfiguration config) { - super(session, true); - } - - @Override - protected BundleContext getBundleContext() { - return GdbPlugin.getBundleContext(); - } - - @Override - public void initialize(final RequestMonitor requestMonitor) { - super.initialize( new RequestMonitor(getExecutor(), requestMonitor) { - @Override - protected void handleSuccess() { - doInitialize(requestMonitor); - } - }); - } - - public void doInitialize(final RequestMonitor requestMonitor) { - fMIBackend = getServicesTracker().getService(IGDBBackend.class); - - // getId uses the MIBackend service, which is why we must wait until we - // have it, before we can create this context. - fControlDmc = new GDBControlDMContext(getSession().getId(), getId()); - - final Sequence.Step[] initializeSteps = new Sequence.Step[] { - new CommandMonitoringStep(InitializationShutdownStep.Direction.INITIALIZING), - new InferiorInputOutputInitStep(InitializationShutdownStep.Direction.INITIALIZING), - new CommandProcessorsStep(InitializationShutdownStep.Direction.INITIALIZING), - new RegisterStep(InitializationShutdownStep.Direction.INITIALIZING), - }; - - Sequence startupSequence = new Sequence(getExecutor(), requestMonitor) { - @Override public Step[] getSteps() { return initializeSteps; } - }; - getExecutor().execute(startupSequence); - } - - @Override - public void shutdown(final RequestMonitor requestMonitor) { - 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), - }; - Sequence shutdownSequence = new Sequence(getExecutor(), requestMonitor) { - @Override public Step[] getSteps() { return shutdownSteps; } - }; - getExecutor().execute(shutdownSequence); - - } - - public String getId() { - return fMIBackend.getId(); - } - - @Override - public MIControlDMContext getControlDMContext() { - return fControlDmc; - } - - public ICommandControlDMContext getContext() { - return fControlDmc; - } - - public void terminate(final RequestMonitor rm) { - // To fix bug 234467: - // Interrupt GDB in case the inferior is running. - // That way, the inferior will also be killed when we exit GDB. - // - if (fInferiorProcess.getState() == State.RUNNING) { - fMIBackend.interrupt(); - } - - // Schedule a runnable to be executed 2 seconds from now. - // If we don't get a response to the quit command, this - // runnable will kill the task. - final Future<?> forceQuitTask = getExecutor().schedule( - new DsfRunnable() { - public void run() { - fMIBackend.destroy(); - rm.done(); - } - - @Override - protected boolean isExecutionRequired() { - return false; - } - }, - 2, TimeUnit.SECONDS); - - queueCommand( - new MIGDBExit(fControlDmc), - new DataRequestMonitor<MIInfo>(getExecutor(), rm) { - @Override - public void handleCompleted() { - if (isSuccess()) { - // Cancel the time out runnable (if it hasn't run yet). - forceQuitTask.cancel(false); - rm.done(); - } - // else: the forceQuitTask has or will handle it. - // It is good to wait for the forceQuitTask to trigger - // to leave enough time for the interrupt() to complete. - } - } - ); - } - - /* - * 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 should - * be used instead; this decision is based on the type of session. - */ - public void initInferiorInputOutput(final RequestMonitor requestMonitor) { - if (fMIBackend.getSessionType() == SessionType.REMOTE || fMIBackend.getIsAttachSession()) { - // 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<MIInfo>(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 (fMIBackend.getIsAttachSession()|| fMIBackend.getSessionType() == SessionType.CORE) { - 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 (fMIBackend.getSessionType() == 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. - * Note that restart does not apply to remote or attach sessions. - * - * If we want to enable Reverse debugging from the start of the program we do the following: - * attachSession => enable reverse - * else => set temp bp on main, run, enable reverse, continue if bp on main was not requested by user - */ - protected void startOrRestart(final GdbLaunch launch, boolean restart, RequestMonitor requestMonitor) { - boolean tmpReverseEnabled = IGDBLaunchConfigurationConstants.DEBUGGER_REVERSE_DEFAULT; - try { - tmpReverseEnabled = launch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REVERSE, - IGDBLaunchConfigurationConstants.DEBUGGER_REVERSE_DEFAULT); - } catch (CoreException e) { - } - final boolean reverseEnabled = tmpReverseEnabled; - - if (fMIBackend.getIsAttachSession()) { - // Restart does not apply to attach sessions. - // - // 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. - // We only need to turn on Reverse Debugging if requested. - if (reverseEnabled) { - IReverseRunControl reverseService = getServicesTracker().getService(IReverseRunControl.class); - if (reverseService != null) { - reverseService.enableReverseMode(fControlDmc, true, requestMonitor); - return; - } - } - requestMonitor.done(); - return; - } - - // When it is not an attach session, it gets a little more complicated - // so let's use a sequence. - getExecutor().execute(new Sequence(getExecutor(), requestMonitor) { - IContainerDMContext fContainerDmc; - MICommand<MIInfo> fExecCommand; - String fUserStopSymbol = null; - - MIBreakpoint fUserBreakpoint = null; - boolean fUserBreakpointIsOnMain = false; - - Step[] fSteps = new Step[] { - /* - * Figure out if we should use 'exec-continue' or '-exec-run'. - */ - new Step() { - @Override - public void execute(RequestMonitor rm) { - IMIProcesses procService = getServicesTracker().getService(IMIProcesses.class); - IProcessDMContext procDmc = procService.createProcessContext(fControlDmc, MIProcesses.UNIQUE_GROUP_ID); - fContainerDmc = procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID); - - if (fMIBackend.getSessionType() == SessionType.REMOTE) { - // Restart does not apply to remote sessions - // - // When doing remote debugging, we use -exec-continue instead of -exec-run - fExecCommand = new MIExecContinue(fContainerDmc); - } else { - fExecCommand = new MIExecRun(fContainerDmc, new String[0]); - } - rm.done(); - }}, - /* - * If the user requested a 'stopOnMain', let's set the temporary breakpoint - * where the user specified. - */ - new Step() { - @Override - public void execute(final RequestMonitor rm) { - boolean userRequestedStop = false; - try { - userRequestedStop = launch.getLaunchConfiguration().getAttribute( - ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, - false); - } catch (CoreException e) { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve stop at entry point boolean", e)); //$NON-NLS-1$ - rm.done(); - return; - } - - if (userRequestedStop) { - try { - fUserStopSymbol = launch.getLaunchConfiguration().getAttribute( - ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, - ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT); - } catch (CoreException e) { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.CONFIGURATION_INVALID, "Cannot retrieve the entry point symbol", e)); //$NON-NLS-1$ - rm.done(); - return; - } - - queueCommand(new MIBreakInsert(fControlDmc, true, false, null, 0, fUserStopSymbol, 0), - new DataRequestMonitor<MIBreakInsertInfo>(getExecutor(), rm) { - @Override - public void handleSuccess() { - if (getData() != null) { - MIBreakpoint[] breakpoints = getData().getMIBreakpoints(); - if (breakpoints.length > 0) { - fUserBreakpoint = breakpoints[0]; - } - } - rm.done(); - } - }); - } else { - rm.done(); - } - }}, - /* - * If reverse debugging, set a breakpoint on main to be able to enable reverse - * as early as possible. - * If the user has requested a stop at the same point, we could skip this breakpoint - * however, we have to first set it to find out! So, we just leave it. - */ - new Step() { - @Override - public void execute(final RequestMonitor rm) { - if (reverseEnabled) { - queueCommand(new MIBreakInsert(fControlDmc, true, false, null, 0, - ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT, 0), - new DataRequestMonitor<MIBreakInsertInfo>(getExecutor(), rm) { - @Override - public void handleSuccess() { - if (getData() != null) { - MIBreakpoint[] breakpoints = getData().getMIBreakpoints(); - if (breakpoints.length > 0 && fUserBreakpoint != null) { - fUserBreakpointIsOnMain = breakpoints[0].getAddress().equals(fUserBreakpoint.getAddress()); - } - } - rm.done(); - } - }); - } else { - rm.done(); - } - }}, - /* - * Now, run the program. - */ - new Step() { - @Override - public void execute(RequestMonitor rm) { - queueCommand(fExecCommand, new DataRequestMonitor<MIInfo>(getExecutor(), rm)); - }}, - /* - * In case of a restart, reverse debugging should be marked as off here because - * GDB will have turned it off. We may turn it back on after. - */ - new Step() { - @Override - public void execute(RequestMonitor rm) { - // Although it only makes sense for a restart, it doesn't hurt - // do to it all the time. - GDBRunControl_7_0 reverseService = getServicesTracker().getService(GDBRunControl_7_0.class); - if (reverseService != null) { - reverseService.setReverseModeEnabled(false); - } - rm.done(); - }}, - /* - * Since we have started the program, we can turn on reverse debugging if needed - */ - new Step() { - @Override - public void execute(RequestMonitor rm) { - if (reverseEnabled) { - IReverseRunControl reverseService = getServicesTracker().getService(IReverseRunControl.class); - if (reverseService != null) { - reverseService.enableReverseMode(fControlDmc, true, rm); - return; - } - } - rm.done(); - }}, - /* - * Finally, if we are enabling reverse, and the userSymbolStop is not on main, - * we should do a continue because we are currently stopped on main but that - * is not what the user requested - */ - new Step() { - @Override - public void execute(RequestMonitor rm) { - if (reverseEnabled && !fUserBreakpointIsOnMain) { - queueCommand(new MIExecContinue(fContainerDmc), - new DataRequestMonitor<MIInfo>(getExecutor(), rm)); - } else { - rm.done(); - } - }}, - }; - - @Override - public Step[] getSteps() { - return fSteps; - } - }); - } - - /** - * 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_7_0.this, fMIBackend, fMIBackend.getMIOutputStream()); - } else { - fInferiorProcess = new GDBInferiorProcess(GDBControl_7_0.this, fMIBackend, fPty); - } - } - - public boolean isConnected() { - return fInferiorProcess.getState() != MIInferiorProcess.State.TERMINATED && - (!fMIBackend.getIsAttachSession() || fConnected > 0); - } - - public void setConnected(boolean connected) { - if (connected) { - fConnected++; - } else { - if (fConnected > 0) fConnected--; - } - } - - public AbstractCLIProcess getCLIProcess() { - return fCLIProcess; - } - - public MIInferiorProcess getInferiorProcess() { - return fInferiorProcess; - } - - /** - * @since 2.0 - */ - public void setTracingStream(OutputStream tracingStream) { - setMITracingStream(tracingStream); - } - - @DsfServiceEventHandler - public void eventDispatched(ICommandControlShutdownDMEvent e) { - // Handle our "GDB Exited" event and stop processing commands. - stopCommandProcessing(); - } - - @DsfServiceEventHandler - public void eventDispatched(BackendStateChangedEvent e) { - if (e.getState() == IMIBackend.State.TERMINATED && e.getBackendId().equals(fMIBackend.getId())) { - // Handle "GDB Exited" event, just relay to following event. - getSession().dispatchEvent(new GDBControlShutdownDMEvent(fControlDmc), getProperties()); - } - } - - public static class InitializationShutdownStep extends Sequence.Step { - public enum Direction { INITIALIZING, SHUTTING_DOWN } - - private Direction fDirection; - public InitializationShutdownStep(Direction direction) { fDirection = direction; } - - @Override - final public void execute(RequestMonitor requestMonitor) { - if (fDirection == Direction.INITIALIZING) { - initialize(requestMonitor); - } else { - shutdown(requestMonitor); - } - } - - @Override - final public void rollBack(RequestMonitor requestMonitor) { - if (fDirection == Direction.INITIALIZING) { - shutdown(requestMonitor); - } else { - super.rollBack(requestMonitor); - } - } - - protected void initialize(RequestMonitor requestMonitor) { - requestMonitor.done(); - } - protected void shutdown(RequestMonitor requestMonitor) { - requestMonitor.done(); - } - } - - protected class CommandMonitoringStep extends InitializationShutdownStep { - CommandMonitoringStep(Direction direction) { super(direction); } - - @Override - protected void initialize(final RequestMonitor requestMonitor) { - startCommandProcessing(fMIBackend.getMIInputStream(), fMIBackend.getMIOutputStream()); - requestMonitor.done(); - } - - @Override - protected void shutdown(RequestMonitor requestMonitor) { - stopCommandProcessing(); - 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); } - - @Override - public void initialize(final RequestMonitor requestMonitor) { - try { - fCLIProcess = new GDBBackendCLIProcess(GDBControl_7_0.this, fMIBackend); - } - catch(IOException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Failed to create CLI Process", e)); //$NON-NLS-1$ - requestMonitor.done(); - return; - } - - createInferiorProcess(); - - fCLICommandProcessor = new CLIEventProcessor_7_0(GDBControl_7_0.this, fControlDmc); - fMIEventProcessor = new MIRunControlEventProcessor_7_0(GDBControl_7_0.this, fControlDmc); - - requestMonitor.done(); - } - - @Override - protected void shutdown(RequestMonitor requestMonitor) { - fCLICommandProcessor.dispose(); - fMIEventProcessor.dispose(); - fCLIProcess.dispose(); - fInferiorProcess.dispose(); - - requestMonitor.done(); - } - } - - protected class RegisterStep extends InitializationShutdownStep { - RegisterStep(Direction direction) { super(direction); } - @Override - public void initialize(final RequestMonitor requestMonitor) { - getSession().addServiceEventListener(GDBControl_7_0.this, null); - register( - new String[]{ ICommandControl.class.getName(), - ICommandControlService.class.getName(), - AbstractMIControl.class.getName(), - IGDBControl.class.getName() }, - new Hashtable<String,String>()); - getSession().dispatchEvent(new GDBControlInitializedDMEvent(fControlDmc), getProperties()); - requestMonitor.done(); - } - - @Override - protected void shutdown(RequestMonitor requestMonitor) { - unregister(); - getSession().removeServiceEventListener(GDBControl_7_0.this); - requestMonitor.done(); - } - } -} +/*******************************************************************************
+ * Copyright (c) 2006, 2008 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
+ * Ericsson - Modified for additional features in DSF Reference implementation
+ * Ericsson - New version for 7_0
+ *******************************************************************************/
+package org.eclipse.cdt.dsf.gdb.service.command;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Hashtable;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
+import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.Sequence;
+import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent;
+import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext;
+import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
+import org.eclipse.cdt.dsf.debug.service.command.ICommandControl;
+import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
+import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
+import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants;
+import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
+import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
+import org.eclipse.cdt.dsf.gdb.service.GDBRunControl_7_0;
+import org.eclipse.cdt.dsf.gdb.service.IGDBBackend;
+import org.eclipse.cdt.dsf.gdb.service.IReverseRunControl;
+import org.eclipse.cdt.dsf.gdb.service.SessionType;
+import org.eclipse.cdt.dsf.gdb.service.GDBProcesses_7_0.ContainerExitedDMEvent;
+import org.eclipse.cdt.dsf.mi.service.IMIBackend;
+import org.eclipse.cdt.dsf.mi.service.IMIProcesses;
+import org.eclipse.cdt.dsf.mi.service.MIProcesses;
+import org.eclipse.cdt.dsf.mi.service.IMIBackend.BackendStateChangedEvent;
+import org.eclipse.cdt.dsf.mi.service.command.AbstractCLIProcess;
+import org.eclipse.cdt.dsf.mi.service.command.AbstractMIControl;
+import org.eclipse.cdt.dsf.mi.service.command.CLIEventProcessor_7_0;
+import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext;
+import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess;
+import org.eclipse.cdt.dsf.mi.service.command.MIRunControlEventProcessor_7_0;
+import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess.State;
+import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakInsert;
+import org.eclipse.cdt.dsf.mi.service.command.commands.MICommand;
+import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecContinue;
+import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecRun;
+import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBExit;
+import org.eclipse.cdt.dsf.mi.service.command.commands.MIInferiorTTYSet;
+import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo;
+import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakpoint;
+import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo;
+import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.cdt.utils.pty.PTY;
+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.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.osgi.framework.BundleContext;
+
+/**
+ * GDB Debugger control implementation. This implementation extends the
+ * base MI control implementation to provide the GDB-specific debugger
+ * features. This includes:<br>
+ * - CLI console support,<br>
+ * - inferior process status tracking.<br>
+ */
+public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl {
+
+ /**
+ * Event indicating that the back end process has started.
+ */
+ private static class GDBControlInitializedDMEvent extends AbstractDMEvent<ICommandControlDMContext>
+ implements ICommandControlInitializedDMEvent
+ {
+ public GDBControlInitializedDMEvent(ICommandControlDMContext context) {
+ super(context);
+ }
+ }
+
+ /**
+ * Event indicating that the CommandControl (back end process) has terminated.
+ */
+ private static class GDBControlShutdownDMEvent extends AbstractDMEvent<ICommandControlDMContext>
+ implements ICommandControlShutdownDMEvent
+ {
+ public GDBControlShutdownDMEvent(ICommandControlDMContext context) {
+ super(context);
+ }
+ }
+
+ private GDBControlDMContext fControlDmc;
+
+ private IGDBBackend fMIBackend;
+
+ private int fConnected = 0;
+
+ private MIRunControlEventProcessor_7_0 fMIEventProcessor;
+ private CLIEventProcessor_7_0 fCLICommandProcessor;
+ private AbstractCLIProcess fCLIProcess;
+ private MIInferiorProcess fInferiorProcess = null;
+
+ private PTY fPty;
+
+ public GDBControl_7_0(DsfSession session, ILaunchConfiguration config) {
+ super(session, true);
+ }
+
+ @Override
+ protected BundleContext getBundleContext() {
+ return GdbPlugin.getBundleContext();
+ }
+
+ @Override
+ public void initialize(final RequestMonitor requestMonitor) {
+ super.initialize( new RequestMonitor(getExecutor(), requestMonitor) {
+ @Override
+ protected void handleSuccess() {
+ doInitialize(requestMonitor);
+ }
+ });
+ }
+
+ public void doInitialize(final RequestMonitor requestMonitor) {
+ fMIBackend = getServicesTracker().getService(IGDBBackend.class);
+
+ // getId uses the MIBackend service, which is why we must wait until we
+ // have it, before we can create this context.
+ fControlDmc = new GDBControlDMContext(getSession().getId(), getId());
+
+ final Sequence.Step[] initializeSteps = new Sequence.Step[] {
+ new CommandMonitoringStep(InitializationShutdownStep.Direction.INITIALIZING),
+ new InferiorInputOutputInitStep(InitializationShutdownStep.Direction.INITIALIZING),
+ new CommandProcessorsStep(InitializationShutdownStep.Direction.INITIALIZING),
+ new RegisterStep(InitializationShutdownStep.Direction.INITIALIZING),
+ };
+
+ Sequence startupSequence = new Sequence(getExecutor(), requestMonitor) {
+ @Override public Step[] getSteps() { return initializeSteps; }
+ };
+ getExecutor().execute(startupSequence);
+ }
+
+ @Override
+ public void shutdown(final RequestMonitor requestMonitor) {
+ 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),
+ };
+ Sequence shutdownSequence = new Sequence(getExecutor(), requestMonitor) {
+ @Override public Step[] getSteps() { return shutdownSteps; }
+ };
+ getExecutor().execute(shutdownSequence);
+
+ }
+
+ public String getId() {
+ return fMIBackend.getId();
+ }
+
+ @Override
+ public MIControlDMContext getControlDMContext() {
+ return fControlDmc;
+ }
+
+ public ICommandControlDMContext getContext() {
+ return fControlDmc;
+ }
+
+ public void terminate(final RequestMonitor rm) {
+ // To fix bug 234467:
+ // Interrupt GDB in case the inferior is running.
+ // That way, the inferior will also be killed when we exit GDB.
+ //
+ if (fInferiorProcess.getState() == State.RUNNING) {
+ fMIBackend.interrupt();
+ }
+
+ // Schedule a runnable to be executed 2 seconds from now.
+ // If we don't get a response to the quit command, this
+ // runnable will kill the task.
+ final Future<?> forceQuitTask = getExecutor().schedule(
+ new DsfRunnable() {
+ public void run() {
+ fMIBackend.destroy();
+ rm.done();
+ }
+
+ @Override
+ protected boolean isExecutionRequired() {
+ return false;
+ }
+ },
+ 2, TimeUnit.SECONDS);
+
+ queueCommand(
+ new MIGDBExit(fControlDmc),
+ new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
+ @Override
+ public void handleCompleted() {
+ if (isSuccess()) {
+ // Cancel the time out runnable (if it hasn't run yet).
+ forceQuitTask.cancel(false);
+ rm.done();
+ }
+ // else: the forceQuitTask has or will handle it.
+ // It is good to wait for the forceQuitTask to trigger
+ // to leave enough time for the interrupt() to complete.
+ }
+ }
+ );
+ }
+
+ /*
+ * 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 should
+ * be used instead; this decision is based on the type of session.
+ */
+ public void initInferiorInputOutput(final RequestMonitor requestMonitor) {
+ if (fMIBackend.getSessionType() == SessionType.REMOTE || fMIBackend.getIsAttachSession()) {
+ // 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<MIInfo>(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 (fMIBackend.getIsAttachSession()|| fMIBackend.getSessionType() == SessionType.CORE) {
+ 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 (fMIBackend.getSessionType() == 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.
+ * Note that restart does not apply to remote or attach sessions.
+ *
+ * If we want to enable Reverse debugging from the start of the program we do the following:
+ * attachSession => enable reverse
+ * else => set temp bp on main, run, enable reverse, continue if bp on main was not requested by user
+ */
+ protected void startOrRestart(final GdbLaunch launch, boolean restart, RequestMonitor requestMonitor) {
+ boolean tmpReverseEnabled = IGDBLaunchConfigurationConstants.DEBUGGER_REVERSE_DEFAULT;
+ try {
+ tmpReverseEnabled = launch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REVERSE,
+ IGDBLaunchConfigurationConstants.DEBUGGER_REVERSE_DEFAULT);
+ } catch (CoreException e) {
+ }
+ final boolean reverseEnabled = tmpReverseEnabled;
+
+ if (fMIBackend.getIsAttachSession()) {
+ // Restart does not apply to attach sessions.
+ //
+ // 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.
+ // We only need to turn on Reverse Debugging if requested.
+ if (reverseEnabled) {
+ IReverseRunControl reverseService = getServicesTracker().getService(IReverseRunControl.class);
+ if (reverseService != null) {
+ reverseService.enableReverseMode(fControlDmc, true, requestMonitor);
+ return;
+ }
+ }
+ requestMonitor.done();
+ return;
+ }
+
+ // When it is not an attach session, it gets a little more complicated
+ // so let's use a sequence.
+ getExecutor().execute(new Sequence(getExecutor(), requestMonitor) {
+ IContainerDMContext fContainerDmc;
+ MICommand<MIInfo> fExecCommand;
+ String fUserStopSymbol = null;
+
+ MIBreakpoint fUserBreakpoint = null;
+ boolean fUserBreakpointIsOnMain = false;
+
+ Step[] fSteps = new Step[] {
+ /*
+ * Figure out if we should use 'exec-continue' or '-exec-run'.
+ */
+ new Step() {
+ @Override
+ public void execute(RequestMonitor rm) {
+ IMIProcesses procService = getServicesTracker().getService(IMIProcesses.class);
+ IProcessDMContext procDmc = procService.createProcessContext(fControlDmc, MIProcesses.UNIQUE_GROUP_ID);
+ fContainerDmc = procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID);
+
+ if (fMIBackend.getSessionType() == SessionType.REMOTE) {
+ // Restart does not apply to remote sessions
+ //
+ // When doing remote debugging, we use -exec-continue instead of -exec-run
+ fExecCommand = new MIExecContinue(fContainerDmc);
+ } else {
+ fExecCommand = new MIExecRun(fContainerDmc, new String[0]);
+ }
+ rm.done();
+ }},
+ /*
+ * If the user requested a 'stopOnMain', let's set the temporary breakpoint
+ * where the user specified.
+ */
+ new Step() {
+ @Override
+ public void execute(final RequestMonitor rm) {
+ boolean userRequestedStop = false;
+ try {
+ userRequestedStop = launch.getLaunchConfiguration().getAttribute(
+ ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN,
+ false);
+ } catch (CoreException e) {
+ rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve stop at entry point boolean", e)); //$NON-NLS-1$
+ rm.done();
+ return;
+ }
+
+ if (userRequestedStop) {
+ try {
+ fUserStopSymbol = launch.getLaunchConfiguration().getAttribute(
+ ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL,
+ ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT);
+ } catch (CoreException e) {
+ rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.CONFIGURATION_INVALID, "Cannot retrieve the entry point symbol", e)); //$NON-NLS-1$
+ rm.done();
+ return;
+ }
+
+ queueCommand(new MIBreakInsert(fControlDmc, true, false, null, 0, fUserStopSymbol, 0),
+ new DataRequestMonitor<MIBreakInsertInfo>(getExecutor(), rm) {
+ @Override
+ public void handleSuccess() {
+ if (getData() != null) {
+ MIBreakpoint[] breakpoints = getData().getMIBreakpoints();
+ if (breakpoints.length > 0) {
+ fUserBreakpoint = breakpoints[0];
+ }
+ }
+ rm.done();
+ }
+ });
+ } else {
+ rm.done();
+ }
+ }},
+ /*
+ * If reverse debugging, set a breakpoint on main to be able to enable reverse
+ * as early as possible.
+ * If the user has requested a stop at the same point, we could skip this breakpoint
+ * however, we have to first set it to find out! So, we just leave it.
+ */
+ new Step() {
+ @Override
+ public void execute(final RequestMonitor rm) {
+ if (reverseEnabled) {
+ queueCommand(new MIBreakInsert(fControlDmc, true, false, null, 0,
+ ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT, 0),
+ new DataRequestMonitor<MIBreakInsertInfo>(getExecutor(), rm) {
+ @Override
+ public void handleSuccess() {
+ if (getData() != null) {
+ MIBreakpoint[] breakpoints = getData().getMIBreakpoints();
+ if (breakpoints.length > 0 && fUserBreakpoint != null) {
+ fUserBreakpointIsOnMain = breakpoints[0].getAddress().equals(fUserBreakpoint.getAddress());
+ }
+ }
+ rm.done();
+ }
+ });
+ } else {
+ rm.done();
+ }
+ }},
+ /*
+ * Now, run the program.
+ */
+ new Step() {
+ @Override
+ public void execute(RequestMonitor rm) {
+ queueCommand(fExecCommand, new DataRequestMonitor<MIInfo>(getExecutor(), rm));
+ }},
+ /*
+ * In case of a restart, reverse debugging should be marked as off here because
+ * GDB will have turned it off. We may turn it back on after.
+ */
+ new Step() {
+ @Override
+ public void execute(RequestMonitor rm) {
+ // Although it only makes sense for a restart, it doesn't hurt
+ // do to it all the time.
+ GDBRunControl_7_0 reverseService = getServicesTracker().getService(GDBRunControl_7_0.class);
+ if (reverseService != null) {
+ reverseService.setReverseModeEnabled(false);
+ }
+ rm.done();
+ }},
+ /*
+ * Since we have started the program, we can turn on reverse debugging if needed
+ */
+ new Step() {
+ @Override
+ public void execute(RequestMonitor rm) {
+ if (reverseEnabled) {
+ IReverseRunControl reverseService = getServicesTracker().getService(IReverseRunControl.class);
+ if (reverseService != null) {
+ reverseService.enableReverseMode(fControlDmc, true, rm);
+ return;
+ }
+ }
+ rm.done();
+ }},
+ /*
+ * Finally, if we are enabling reverse, and the userSymbolStop is not on main,
+ * we should do a continue because we are currently stopped on main but that
+ * is not what the user requested
+ */
+ new Step() {
+ @Override
+ public void execute(RequestMonitor rm) {
+ if (reverseEnabled && !fUserBreakpointIsOnMain) {
+ queueCommand(new MIExecContinue(fContainerDmc),
+ new DataRequestMonitor<MIInfo>(getExecutor(), rm));
+ } else {
+ rm.done();
+ }
+ }},
+ };
+
+ @Override
+ public Step[] getSteps() {
+ return fSteps;
+ }
+ });
+ }
+
+ /**
+ * 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_7_0.this, fMIBackend, fMIBackend.getMIOutputStream());
+ } else {
+ fInferiorProcess = new GDBInferiorProcess(GDBControl_7_0.this, fMIBackend, fPty);
+ }
+ }
+
+ public boolean isConnected() {
+ return fInferiorProcess.getState() != MIInferiorProcess.State.TERMINATED &&
+ (!fMIBackend.getIsAttachSession() || fConnected > 0);
+ }
+
+ public void setConnected(boolean connected) {
+ if (connected) {
+ fConnected++;
+ } else {
+ if (fConnected > 0) fConnected--;
+ }
+ }
+
+ public AbstractCLIProcess getCLIProcess() {
+ return fCLIProcess;
+ }
+
+ public MIInferiorProcess getInferiorProcess() {
+ return fInferiorProcess;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void setTracingStream(OutputStream tracingStream) {
+ setMITracingStream(tracingStream);
+ }
+
+ @DsfServiceEventHandler
+ public void eventDispatched(ICommandControlShutdownDMEvent e) {
+ // Handle our "GDB Exited" event and stop processing commands.
+ stopCommandProcessing();
+ }
+
+ @DsfServiceEventHandler
+ public void eventDispatched(BackendStateChangedEvent e) {
+ if (e.getState() == IMIBackend.State.TERMINATED && e.getBackendId().equals(fMIBackend.getId())) {
+ // Handle "GDB Exited" event, just relay to following event.
+ getSession().dispatchEvent(new GDBControlShutdownDMEvent(fControlDmc), getProperties());
+ }
+ }
+
+ /** @since 2.0 */
+ @DsfServiceEventHandler
+ public void eventDispatched(ContainerExitedDMEvent e) {
+ if (Platform.getPreferencesService().getBoolean("org.eclipse.cdt.dsf.gdb.ui", //$NON-NLS-1$
+ IGdbDebugConstants.PREF_AUTO_TERMINATE_GDB,
+ true, null)) {
+ if (!isConnected() && !fMIBackend.getIsAttachSession()) {
+ // If the last process we are debugging finishes, let's terminate GDB
+ // but not for an attach session, since we could request to attach
+ // to another process
+ terminate(new RequestMonitor(getExecutor(), null));
+ }
+ }
+ }
+
+ public static class InitializationShutdownStep extends Sequence.Step {
+ public enum Direction { INITIALIZING, SHUTTING_DOWN }
+
+ private Direction fDirection;
+ public InitializationShutdownStep(Direction direction) { fDirection = direction; }
+
+ @Override
+ final public void execute(RequestMonitor requestMonitor) {
+ if (fDirection == Direction.INITIALIZING) {
+ initialize(requestMonitor);
+ } else {
+ shutdown(requestMonitor);
+ }
+ }
+
+ @Override
+ final public void rollBack(RequestMonitor requestMonitor) {
+ if (fDirection == Direction.INITIALIZING) {
+ shutdown(requestMonitor);
+ } else {
+ super.rollBack(requestMonitor);
+ }
+ }
+
+ protected void initialize(RequestMonitor requestMonitor) {
+ requestMonitor.done();
+ }
+ protected void shutdown(RequestMonitor requestMonitor) {
+ requestMonitor.done();
+ }
+ }
+
+ protected class CommandMonitoringStep extends InitializationShutdownStep {
+ CommandMonitoringStep(Direction direction) { super(direction); }
+
+ @Override
+ protected void initialize(final RequestMonitor requestMonitor) {
+ startCommandProcessing(fMIBackend.getMIInputStream(), fMIBackend.getMIOutputStream());
+ requestMonitor.done();
+ }
+
+ @Override
+ protected void shutdown(RequestMonitor requestMonitor) {
+ stopCommandProcessing();
+ 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); }
+
+ @Override
+ public void initialize(final RequestMonitor requestMonitor) {
+ try {
+ fCLIProcess = new GDBBackendCLIProcess(GDBControl_7_0.this, fMIBackend);
+ }
+ catch(IOException e) {
+ requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Failed to create CLI Process", e)); //$NON-NLS-1$
+ requestMonitor.done();
+ return;
+ }
+
+ createInferiorProcess();
+
+ fCLICommandProcessor = new CLIEventProcessor_7_0(GDBControl_7_0.this, fControlDmc);
+ fMIEventProcessor = new MIRunControlEventProcessor_7_0(GDBControl_7_0.this, fControlDmc);
+
+ requestMonitor.done();
+ }
+
+ @Override
+ protected void shutdown(RequestMonitor requestMonitor) {
+ fCLICommandProcessor.dispose();
+ fMIEventProcessor.dispose();
+ fCLIProcess.dispose();
+ fInferiorProcess.dispose();
+
+ requestMonitor.done();
+ }
+ }
+
+ protected class RegisterStep extends InitializationShutdownStep {
+ RegisterStep(Direction direction) { super(direction); }
+ @Override
+ public void initialize(final RequestMonitor requestMonitor) {
+ getSession().addServiceEventListener(GDBControl_7_0.this, null);
+ register(
+ new String[]{ ICommandControl.class.getName(),
+ ICommandControlService.class.getName(),
+ AbstractMIControl.class.getName(),
+ IGDBControl.class.getName() },
+ new Hashtable<String,String>());
+ getSession().dispatchEvent(new GDBControlInitializedDMEvent(fControlDmc), getProperties());
+ requestMonitor.done();
+ }
+
+ @Override
+ protected void shutdown(RequestMonitor requestMonitor) {
+ unregister();
+ getSession().removeServiceEventListener(GDBControl_7_0.this);
+ requestMonitor.done();
+ }
+ }
+}
|