diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java index 4b0fd4dc2e7..cca13a77e99 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2011 Ericsson and others. + * Copyright (c) 2008, 2010 Ericsson 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 @@ -15,35 +15,30 @@ package org.eclipse.cdt.dsf.gdb.launching; import java.util.List; import java.util.Map; -import java.util.Properties; +import org.eclipse.cdt.debug.core.CDebugUtils; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; import org.eclipse.cdt.dsf.concurrent.ReflectionSequence; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.DataModelInitializedEvent; import org.eclipse.cdt.dsf.datamodel.IDMContext; -import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; -import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants; import org.eclipse.cdt.dsf.gdb.actions.IConnect; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.service.IGDBBackend; -import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses; import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl; import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceTargetDMContext; import org.eclipse.cdt.dsf.gdb.service.SessionType; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; import org.eclipse.cdt.dsf.mi.service.CSourceLookup; -import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; import org.eclipse.cdt.dsf.mi.service.IMIProcesses; -import org.eclipse.cdt.dsf.mi.service.MIBreakpointsManager; -import org.eclipse.cdt.dsf.mi.service.MIProcesses; import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; import org.eclipse.cdt.dsf.service.DsfServicesTracker; @@ -62,6 +57,9 @@ public class FinalLaunchSequence extends ReflectionSequence { private GdbLaunch fLaunch; private SessionType fSessionType; private boolean fAttach; + + // The launchConfiguration attributes + private Map fAttributes; private IGDBControl fCommandControl; private IGDBBackend fGDBBackend; @@ -82,22 +80,24 @@ public class FinalLaunchSequence extends ReflectionSequence { if (GROUP_TOP_LEVEL.equals(group)) { return new String[] { "stepInitializeFinalLaunchSequence", //$NON-NLS-1$ + // Global GDB settings "stepSetEnvironmentDirectory", //$NON-NLS-1$ "stepSetBreakpointPending", //$NON-NLS-1$ "stepEnablePrettyPrinting", //$NON-NLS-1$ "stepSourceGDBInitFile", //$NON-NLS-1$ - "stepSetEnvironmentVariables", //$NON-NLS-1$ - "stepSetExecutable", //$NON-NLS-1$ - "stepSetArguments", //$NON-NLS-1$ "stepSetNonStop", //$NON-NLS-1$ "stepSetAutoLoadSharedLibrarySymbols", //$NON-NLS-1$ "stepSetSharedLibraryPaths", //$NON-NLS-1$ "stepSetSourceLookupPath", //$NON-NLS-1$ + // For post-mortem launch only "stepSpecifyCoreFile", //$NON-NLS-1$ + // For remote-attach launch only "stepRemoteConnection", //$NON-NLS-1$ + // For all launches except attach ones + "stepNewProcess", //$NON-NLS-1$ + // For local attach launch only "stepAttachToProcess", //$NON-NLS-1$ - "stepStartTrackingBreakpoints", //$NON-NLS-1$ - "stepStartExecution", //$NON-NLS-1$ + // Global "stepDataModelInitializationComplete", //$NON-NLS-1$ "stepCleanup", //$NON-NLS-1$ }; @@ -110,6 +110,7 @@ public class FinalLaunchSequence extends ReflectionSequence { * This step is mandatory for the rest fo the sequence to complete. * @since 4.0 */ + @SuppressWarnings("unchecked") @Execute public void stepInitializeFinalLaunchSequence(RequestMonitor requestMonitor) { fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fLaunch.getSession().getId()); @@ -136,6 +137,14 @@ public class FinalLaunchSequence extends ReflectionSequence { return; } + try { + fAttributes = fLaunch.getLaunchConfiguration().getAttributes(); + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain launch configuration attributes", null)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } + requestMonitor.done(); } @@ -247,91 +256,16 @@ public class FinalLaunchSequence extends ReflectionSequence { } } - /** - * Specify environment variables if needed - * @since 4.0 - */ - @Execute - public void stepSetEnvironmentVariables(final RequestMonitor requestMonitor) { - boolean clear = false; - Properties properties = new Properties(); - try { - clear = fGDBBackend.getClearEnvironment(); - properties = fGDBBackend.getEnvironmentVariables(); - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get environment information", e)); //$NON-NLS-1$ - requestMonitor.done(); - return; - } - - if (clear == true || properties.size() > 0) { - fCommandControl.setEnvironment(properties, clear, requestMonitor); - } else { - requestMonitor.done(); - } - } - - /** - * Specify the executable file to be debugged and read the symbol table. - * @since 4.0 - */ - @Execute - public void stepSetExecutable(final RequestMonitor requestMonitor) { - boolean noFileCommand = IGDBLaunchConfigurationConstants.DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP_DEFAULT; - try { - noFileCommand = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP, - IGDBLaunchConfigurationConstants.DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP_DEFAULT); - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot read use solib symbols for app options", e)); //$NON-NLS-1$ - requestMonitor.done(); - return; - } - - final IPath execPath = fGDBBackend.getProgramPath(); - if (!noFileCommand && execPath != null && !execPath.isEmpty()) { - fCommandControl.queueCommand( - fCommandFactory.createMIFileExecAndSymbols(fCommandControl.getContext(), - execPath.toPortableString()), - new DataRequestMonitor(getExecutor(), requestMonitor)); - } else { - requestMonitor.done(); - } - } - - /** - * Specify the arguments to the executable file. - * @since 4.0 - */ - @Execute - public void stepSetArguments(final RequestMonitor requestMonitor) { - try { - String args = fGDBBackend.getProgramArguments(); - - if (args != null) { - fCommandControl.queueCommand( - fCommandFactory.createMIGDBSetArgs(fCommandControl.getContext(), args), - new DataRequestMonitor(getExecutor(), requestMonitor)); - } else { - requestMonitor.done(); - } - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get inferior arguments", e)); //$NON-NLS-1$ - requestMonitor.done(); - } - } - /** * Enable non-stop mode if requested. * @since 4.0 */ @Execute public void stepSetNonStop(final RequestMonitor requestMonitor) { - boolean isNonStop = false; - try { - isNonStop = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, - IGDBLaunchConfigurationConstants.DEBUGGER_NON_STOP_DEFAULT); - } catch (CoreException e) { - } + boolean isNonStop = CDebugUtils.getAttribute( + fAttributes, + IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, + IGDBLaunchConfigurationConstants.DEBUGGER_NON_STOP_DEFAULT); // GDBs that don't support non-stop don't allow you to set it to false. // We really should set it to false when GDB supports it though. @@ -365,16 +299,14 @@ public class FinalLaunchSequence extends ReflectionSequence { */ @Execute public void stepSetAutoLoadSharedLibrarySymbols(RequestMonitor requestMonitor) { - try { - boolean autolib = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_AUTO_SOLIB, - IGDBLaunchConfigurationConstants.DEBUGGER_AUTO_SOLIB_DEFAULT); - fCommandControl.queueCommand( - fCommandFactory.createMIGDBSetAutoSolib(fCommandControl.getContext(), autolib), - new DataRequestMonitor(getExecutor(), requestMonitor)); - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot set shared library option", e)); //$NON-NLS-1$ - requestMonitor.done(); - } + boolean autolib = CDebugUtils.getAttribute( + fAttributes, + IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_AUTO_SOLIB, + IGDBLaunchConfigurationConstants.DEBUGGER_AUTO_SOLIB_DEFAULT); + + fCommandControl.queueCommand( + fCommandFactory.createMIGDBSetAutoSolib(fCommandControl.getContext(), autolib), + new DataRequestMonitor(getExecutor(), requestMonitor)); } /** @@ -476,10 +408,14 @@ public class FinalLaunchSequence extends ReflectionSequence { @Execute public void stepSpecifyCoreFile(final RequestMonitor requestMonitor) { if (fSessionType == SessionType.CORE) { - try { - String coreFile = fLaunch.getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_COREFILE_PATH, ""); //$NON-NLS-1$ - final String coreType = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_POST_MORTEM_TYPE, - IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_TYPE_DEFAULT); + String coreFile = CDebugUtils.getAttribute( + fAttributes, + ICDTLaunchConfigurationConstants.ATTR_COREFILE_PATH, ""); //$NON-NLS-1$ + final String coreType = CDebugUtils.getAttribute( + fAttributes, + IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_POST_MORTEM_TYPE, + IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_TYPE_DEFAULT); + if (coreFile.length() == 0) { new PromptForCoreJob( "Prompt for post mortem file", //$NON-NLS-1$ @@ -530,124 +466,93 @@ public class FinalLaunchSequence extends ReflectionSequence { requestMonitor.done(); } } - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get post mortem file path", e)); - requestMonitor.done(); - } } else { requestMonitor.done(); } } - - - private boolean fTcpConnection; - private String fRemoteTcpHost; - private String fRemoteTcpPort; - private String fSerialDevice; - - private boolean checkConnectionType(RequestMonitor requestMonitor) { - try { - fTcpConnection = fLaunch.getLaunchConfiguration().getAttribute( - IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, - false); - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve connection mode", e)); //$NON-NLS-1$ - requestMonitor.done(); - return false; - } - return true; - } - - private boolean getSerialDevice(RequestMonitor requestMonitor) { - try { - fSerialDevice = fLaunch.getLaunchConfiguration().getAttribute( - IGDBLaunchConfigurationConstants.ATTR_DEV, "invalid"); //$NON-NLS-1$ - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve serial device", e)); //$NON-NLS-1$ - requestMonitor.done(); - return false; - } - return true; - } - - private boolean getTcpHost(RequestMonitor requestMonitor) { - try { - fRemoteTcpHost = fLaunch.getLaunchConfiguration().getAttribute( - IGDBLaunchConfigurationConstants.ATTR_HOST, "invalid"); //$NON-NLS-1$ - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve remote TCP host", e)); //$NON-NLS-1$ - requestMonitor.done(); - return false; - } - return true; - } - - private boolean getTcpPort(RequestMonitor requestMonitor) { - try { - fRemoteTcpPort = fLaunch.getLaunchConfiguration().getAttribute( - IGDBLaunchConfigurationConstants.ATTR_PORT, "invalid"); //$NON-NLS-1$ - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve remote TCP port", e)); //$NON-NLS-1$ - requestMonitor.done(); - return false; - } - return true; - } - + + private final static String INVALID = "invalid"; //$NON-NLS-1$ /** - * If we are dealing with a remote debugging session, connect to the target. + * If we are dealing with a remote-attach debugging session, connect to the target. * @since 4.0 */ @Execute - public void stepRemoteConnection(final RequestMonitor requestMonitor) { - if (fSessionType == SessionType.REMOTE) { - if (!checkConnectionType(requestMonitor)) return; + public void stepRemoteConnection(final RequestMonitor rm) { + if (fSessionType == SessionType.REMOTE && fAttach) { + boolean isTcpConnection = CDebugUtils.getAttribute( + fAttributes, + IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, + false); - if (fTcpConnection) { - if (!getTcpHost(requestMonitor)) return; - if (!getTcpPort(requestMonitor)) return; + if (isTcpConnection) { + String remoteTcpHost = CDebugUtils.getAttribute( + fAttributes, + IGDBLaunchConfigurationConstants.ATTR_HOST, INVALID); + String remoteTcpPort = CDebugUtils.getAttribute( + fAttributes, + IGDBLaunchConfigurationConstants.ATTR_PORT, INVALID); fCommandControl.queueCommand( fCommandFactory.createMITargetSelect(fCommandControl.getContext(), - fRemoteTcpHost, fRemoteTcpPort, fAttach), - new DataRequestMonitor(getExecutor(), requestMonitor)); + remoteTcpHost, remoteTcpPort, true), + new DataRequestMonitor(ImmediateExecutor.getInstance(), rm)); } else { - if (!getSerialDevice(requestMonitor)) return; + String serialDevice = CDebugUtils.getAttribute( + fAttributes, + IGDBLaunchConfigurationConstants.ATTR_DEV, INVALID); fCommandControl.queueCommand( fCommandFactory.createMITargetSelect(fCommandControl.getContext(), - fSerialDevice, fAttach), - new DataRequestMonitor(getExecutor(), requestMonitor)); + serialDevice, true), + new DataRequestMonitor(ImmediateExecutor.getInstance(), rm)); } } else { - requestMonitor.done(); + rm.done(); } } /** - * If we are dealing with an attach debugging session, perform the attach. + * Start a new process if we are not dealing with an attach session + * i.e., a local session, a remote session or a post-mortem (core) session. + * @since 4.0 + */ + @Execute + public void stepNewProcess(final RequestMonitor rm) { + if (!fAttach) { + + boolean noBinarySpecified = CDebugUtils.getAttribute( + fAttributes, + IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP, + IGDBLaunchConfigurationConstants.DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP_DEFAULT); + + String binary = null; + final IPath execPath = fGDBBackend.getProgramPath(); + if (!noBinarySpecified && execPath != null && !execPath.isEmpty()) { + binary = execPath.toPortableString(); + } + + // Even if binary is null, we must call this to do all the other steps + // necessary to create a process. It is possible that the binary is not needed + fProcService.debugNewProcess(fCommandControl.getContext(), binary, fAttributes, + new DataRequestMonitor(getExecutor(), rm)); + } else { + rm.done(); + } + } + + /** + * If we are dealing with an local attach session, perform the attach. + * For a remote attach session, we don't attach during the launch; instead + * we wait for the user to manually do the attach. * @since 4.0 */ @Execute public void stepAttachToProcess(final RequestMonitor requestMonitor) { - // A local attach can figure out the binary from the attach - // command. This allows the user not to specify the binary - // in the launch. But for breakpoints to work, we must do the - // attach before we set the breakpoints, i.e., here. - // On the other hand, for a remote attach, we need to specify - // the binary anyway, or use the solib command. In both cases, - // breakpoints can be set before we attach. Therefore, we don't - // force an attach here, but wait for the user to decide to connect - // using the connect action. if (fAttach && fSessionType != SessionType.REMOTE) { - // If we are attaching, get the process id. - int pid = -1; - try { - // have we already been given the pid (maybe from a JUnit test launch or something) - pid = fLaunch.getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_ATTACH_PROCESS_ID, -1); - } catch (CoreException e) { - // do nothing and fall to below - } + // Is the process id already stored in the launch? + int pid = CDebugUtils.getAttribute( + fAttributes, + ICDTLaunchConfigurationConstants.ATTR_ATTACH_PROCESS_ID, -1); if (pid != -1) { fProcService.attachDebuggerToProcess( @@ -666,48 +571,6 @@ public class FinalLaunchSequence extends ReflectionSequence { } } - /** - * Start tracking the breakpoints. Note that for remote debugging - * we should first connect to the target. - * @since 4.0 - */ - @Execute - public void stepStartTrackingBreakpoints(final RequestMonitor requestMonitor) { - if (fSessionType != SessionType.CORE) { - MIBreakpointsManager bpmService = fTracker.getService(MIBreakpointsManager.class); - IMIContainerDMContext containerDmc = fProcService.createContainerContextFromGroupId(fCommandControl.getContext(), null); - IBreakpointsTargetDMContext bpTargetDmc = DMContexts.getAncestorOfType(containerDmc, IBreakpointsTargetDMContext.class); - - bpmService.startTrackingBreakpoints(bpTargetDmc, requestMonitor); - } else { - requestMonitor.done(); - } - } - - /** - * Start executing the program. - * @since 4.0 - */ - @SuppressWarnings("unchecked") - @Execute - public void stepStartExecution(final RequestMonitor requestMonitor) { - if (fSessionType != SessionType.CORE) { - Map attributes = null; - try { - attributes = fLaunch.getLaunchConfiguration().getAttributes(); - } catch (CoreException e) {} - - IGDBProcesses procService = fTracker.getService(IGDBProcesses.class); - IContainerDMContext containerDmc = procService.createContainerContextFromGroupId(fCommandControl.getContext(), MIProcesses.UNIQUE_GROUP_ID); - - // For now, call restart since it does the same as start - // but this is just temporary until procService.debugNewProcess is ready - procService.restart(containerDmc, attributes, requestMonitor); - } else { - requestMonitor.done(); - } - } - /** * Indicate that the Data Model has been filled. This will trigger the Debug view to expand. * @since 4.0 diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/DebugNewProcessSequence.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/DebugNewProcessSequence.java new file mode 100644 index 00000000000..501baf0485c --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/DebugNewProcessSequence.java @@ -0,0 +1,302 @@ +/******************************************************************************* + * Copyright (c) 2011 Ericsson 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service; + +import java.util.Map; +import java.util.Properties; + +import org.eclipse.cdt.debug.core.CDebugUtils; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.ReflectionSequence; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; +import org.eclipse.cdt.dsf.mi.service.IMICommandControl; +import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; +import org.eclipse.cdt.dsf.mi.service.MIBreakpointsManager; +import org.eclipse.cdt.dsf.mi.service.MIProcesses; +import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * @since 4.0 + */ +public class DebugNewProcessSequence extends ReflectionSequence { + + private final static String INVALID = "invalid"; //$NON-NLS-1$ + + private IGDBControl fCommandControl; + private CommandFactory fCommandFactory; + private IGDBBackend fBackend; + private IGDBProcesses fProcService; + private DsfServicesTracker fTracker; + + private IDMContext fContext; + private String fBinaryName; + private Map fAttributes; + private IMIContainerDMContext fContainerCtx; + + // Store the dataRM so that we can fill it with the container context that we will be creating + private DataRequestMonitor fDataRequestMonitor; + + + protected IMIContainerDMContext getContainerContext() { + return fContainerCtx; + } + + protected void setContainerContext(IMIContainerDMContext ctx) { + fContainerCtx = ctx; + } + + public DebugNewProcessSequence(DsfExecutor executor, IDMContext dmc, String file, Map attributes, DataRequestMonitor rm) { + super(executor, rm); + fContext = dmc; + fBinaryName = file; + fAttributes = attributes; + fDataRequestMonitor = rm; + } + + @Override + protected String[] getExecutionOrder(String group) { + if (GROUP_TOP_LEVEL.equals(group)) { + return new String[] { + "stepInitializeBaseSequence", //$NON-NLS-1$ + "stepSetEnvironmentVariables", //$NON-NLS-1$ + "stepSetExecutable", //$NON-NLS-1$ + "stepSetArguments", //$NON-NLS-1$ + "stepRemoteConnection", //$NON-NLS-1$ + "stepStartTrackingBreakpoints", //$NON-NLS-1$ + "stepStartExecution", //$NON-NLS-1$ + "stepCleanupBaseSequence", //$NON-NLS-1$ + }; + } + return null; + } + + /** + * Initialize the members of the {@link StartOrRestartProcessSequence_7_0} class. + * This step is mandatory for the rest of the sequence to complete. + */ + @Execute + public void stepInitializeBaseSequence(RequestMonitor rm) { + fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fContext.getSessionId()); + fBackend = fTracker.getService(IGDBBackend.class); + fCommandControl = fTracker.getService(IGDBControl.class); + fCommandFactory = fTracker.getService(IMICommandControl.class).getCommandFactory(); + fProcService = fTracker.getService(IGDBProcesses.class); + + if (fBackend == null || fCommandControl == null || fCommandFactory == null || fProcService == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Cannot obtain service", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // When we are starting to debug a new process, the container is the default process used by GDB. + // We don't have a pid yet, so we can simply create the container with the UNIQUE_GROUP_ID + setContainerContext(fProcService.createContainerContextFromGroupId(fCommandControl.getContext(), MIProcesses.UNIQUE_GROUP_ID)); + + rm.done(); + } + + /** + * Rollback method for {@link #stepInitializeBaseSequence()} + * @since 4.0 + */ + @RollBack("stepInitializeBaseSequence") + public void rollBackInitializeBaseSequence(RequestMonitor rm) { + if (fTracker != null) fTracker.dispose(); + fTracker = null; + rm.done(); + } + + /** + * Specify environment variables if needed + */ + @Execute + public void stepSetEnvironmentVariables(RequestMonitor rm) { + boolean clear = false; + Properties properties = new Properties(); + try { + // here we need to pass the proper container context + clear = fBackend.getClearEnvironment(); + properties = fBackend.getEnvironmentVariables(); + } catch (CoreException e) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Cannot get environment information", e)); //$NON-NLS-1$ + rm.done(); + return; + } + + if (clear == true || properties.size() > 0) { + // here we need to pass the proper container context + fCommandControl.setEnvironment(properties, clear, rm); + } else { + rm.done(); + } + } + + /** + * Specify the executable file to be debugged and read the symbol table. + */ + @Execute + public void stepSetExecutable(RequestMonitor rm) { + boolean noFileCommand = CDebugUtils.getAttribute( + fAttributes, + IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP, + IGDBLaunchConfigurationConstants.DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP_DEFAULT); + + if (!noFileCommand && fBinaryName != null && fBinaryName.length() > 0) { + fCommandControl.queueCommand( + fCommandFactory.createMIFileExecAndSymbols(getContainerContext(), fBinaryName), + new DataRequestMonitor(ImmediateExecutor.getInstance(), rm)); + } else { + rm.done(); + } + } + + /** + * Specify the arguments to the program that will be run. + */ + @Execute + public void stepSetArguments(RequestMonitor rm) { + try { + String args = fBackend.getProgramArguments(); + + if (args != null) { + fCommandControl.queueCommand( + // here we need to pass the proper container context + fCommandFactory.createMIGDBSetArgs(fCommandControl.getContext(), args), + new DataRequestMonitor(ImmediateExecutor.getInstance(), rm)); + } else { + rm.done(); + } + } catch (CoreException e) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Cannot get inferior arguments", e)); //$NON-NLS-1$ + rm.done(); + } + } + + /** + * If we are dealing with a remote debugging session, connect to the target. + * @since 4.0 + */ + @Execute + public void stepRemoteConnection(RequestMonitor rm) { + // If we are dealing with a remote session, it is now time to connect + // to the remote side. Note that this is the 'target remote' case + // and not the 'target extended-remote' case (remote attach session) + // We know this because a remote attach session does not start a new + // process, so we wouldn't be in this sequence + // This step is actually global for GDB. However, we have to do it after + // we have specified the executable, so we have to do it here. + // It is safe to do it here because a 'target remote' does not support + // multi-process so this step will not be executed more than once. + + assert fBackend.getIsAttachSession() == false; + + if (fBackend.getSessionType() == SessionType.REMOTE) { + boolean isTcpConnection = CDebugUtils.getAttribute( + fAttributes, + IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, + false); + + if (isTcpConnection) { + String remoteTcpHost = CDebugUtils.getAttribute( + fAttributes, + IGDBLaunchConfigurationConstants.ATTR_HOST, INVALID); + String remoteTcpPort = CDebugUtils.getAttribute( + fAttributes, + IGDBLaunchConfigurationConstants.ATTR_PORT, INVALID); + + fCommandControl.queueCommand( + fCommandFactory.createMITargetSelect(fCommandControl.getContext(), + remoteTcpHost, remoteTcpPort, false), + new DataRequestMonitor(ImmediateExecutor.getInstance(), rm)); + } else { + String serialDevice = CDebugUtils.getAttribute( + fAttributes, + IGDBLaunchConfigurationConstants.ATTR_DEV, INVALID); + fCommandControl.queueCommand( + fCommandFactory.createMITargetSelect(fCommandControl.getContext(), + serialDevice, false), + new DataRequestMonitor(ImmediateExecutor.getInstance(), rm)); + } + } else { + rm.done(); + } + } + + /** + * Start tracking the breakpoints. Note that for remote debugging + * we should first connect to the target. + */ + @Execute + public void stepStartTrackingBreakpoints(RequestMonitor rm) { + if (fBackend.getSessionType() != SessionType.CORE) { + MIBreakpointsManager bpmService = fTracker.getService(MIBreakpointsManager.class); + IBreakpointsTargetDMContext bpTargetDmc = DMContexts.getAncestorOfType(getContainerContext(), IBreakpointsTargetDMContext.class); + bpmService.startTrackingBreakpoints(bpTargetDmc, rm); + } else { + rm.done(); + } + } + + /** + * Start executing the program. + */ + @Execute + public void stepStartExecution(final RequestMonitor rm) { + if (fBackend.getSessionType() != SessionType.CORE) { + ImmediateExecutor.getInstance().execute( + new StartOrRestartProcessSequence_7_0( + getExecutor(), getContainerContext(), fAttributes, false, + new DataRequestMonitor(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + assert getData() instanceof IMIContainerDMContext; + + // Set the container that we created + setContainerContext(DMContexts.getAncestorOfType(getData(), IMIContainerDMContext.class)); + fDataRequestMonitor.setData(getContainerContext()); + + // Don't call fDataRequestMonitor.done(), the sequence will + // automatically do that when we call rm.done(); + rm.done(); + } + })); + } else { + fDataRequestMonitor.setData(getContainerContext()); + rm.done(); + } + } + + /** + * Cleanup now that the sequence has been run. + * @since 4.0 + */ + @Execute + public void stepCleanupBaseSequence(RequestMonitor rm) { + fTracker.dispose(); + fTracker = null; + rm.done(); + } +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/DebugNewProcessSequence_7_2.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/DebugNewProcessSequence_7_2.java new file mode 100644 index 00000000000..8ab4c69ca51 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/DebugNewProcessSequence_7_2.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2011 Ericsson 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; +import org.eclipse.cdt.dsf.mi.service.command.output.MIAddInferiorInfo; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * With GDB 7.2, to create a new process, we need to use the -add-inferior command first. + * This allows to create multiple processes, unlike previous versions of GDB. + * Note that GDB 7.1 does support multi-process but didn't have the MI commands (e.g., -add-inferior) + * so we only support multi-process starting with 7.2 + * + * @since 4.0 + */ +public class DebugNewProcessSequence_7_2 extends DebugNewProcessSequence { + + private IGDBControl fGdbControl; + private IGDBProcesses fProcService; + private String fSessionId; + private boolean fInitialProcess; + + public DebugNewProcessSequence_7_2(DsfExecutor executor, boolean initialProcess, IDMContext dmc, String file, + Map attributes, DataRequestMonitor rm) { + super(executor, dmc, file, attributes, rm); + fSessionId = dmc.getSessionId(); + fInitialProcess = initialProcess; + } + + @Override + protected String[] getExecutionOrder(String group) { + if (GROUP_TOP_LEVEL.equals(group)) { + // Initialize the list with the base class' steps + // We need to create a list that we can modify, which is why we create our own ArrayList. + List orderList = new ArrayList(Arrays.asList(super.getExecutionOrder(GROUP_TOP_LEVEL))); + + // Now insert our steps right after the initialization of the base class. + orderList.add(orderList.indexOf("stepInitializeBaseSequence") + 1, "stepInitializeSequence_7_2"); //$NON-NLS-1$ //$NON-NLS-2$ + orderList.add(orderList.indexOf("stepInitializeSequence_7_2") + 1, "stepAddInferior"); //$NON-NLS-1$ //$NON-NLS-2$ + + return orderList.toArray(new String[orderList.size()]); + } + + return null; + } + + /** + * Initialize the members of the {@link DebugNewProcessSequence_7_2} class. + * This step is mandatory for the rest of the sequence to complete. + */ + @Execute + public void stepInitializeSequence_7_2(RequestMonitor rm) { + DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSessionId); + fGdbControl = tracker.getService(IGDBControl.class); + fProcService = tracker.getService(IGDBProcesses.class); + tracker.dispose(); + + if (fGdbControl == null || fProcService == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Cannot obtain service", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + rm.done(); + } + + /** + * Add a new inferior. + */ + @Execute + public void stepAddInferior(final RequestMonitor rm) { + if (fInitialProcess) { + // The first process is automatically created by GDB. We actually want to use that first created process + // instead of ignoring it and creating a new one for a couple of reasons: + // 1- post-mortem and non-attach remote sessions don't support creating a new process + // 2- commands that were part of the .gdbinit file will affect the initial process, which is what the user expects, + // but would not affect a new process we created instead. + setContainerContext(fProcService.createContainerContextFromGroupId(fGdbControl.getContext(), "i1")); //$NON-NLS-1$ + rm.done(); + return; + } + + fGdbControl.queueCommand( + fGdbControl.getCommandFactory().createMIAddInferior(fGdbControl.getContext()), + new DataRequestMonitor(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + final String groupId = getData().getGroupId(); + if (groupId == null || groupId.trim().length() == 0) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Invalid gdb group id.", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + setContainerContext(fProcService.createContainerContextFromGroupId(fGdbControl.getContext(), groupId)); + rm.done(); + } + }); + } + } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses.java index 811506571f2..da90ecd1f5c 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses.java @@ -20,6 +20,7 @@ import org.eclipse.cdt.core.IProcessList; import org.eclipse.cdt.debug.core.CDebugUtils; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.IDMContext; @@ -37,6 +38,7 @@ import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; import org.eclipse.cdt.dsf.mi.service.IMIProcessDMContext; import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIBreakpointsManager; import org.eclipse.cdt.dsf.mi.service.MIProcesses; import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess; @@ -46,6 +48,7 @@ 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.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.osgi.framework.BundleContext; @@ -200,22 +203,51 @@ public class GDBProcesses extends MIProcesses implements IGDBProcesses { @Override public void attachDebuggerToProcess(final IProcessDMContext procCtx, final DataRequestMonitor rm) { - super.attachDebuggerToProcess( - procCtx, - new DataRequestMonitor(getExecutor(), rm) { - @Override - protected void handleSuccess() { - fGdb.setConnected(true); + // For remote attach, we must set the binary first + // For a local attach, GDB can figure out the binary automatically, + // so we don't specify it. + + IMIContainerDMContext containerDmc = createContainerContext(procCtx, MIProcesses.UNIQUE_GROUP_ID); - MIInferiorProcess inferiorProcess = fGdb.getInferiorProcess(); - if (inferiorProcess != null) { - inferiorProcess.setPid(((IMIProcessDMContext)procCtx).getProcId()); - } + DataRequestMonitor attachRm = new DataRequestMonitor(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + GDBProcesses.super.attachDebuggerToProcess( + procCtx, + new DataRequestMonitor(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + fGdb.setConnected(true); - rm.setData(getData()); - rm.done(); - } - }); + MIInferiorProcess inferiorProcess = fGdb.getInferiorProcess(); + if (inferiorProcess != null) { + inferiorProcess.setPid(((IMIProcessDMContext)procCtx).getProcId()); + } + + IDMContext containerDmc = getData(); + rm.setData(containerDmc); + + // Start tracking breakpoints. + MIBreakpointsManager bpmService = getServicesTracker().getService(MIBreakpointsManager.class); + IBreakpointsTargetDMContext bpTargetDmc = DMContexts.getAncestorOfType(containerDmc, IBreakpointsTargetDMContext.class); + bpmService.startTrackingBreakpoints(bpTargetDmc, rm); + } + }); + } + }; + + if (fBackend.getSessionType() == SessionType.REMOTE) { + final IPath execPath = fBackend.getProgramPath(); + if (execPath != null && !execPath.isEmpty()) { + fGdb.queueCommand( + fCommandFactory.createMIFileExecAndSymbols(containerDmc, execPath.toPortableString()), + attachRm); + return; + } + } + + // If we get here, let's do the attach by completing the requestMonitor + attachRm.done(); } @Override @@ -243,6 +275,13 @@ public class GDBProcesses extends MIProcesses implements IGDBProcesses { }); } + @Override + public void debugNewProcess(IDMContext dmc, String file, + Map attributes, DataRequestMonitor rm) { + ImmediateExecutor.getInstance().execute( + new DebugNewProcessSequence(getExecutor(), dmc, file, attributes, rm)); + } + @Override public void getProcessesBeingDebugged(IDMContext dmc, DataRequestMonitor rm) { MIInferiorProcess inferiorProcess = fGdb.getInferiorProcess(); diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_0.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_0.java index 18e6f314bf5..45b5288b9f0 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_0.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_0.java @@ -27,6 +27,7 @@ import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; import org.eclipse.cdt.dsf.concurrent.Immutable; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; import org.eclipse.cdt.dsf.datamodel.DMContexts; @@ -47,6 +48,7 @@ import org.eclipse.cdt.dsf.debug.service.command.BufferedCommandControl; import org.eclipse.cdt.dsf.debug.service.command.CommandCache; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; import org.eclipse.cdt.dsf.debug.service.command.IEventListener; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; import org.eclipse.cdt.dsf.mi.service.IMICommandControl; @@ -54,6 +56,7 @@ import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; import org.eclipse.cdt.dsf.mi.service.IMIProcessDMContext; import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIBreakpointsManager; import org.eclipse.cdt.dsf.mi.service.MIProcesses; import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadGroupCreatedEvent; @@ -73,8 +76,10 @@ import org.eclipse.cdt.dsf.service.AbstractDsfService; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.ILaunch; import org.osgi.framework.BundleContext; /** @@ -430,6 +435,8 @@ public class GDBProcesses_7_0 extends AbstractDsfService private static final String FAKE_THREAD_ID = "0"; //$NON-NLS-1$ + private boolean fInitialProcess = true; + public GDBProcesses_7_0(DsfSession session) { super(session); } @@ -639,8 +646,7 @@ public class GDBProcesses_7_0 extends AbstractDsfService } else if (name.length() == 0) { // Probably will not happen, but just in case...use the // binary file name (absolute path) - IGDBBackend backend = getServicesTracker().getService(IGDBBackend.class); - name = backend.getProgramPath().toOSString(); + name = fBackend.getProgramPath().toOSString(); fDebuggedProcessesAndNames.put(id, name); } } @@ -702,11 +708,7 @@ public class GDBProcesses_7_0 extends AbstractDsfService /** @since 4.0 */ protected boolean doIsDebuggerAttachSupported() { - IGDBBackend backend = getServicesTracker().getService(IGDBBackend.class); - if (backend != null) { - return backend.getIsAttachSession(); - } - return false; + return fBackend.getIsAttachSession() && !fCommandControl.isConnected(); } public void isDebuggerAttachSupported(IDMContext dmc, DataRequestMonitor rm) { @@ -714,39 +716,102 @@ public class GDBProcesses_7_0 extends AbstractDsfService rm.done(); } - public void attachDebuggerToProcess(final IProcessDMContext procCtx, final DataRequestMonitor rm) { + public void attachDebuggerToProcess(final IProcessDMContext procCtx, final DataRequestMonitor dataRm) { if (procCtx instanceof IMIProcessDMContext) { if (!doIsDebuggerAttachSupported()) { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Attach not supported.", null)); //$NON-NLS-1$ - rm.done(); + dataRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Attach not supported.", null)); //$NON-NLS-1$ + dataRm.done(); return; } - ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(procCtx, ICommandControlDMContext.class); - fCommandControl.queueCommand( - fCommandFactory.createMITargetAttach(controlDmc, ((IMIProcessDMContext)procCtx).getProcId()), - new DataRequestMonitor(getExecutor(), rm) { - @Override - protected void handleSuccess() { - // By now, GDB has reported the groupId that was attached to this process - rm.setData(createContainerContext(procCtx, getGroupFromPid(((IMIProcessDMContext)procCtx).getProcId()))); - rm.done(); - } - }); + // Use a sequence for better control of each step + ImmediateExecutor.getInstance().execute(new Sequence(getExecutor(), dataRm) { + + private IMIContainerDMContext fContainerDmc; + + private Step[] steps = new Step[] { + // For remote attach, we must set the binary first + // For a local attach, GDB can figure out the binary automatically, + // so we don't specify it. + new Step() { + @Override + public void execute(RequestMonitor rm) { + // There is no groupId until we attach, so we can use the default groupId + fContainerDmc = createContainerContext(procCtx, MIProcesses.UNIQUE_GROUP_ID); + if (fBackend.getSessionType() == SessionType.REMOTE) { + final IPath execPath = fBackend.getProgramPath(); + if (execPath != null && !execPath.isEmpty()) { + fCommandControl.queueCommand( + fCommandFactory.createMIFileExecAndSymbols(fContainerDmc, execPath.toPortableString()), + new DataRequestMonitor(ImmediateExecutor.getInstance(), rm)); + return; + } + } + + rm.done(); + } + }, + // Attach to the process + new Step() { + @Override + public void execute(RequestMonitor rm) { + fCommandControl.queueCommand( + fCommandFactory.createMITargetAttach(fContainerDmc, ((IMIProcessDMContext)procCtx).getProcId()), + new DataRequestMonitor(getExecutor(), rm)); + } + }, + new Step() { + @Override + public void execute(RequestMonitor rm) { + // By now, GDB has reported the groupId that was created for this process + fContainerDmc = createContainerContext(procCtx, getGroupFromPid(((IMIProcessDMContext)procCtx).getProcId())); + // Store the fully formed container context so it can be returned to the caller. + dataRm.setData(fContainerDmc); + + // Start tracking breakpoints. + MIBreakpointsManager bpmService = getServicesTracker().getService(MIBreakpointsManager.class); + IBreakpointsTargetDMContext bpTargetDmc = DMContexts.getAncestorOfType(fContainerDmc, IBreakpointsTargetDMContext.class); + bpmService.startTrackingBreakpoints(bpTargetDmc, rm); + } + }, + // Turn on reverse debugging if it was enabled as a launch option + new Step() { + @Override + public void execute(RequestMonitor rm) { + IReverseRunControl reverseService = getServicesTracker().getService(IReverseRunControl.class); + if (reverseService != null) { + ILaunch launch = (ILaunch)procCtx.getAdapter(ILaunch.class); + if (launch != null) { + try { + boolean reverseEnabled = + launch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REVERSE, + IGDBLaunchConfigurationConstants.DEBUGGER_REVERSE_DEFAULT); + if (reverseEnabled) { + reverseService.enableReverseMode(fCommandControl.getContext(), true, rm); + return; + } + } catch (CoreException e) { + // Ignore, just don't set reverse + } + } + } + rm.done(); + } + }, + }; + + @Override public Step[] getSteps() { return steps; } + }); } else { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid process context.", null)); //$NON-NLS-1$ - rm.done(); + dataRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid process context.", null)); //$NON-NLS-1$ + dataRm.done(); } } /** @since 4.0 */ protected boolean doCanDetachDebuggerFromProcess() { - IGDBBackend backend = getServicesTracker().getService(IGDBBackend.class); - if (backend != null) { - return backend.getIsAttachSession() && fCommandControl.isConnected(); - } - return false; + return fBackend.getIsAttachSession() && fCommandControl.isConnected(); } public void canDetachDebuggerFromProcess(IDMContext dmc, DataRequestMonitor rm) { @@ -781,15 +846,29 @@ public class GDBProcesses_7_0 extends AbstractDsfService } public void isDebugNewProcessSupported(IDMContext dmc, DataRequestMonitor rm) { - rm.setData(false); - rm.done(); + rm.setData(doIsDebugNewProcessSupported()); + rm.done(); } + /** @since 4.0 */ + protected boolean doIsDebugNewProcessSupported() { + return false; + } + public void debugNewProcess(IDMContext dmc, String file, Map attributes, DataRequestMonitor rm) { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, - NOT_SUPPORTED, "Not supported", null)); //$NON-NLS-1$ - rm.done(); + if (!fInitialProcess) { + // If we have already dealt with the initial process, check if we are allowed to create another one + if (!doIsDebugNewProcessSupported()) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Cannot start a new process", null)); //$NON-NLS-1$ + rm.done(); + return; + } + } + + fInitialProcess = false; + ImmediateExecutor.getInstance().execute( + new DebugNewProcessSequence(getExecutor(), dmc, file, attributes, rm)); } public void getProcessesBeingDebugged(final IDMContext dmc, final DataRequestMonitor rm) { @@ -904,8 +983,7 @@ public class GDBProcesses_7_0 extends AbstractDsfService // list natively (as we do with gdb 6.8). If // we're debugging remotely, the user is out // of luck - IGDBBackend backend = getServicesTracker().getService(IGDBBackend.class); - if (backend.getSessionType() == SessionType.LOCAL) { + if (fBackend.getSessionType() == SessionType.LOCAL) { IProcessList list = null; try { list = CCorePlugin.getDefault().getProcessList(); @@ -1185,8 +1263,7 @@ public class GDBProcesses_7_0 extends AbstractDsfService // Looks like this gdb doesn't truly support // "-list-thread-groups --available". Get the // process list natively if we're debugging locally - IGDBBackend backend = getServicesTracker().getService(IGDBBackend.class); - if (backend.getSessionType() == SessionType.LOCAL) { + if (fBackend.getSessionType() == SessionType.LOCAL) { IProcessList list = null; try { list = CCorePlugin.getDefault().getProcessList(); diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_2.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_2.java index 2fa7b87c8ef..5341a670113 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_2.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_2.java @@ -10,12 +10,17 @@ *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; +import java.util.Map; + import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; import org.eclipse.cdt.dsf.mi.service.IMICommandControl; @@ -26,8 +31,11 @@ import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; import org.eclipse.cdt.dsf.mi.service.command.output.MIAddInferiorInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.ILaunch; /** * Adding support for multi-process with GDB 7.2 @@ -38,6 +46,14 @@ public class GDBProcesses_7_2 extends GDBProcesses_7_1 { private CommandFactory fCommandFactory; private IGDBControl fCommandControl; + private IGDBBackend fBackend; + + /** + * Keeps track if we are dealing with the very first + * process of GDB. In such a case, we should not create a new + * inferior, since GDB has already created one by default. + */ + private boolean fInitialProcess = true; public GDBProcesses_7_2(DsfSession session) { super(session); @@ -64,6 +80,8 @@ public class GDBProcesses_7_2 extends GDBProcesses_7_1 { private void doInitialize(RequestMonitor requestMonitor) { fCommandControl = getServicesTracker().getService(IGDBControl.class); fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory(); + fBackend = getServicesTracker().getService(IGDBBackend.class); + requestMonitor.done(); } @@ -72,47 +90,140 @@ public class GDBProcesses_7_2 extends GDBProcesses_7_1 { super.shutdown(requestMonitor); } + @Override + protected boolean doIsDebuggerAttachSupported() { + // Multi-process is not applicable to post-mortem sessions (core) + // or to non-attach remote sessions. + if (fBackend.getSessionType() == SessionType.CORE) { + return false; + } + + if (fBackend.getSessionType() == SessionType.REMOTE && !fBackend.getIsAttachSession()) { + return false; + } + + return true; + } + @Override - public void attachDebuggerToProcess(final IProcessDMContext procCtx, final DataRequestMonitor rm) { + public void attachDebuggerToProcess(final IProcessDMContext procCtx, final DataRequestMonitor dataRm) { if (procCtx instanceof IMIProcessDMContext) { if (!doIsDebuggerAttachSupported()) { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Attach not supported.", null)); //$NON-NLS-1$ - rm.done(); + dataRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Attach not supported.", null)); //$NON-NLS-1$ + dataRm.done(); return; } - ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(procCtx, ICommandControlDMContext.class); - fCommandControl.queueCommand( - fCommandFactory.createMIAddInferior(controlDmc), - new DataRequestMonitor(getExecutor(), rm) { - @Override - protected void handleSuccess() { - final String groupId = getData().getGroupId(); - if (groupId == null || groupId.trim().length() == 0) { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid gdb group id.", null)); //$NON-NLS-1$ - rm.done(); - return; - } - - final IMIContainerDMContext containerDmc = createContainerContext(procCtx, groupId); - fCommandControl.queueCommand( - fCommandFactory.createMITargetAttach(containerDmc, ((IMIProcessDMContext)procCtx).getProcId()), - new DataRequestMonitor(getExecutor(), rm) { - @Override - protected void handleSuccess() { - rm.setData(containerDmc); - - // Start tracking this process' breakpoints. - MIBreakpointsManager bpmService = getServicesTracker().getService(MIBreakpointsManager.class); - IBreakpointsTargetDMContext bpTargetDmc = DMContexts.getAncestorOfType(containerDmc, IBreakpointsTargetDMContext.class); - bpmService.startTrackingBreakpoints(bpTargetDmc, rm); - } - }); - } - }); + // Use a sequence for better control of each step + ImmediateExecutor.getInstance().execute(new Sequence(getExecutor(), dataRm) { + private IMIContainerDMContext fContainerDmc; + + private Step[] steps = new Step[] { + // If this is not the very first inferior, we first need create the new inferior + new Step() { + @Override + public void execute(final RequestMonitor rm) { + if (fInitialProcess) { + fInitialProcess = false; + fContainerDmc = createContainerContext(procCtx, "i1"); //$NON-NLS-1$ + rm.done(); + return; + } + + ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(procCtx, ICommandControlDMContext.class); + fCommandControl.queueCommand( + fCommandFactory.createMIAddInferior(controlDmc), + new DataRequestMonitor(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + final String groupId = getData().getGroupId(); + if (groupId == null || groupId.trim().length() == 0) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid gdb group id.", null)); //$NON-NLS-1$ + } else { + fContainerDmc = createContainerContext(procCtx, groupId); + } + rm.done(); + } + }); + } + }, + // For remote attach, we must set the binary first + // For a local attach, GDB can figure out the binary automatically, + // so we don't specify it. + new Step() { + @Override + public void execute(RequestMonitor rm) { + if (fBackend.getSessionType() == SessionType.REMOTE) { + final IPath execPath = fBackend.getProgramPath(); + if (execPath != null && !execPath.isEmpty()) { + fCommandControl.queueCommand( + fCommandFactory.createMIFileExecAndSymbols(fContainerDmc, execPath.toPortableString()), + new DataRequestMonitor(ImmediateExecutor.getInstance(), rm)); + return; + } + } + + rm.done(); + } + }, + // Now, actually do the attach + new Step() { + @Override + public void execute(RequestMonitor rm) { + fCommandControl.queueCommand( + fCommandFactory.createMITargetAttach(fContainerDmc, ((IMIProcessDMContext)procCtx).getProcId()), + new DataRequestMonitor(ImmediateExecutor.getInstance(), rm)); + } + }, + // Start tracking this process' breakpoints. + new Step() { + @Override + public void execute(RequestMonitor rm) { + MIBreakpointsManager bpmService = getServicesTracker().getService(MIBreakpointsManager.class); + IBreakpointsTargetDMContext bpTargetDmc = DMContexts.getAncestorOfType(fContainerDmc, IBreakpointsTargetDMContext.class); + bpmService.startTrackingBreakpoints(bpTargetDmc, rm); + } + }, + // Turn on reverse debugging if it was enabled as a launch option + new Step() { + @Override + public void execute(RequestMonitor rm) { + IReverseRunControl reverseService = getServicesTracker().getService(IReverseRunControl.class); + if (reverseService != null) { + ILaunch launch = (ILaunch)procCtx.getAdapter(ILaunch.class); + if (launch != null) { + try { + boolean reverseEnabled = + launch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REVERSE, + IGDBLaunchConfigurationConstants.DEBUGGER_REVERSE_DEFAULT); + if (reverseEnabled) { + reverseService.enableReverseMode(fCommandControl.getContext(), true, rm); + return; + } + } catch (CoreException e) { + // Ignore, just don't set reverse + } + } + } + rm.done(); + } + }, + // Store the fully formed container context so it can be returned to the caller. + new Step() { + @Override + public void execute(RequestMonitor rm) { + dataRm.setData(fContainerDmc); + + rm.done(); + } + }, + }; + + @Override public Step[] getSteps() { return steps; } + }); } else { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid process context.", null)); //$NON-NLS-1$ - rm.done(); + dataRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid process context.", null)); //$NON-NLS-1$ + dataRm.done(); } } @@ -156,5 +267,41 @@ public class GDBProcesses_7_2 extends GDBProcesses_7_1 { rm.done(); } } + + @Override + protected boolean doIsDebugNewProcessSupported() { + // Multi-process is not applicable to post-mortem sessions (core) + // or to non-attach remote sessions. + SessionType type = fBackend.getSessionType(); + + if (type == SessionType.CORE) { + return false; + } + + if (type == SessionType.REMOTE && !fBackend.getIsAttachSession()) { + return false; + } + + return true; + } + + @Override + public void debugNewProcess(IDMContext dmc, String file, + Map attributes, DataRequestMonitor rm) { + boolean isInitial = fInitialProcess; + if (fInitialProcess) { + fInitialProcess = false; + } else { + // If we are trying to create another process than the initial one, see if we are allowed + if (!doIsDebugNewProcessSupported()) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Not allowed to create a new process", null)); //$NON-NLS-1$ + rm.done(); + return; + } + } + + ImmediateExecutor.getInstance().execute( + new DebugNewProcessSequence_7_2(getExecutor(), isInitial, dmc, file, attributes, rm)); + } }