diff options
author | Marc Khouzam | 2009-06-04 16:23:22 +0000 |
---|---|---|
committer | Marc Khouzam | 2009-06-04 16:23:22 +0000 |
commit | 8421cfe98640f0c37734576eec1792576b4fd0e5 (patch) | |
tree | ea3c2b5306e33aa7d4891c3b492eba3c2ddf152a /dsf-gdb | |
parent | 02f5fce4c90478995dcf73e767a672c781fd4916 (diff) | |
download | org.eclipse.cdt-8421cfe98640f0c37734576eec1792576b4fd0e5.tar.gz org.eclipse.cdt-8421cfe98640f0c37734576eec1792576b4fd0e5.tar.xz org.eclipse.cdt-8421cfe98640f0c37734576eec1792576b4fd0e5.zip |
Reverting previous commit which was done by mistake.
Diffstat (limited to 'dsf-gdb')
2 files changed, 1186 insertions, 1218 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 c6360bc632c..005bf5983fd 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,538 +1,524 @@ -/*******************************************************************************
- * 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();
- }
- }
-}
+/******************************************************************************* + * 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(); + } + } +} 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 efd9e0f0b41..83170ec69c4 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,680 +1,662 @@ -/*******************************************************************************
- * 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();
- }
- }
-}
+/******************************************************************************* + * 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(); + } + } +} |