diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index e7d82a00a2f..7ee31bd55f6 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -37,6 +37,7 @@ jobs: run: | export DISPLAY=:99 sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & + echo 0| sudo tee /proc/sys/kernel/yama/ptrace_scope mvn \ clean verify -B -V \ -Dmaven.test.failure.ignore=true \ diff --git a/NewAndNoteworthy/CDT-11.2.md b/NewAndNoteworthy/CDT-11.2.md index 77f9b1003e0..f81b3c1bc92 100644 --- a/NewAndNoteworthy/CDT-11.2.md +++ b/NewAndNoteworthy/CDT-11.2.md @@ -12,6 +12,10 @@ This is the New & Noteworthy page for CDT 11.2 which is part of Eclipse 2023-06 Please see [CHANGELOG-API](CHANGELOG-API.md) for details on the breaking API changes in this release as well as future planned API changes. +## `FinalLaunchSequence.stepRemoteConnection()` and `FinalLaunchSequence.stepAttachRemoteToDebugger()` are deprecated + +The remote connection for attach launch will be moved in the implementation of `IGDBProcesses.attachDebuggerToProcess()` + # Noteworthy Issues and Pull Requests See [Noteworthy issues and PRs](https://github.com/eclipse-cdt/cdt/issues?q=is%3Aclosed+label%3Anoteworthy+milestone%3A11.2.0) for this release in the issue/PR tracker. diff --git a/NewAndNoteworthy/CHANGELOG-API.md b/NewAndNoteworthy/CHANGELOG-API.md index c2dd5379fb9..959172965d7 100644 --- a/NewAndNoteworthy/CHANGELOG-API.md +++ b/NewAndNoteworthy/CHANGELOG-API.md @@ -596,3 +596,11 @@ The class BuiltinDetctionArgsGeneric will be removed. Use the correctly spelled BuiltinDetectionArgsGeneric instead. - org.eclipse.cdt.jsoncdb.core.participant.Arglets.BuiltinDetctionArgsGeneric + +## API Removals after June 2025 + +### FinalLaunchSequence.stepRemoteConnection() and FinalLaunchSequence.stepAttachRemoteToDebugger() will be removed + +These APIs will be removed and remote connection for attach launch will be moved in the implementation of `IGDBProcesses.attachDebuggerToProcess()`. + +See https://github.com/eclipse-cdt/cdt/pull/336 diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF index b7d3ab28485..f983704ab53 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.cdt.dsf.gdb;singleton:=true -Bundle-Version: 7.0.0.qualifier +Bundle-Version: 7.1.0.qualifier Bundle-Activator: org.eclipse.cdt.dsf.gdb.internal.GdbPlugin Bundle-Localization: plugin Require-Bundle: org.eclipse.core.runtime, 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 cf07edf79bd..4d034d09904 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 @@ -19,7 +19,6 @@ * Anton Gorenkov - A preference to use RTTI for variable types determination (Bug 377536) * Xavier Raynaud (Kalray) - Avoid duplicating fields in sub-classes (add protected accessors) * Marc Khouzam (Ericsson) - Output the version of GDB at startup (Bug 455408) - * Jonathan Tousignant (NordiaSoft) - Remote session breakpoint (Bug 528145) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.launching; @@ -39,7 +38,6 @@ import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitorWithProgress; import org.eclipse.cdt.dsf.datamodel.DataModelInitializedEvent; import org.eclipse.cdt.dsf.datamodel.IDMContext; -import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; @@ -47,11 +45,11 @@ 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.IGDBSourceLookup; 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.IMIProcesses; 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.MIGDBVersionInfo; @@ -77,7 +75,7 @@ public class FinalLaunchSequence extends ReflectionSequence { private IGDBControl fCommandControl; private IGDBBackend fGDBBackend; - private IMIProcesses fProcService; + private IGDBProcesses fProcService; private CommandFactory fCommandFactory; private DsfServicesTracker fTracker; @@ -136,13 +134,13 @@ public class FinalLaunchSequence extends ReflectionSequence { // // "stepSetSourceLookupPath", //$NON-NLS-1$ - // For remote-attach launch only + // For remote-attach launch only (deprecated, see javadocs) "stepRemoteConnection", //$NON-NLS-1$ // For all launches except attach ones "stepNewProcess", //$NON-NLS-1$ - // For local attach launch only + // For all attach launch only "stepAttachToProcess", //$NON-NLS-1$ - // For remote attach launch only + // For remote attach launch only (deprecated, see javadocs) "stepAttachRemoteToDebugger", //$NON-NLS-1$ // Global "stepDataModelInitializationComplete", //$NON-NLS-1$ @@ -178,7 +176,7 @@ public class FinalLaunchSequence extends ReflectionSequence { fCommandFactory = fCommandControl.getCommandFactory(); - fProcService = fTracker.getService(IMIProcesses.class); + fProcService = fTracker.getService(IGDBProcesses.class); if (fProcService == null) { requestMonitor.setStatus( new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain process service", null)); //$NON-NLS-1$ @@ -552,13 +550,17 @@ public class FinalLaunchSequence extends ReflectionSequence { rm.done(); } + @Deprecated(forRemoval = true) private static final String INVALID = "invalid"; //$NON-NLS-1$ /** * If we are dealing with a remote-attach debugging session, connect to the target. * @since 4.0 + * + * When removing, revive/uncomment code in implementations of IGDBProcesses.attachDebuggerToProcess() */ @Execute + @Deprecated(forRemoval = true) public void stepRemoteConnection(final RequestMonitor rm) { if (fGDBBackend.getSessionType() == SessionType.REMOTE && fGDBBackend.getIsAttachSession()) { boolean isTcpConnection = CDebugUtils.getAttribute(fAttributes, @@ -593,19 +595,9 @@ public class FinalLaunchSequence extends ReflectionSequence { @Execute public void stepNewProcess(final RequestMonitor rm) { if (!fGDBBackend.getIsAttachSession()) { - 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.toString(); - } - // 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, + fProcService.debugNewProcess(fCommandControl.getContext(), getBinary(), fAttributes, new DataRequestMonitor(getExecutor(), rm) { @Override protected void handleCancel() { @@ -623,14 +615,28 @@ public class FinalLaunchSequence extends ReflectionSequence { } /** - * 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 7.1 + */ + protected String getBinary() { + 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.toString(); + } + return binary; + } + + /** + * If we are dealing with an attach session, perform the attach. * @since 4.0 */ @Execute public void stepAttachToProcess(final RequestMonitor requestMonitor) { - if (fGDBBackend.getIsAttachSession() && fGDBBackend.getSessionType() != SessionType.REMOTE) { + if (fGDBBackend.getIsAttachSession()) { // Is the process id already stored in the launch? int pid = CDebugUtils.getAttribute(fAttributes, ICDTLaunchConfigurationConstants.ATTR_ATTACH_PROCESS_ID, -1); @@ -639,6 +645,10 @@ public class FinalLaunchSequence extends ReflectionSequence { fProcService.attachDebuggerToProcess( fProcService.createProcessContext(fCommandControl.getContext(), Integer.toString(pid)), new DataRequestMonitor(getExecutor(), requestMonitor)); + } else if (fGDBBackend.getSessionType() == SessionType.REMOTE) { + // Inline following and remove requestMonitor.done() once FinalLaunchSequence.stepAttachRemoteToDebugger() is removed + // stepAttachRemoteToDebugger(requestMonitor); + requestMonitor.done(); } else { IConnectHandler connectCommand = (IConnectHandler) fSession.getModelAdapter(IConnectHandler.class); if (connectCommand instanceof IConnect) { @@ -656,22 +666,19 @@ public class FinalLaunchSequence extends ReflectionSequence { * If we are dealing with an remote attach session, perform the attach to debugger. * Bug 528145 * @since 6.6 + * + * When removing, revive/uncomment code in implementations in FinalLaunchSequence.stepAttachToProcess(RequestMonitor) */ + @Deprecated(forRemoval = true) @Execute public void stepAttachRemoteToDebugger(final RequestMonitor requestMonitor) { if (fGDBBackend.getIsAttachSession() && fGDBBackend.getSessionType() == SessionType.REMOTE) { - DataRequestMonitor rm = new DataRequestMonitor<>(getExecutor(), null); - fProcService.canDetachDebuggerFromProcess(null, rm); - - if (rm.getData()) { - IProcessDMContext processContext = fProcService.createProcessContext(fCommandControl.getContext(), - MIProcesses.UNKNOWN_PROCESS_ID); - fProcService.attachDebuggerToProcess(processContext, - new DataRequestMonitor(getExecutor(), requestMonitor)); - return; - } + fProcService.attachDebuggerToProcess( + fProcService.createProcessContext(fCommandControl.getContext(), MIProcesses.UNKNOWN_PROCESS_ID), + getBinary(), new DataRequestMonitor(getExecutor(), requestMonitor)); + } else { + requestMonitor.done(); } - requestMonitor.done(); } /** 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 8bca1e10eef..1e96c870efe 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 @@ -39,6 +39,7 @@ import java.util.concurrent.TimeUnit; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.IProcessInfo; import org.eclipse.cdt.core.IProcessList; +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.DsfRunnable; @@ -75,6 +76,7 @@ import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants; import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.launching.GDBRemoteTCPLaunchTargetProvider; import org.eclipse.cdt.dsf.gdb.launching.InferiorRuntimeProcess; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; import org.eclipse.cdt.dsf.mi.service.IMICommandControl; @@ -113,6 +115,8 @@ import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.model.IProcess; +import org.eclipse.launchbar.core.target.ILaunchTarget; +import org.eclipse.launchbar.core.target.launch.ITargetedLaunch; import org.osgi.framework.BundleContext; /** @@ -126,6 +130,7 @@ public class GDBProcesses_7_0 extends AbstractDsfService implements IGDBProcesse * Each one is shown in the debug view. */ private final static int MAX_NUMBER_EXITED_PROCESS = 5; + private final static String INVALID = "invalid"; //$NON-NLS-1$ // Below is the context hierarchy that is implemented between the // MIProcesses service and the MIRunControl service for the MI @@ -1216,13 +1221,6 @@ public class GDBProcesses_7_0 extends AbstractDsfService implements IGDBProcesse new Step() { @Override public void execute(RequestMonitor rm) { - - if (isInitialProcess()) { - // To be proper, set the initialProcess variable to false - // it may be necessary for a class that extends this class - setIsInitialProcess(false); - } - // There is no groupId until we attach, so we can use the default groupId fContainerDmc = createContainerContext(procCtx, MIProcesses.UNIQUE_GROUP_ID); @@ -1240,6 +1238,12 @@ public class GDBProcesses_7_0 extends AbstractDsfService implements IGDBProcesse new Step() { @Override public void execute(RequestMonitor rm) { + if (fBackend.getSessionType() == SessionType.REMOTE && isInitialProcess()) { + // Uncomment following and remove rm.done() once FinalLaunchSequence.stepRemoteConnection() is removed + // connectToTarget(procCtx, rm); + rm.done(); + return; + } // For non-stop mode, we do a non-interrupting attach // Bug 333284 boolean shouldInterrupt = true; @@ -1262,6 +1266,7 @@ public class GDBProcesses_7_0 extends AbstractDsfService implements IGDBProcesse // Store the fully formed container context so it can be returned to the caller. dataRm.setData(fContainerDmc); + setIsInitialProcess(false); // Initialize memory data for this process. IGDBMemory memory = getServicesTracker().getService(IGDBMemory.class); @@ -1302,6 +1307,61 @@ public class GDBProcesses_7_0 extends AbstractDsfService implements IGDBProcesse } } + /** + * @since 7.1 + */ + protected void connectToTarget(IProcessDMContext procCtx, RequestMonitor rm) { + ILaunch launch = procCtx.getAdapter(ILaunch.class); + assert launch != null; + if (launch != null) { + Map attributes = new HashMap<>(); + try { + attributes.putAll(launch.getLaunchConfiguration().getAttributes()); + } catch (CoreException e) { + rm.done(e.getStatus()); + return; + } + + if (launch instanceof ITargetedLaunch) { + ILaunchTarget target = ((ITargetedLaunch) launch).getLaunchTarget(); + if (target != null) { + attributes.putAll(target.getAttributes()); + String tcp = target.getAttribute(IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, ""); //$NON-NLS-1$ + if (!tcp.isEmpty()) { + attributes.put(IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, Boolean.parseBoolean(tcp)); + } else { + attributes.put(IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, + target.getTypeId().equals(GDBRemoteTCPLaunchTargetProvider.TYPE_ID)); + } + } + } + + boolean isTcpConnection = CDebugUtils.getAttribute(attributes, + IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, false); + + if (isTcpConnection) { + String remoteTcpHost = CDebugUtils.getAttribute(attributes, IGDBLaunchConfigurationConstants.ATTR_HOST, + INVALID); + String remoteTcpPort = CDebugUtils.getAttribute(attributes, IGDBLaunchConfigurationConstants.ATTR_PORT, + INVALID); + + fCommandControl.queueCommand(fCommandFactory.createMITargetSelect(fCommandControl.getContext(), + remoteTcpHost, remoteTcpPort, true), new ImmediateDataRequestMonitor(rm)); + } else { + String serialDevice = CDebugUtils.getAttribute(attributes, IGDBLaunchConfigurationConstants.ATTR_DEV, + INVALID); + + fCommandControl.queueCommand( + fCommandFactory.createMITargetSelect(fCommandControl.getContext(), serialDevice, true), + new ImmediateDataRequestMonitor(rm)); + } + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Cannot reconnect to target.", //$NON-NLS-1$ + null)); + rm.done(); + } + } + /** @since 5.0 */ protected void doReverseDebugStep(final IProcessDMContext procCtx, RequestMonitor rm) { // Turn on reverse debugging if it was enabled as a launch option 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 136116a6ed5..59d37ec9419 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 @@ -13,20 +13,17 @@ * Marc Khouzam (Ericsson) - Workaround for Bug 352998 * Marc Khouzam (Ericsson) - Update breakpoint handling for GDB >= 7.4 (Bug 389945) * Alvaro Sanchez-Leon (Ericsson) - Breakpoint Enable does not work after restarting the application (Bug 456959) - * Jonathan Tousignant (NordiaSoft) - Remote session breakpoint (Bug 528145) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; import java.text.MessageFormat; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.Set; -import org.eclipse.cdt.debug.core.CDebugUtils; import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DsfExecutor; @@ -48,9 +45,7 @@ import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; import org.eclipse.cdt.dsf.debug.service.command.ICommand; 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.launching.GDBRemoteTCPLaunchTargetProvider; import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceRecordSelectedChangedDMEvent; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; import org.eclipse.cdt.dsf.mi.service.IMICommandControl; @@ -67,12 +62,8 @@ 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.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfSession; -import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; -import org.eclipse.debug.core.ILaunch; -import org.eclipse.launchbar.core.target.ILaunchTarget; -import org.eclipse.launchbar.core.target.launch.ITargetedLaunch; /** * Adding support for multi-process with GDB 7.2 @@ -176,8 +167,6 @@ public class GDBProcesses_7_2 extends GDBProcesses_7_1 implements IMultiTerminat private IGDBControl fCommandControl; private IGDBBackend fBackend; - private final static String INVALID = "invalid"; //$NON-NLS-1$ - /** * Keep track if we need to reconnect to the target * due to a workaround because of a GDB 7.2 bug. @@ -266,15 +255,13 @@ public class GDBProcesses_7_2 extends GDBProcesses_7_1 implements IMultiTerminat @Override protected boolean doIsDebuggerAttachSupported() { - SessionType sessionType = fBackend.getSessionType(); - // Multi-process is not applicable to post-mortem sessions (core) // or to non-attach remote sessions. - if (sessionType == SessionType.CORE) { + if (fBackend.getSessionType() == SessionType.CORE) { return false; } - if (sessionType == SessionType.REMOTE && !fBackend.getIsAttachSession()) { + if (fBackend.getSessionType() == SessionType.REMOTE && !fBackend.getIsAttachSession()) { return false; } @@ -282,16 +269,9 @@ public class GDBProcesses_7_2 extends GDBProcesses_7_1 implements IMultiTerminat IMIRunControl runControl = getServicesTracker().getService(IMIRunControl.class); if (runControl != null && runControl.getRunMode() == MIRunMode.ALL_STOP) { // Only one process is allowed in all-stop (for now) + return getNumConnected() == 0; // NOTE: when we support multi-process in all-stop mode, // we will need to interrupt the target to when doing the attach. - int numConnected = getNumConnected(); - - if (numConnected == 1 && sessionType == SessionType.REMOTE) { - // Bug 528145: Special case for remote sessions with an existing connection. - return true; - } - - return numConnected == 0; } return true; @@ -325,10 +305,8 @@ public class GDBProcesses_7_2 extends GDBProcesses_7_1 implements IMultiTerminat new Step() { @Override public void execute(final RequestMonitor rm) { - // The remote session is already connected to the process - // Bug 528145 - if (fBackend.getSessionType() == SessionType.REMOTE - && doCanDetachDebuggerFromProcess()) { + if (procCtx instanceof IMIProcessDMContext ctx + && MIProcesses.UNKNOWN_PROCESS_ID.equals(ctx.getProcId())) { rm.done(); return; } @@ -469,14 +447,12 @@ public class GDBProcesses_7_2 extends GDBProcesses_7_1 implements IMultiTerminat new Step() { @Override public void execute(RequestMonitor rm) { - // This call end the current attach to the gdbserver in remote session - // Bug 528145 - if (fBackend.getSessionType() == SessionType.REMOTE - && doCanDetachDebuggerFromProcess()) { + if (fBackend.getSessionType() == SessionType.REMOTE && isInitialProcess()) { + // Uncomment following and remove rm.done() once FinalLaunchSequence.stepRemoteConnection() is removed + // connectToTarget(procCtx, rm); rm.done(); return; } - // For non-stop mode, we do a non-interrupting attach // Bug 333284 boolean shouldInterrupt = true; @@ -560,58 +536,6 @@ public class GDBProcesses_7_2 extends GDBProcesses_7_1 implements IMultiTerminat return false; } - private void connectToTarget(IProcessDMContext procCtx, RequestMonitor rm) { - ILaunch launch = procCtx.getAdapter(ILaunch.class); - assert launch != null; - if (launch != null) { - Map attributes = new HashMap<>(); - try { - attributes.putAll(launch.getLaunchConfiguration().getAttributes()); - } catch (CoreException e) { - rm.done(e.getStatus()); - return; - } - - if (launch instanceof ITargetedLaunch) { - ILaunchTarget target = ((ITargetedLaunch) launch).getLaunchTarget(); - if (target != null) { - attributes.putAll(target.getAttributes()); - String tcp = target.getAttribute(IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, ""); //$NON-NLS-1$ - if (!tcp.isEmpty()) { - attributes.put(IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, Boolean.parseBoolean(tcp)); - } else { - attributes.put(IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, - target.getTypeId().equals(GDBRemoteTCPLaunchTargetProvider.TYPE_ID)); - } - } - } - - boolean isTcpConnection = CDebugUtils.getAttribute(attributes, - IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, false); - - if (isTcpConnection) { - String remoteTcpHost = CDebugUtils.getAttribute(attributes, IGDBLaunchConfigurationConstants.ATTR_HOST, - INVALID); - String remoteTcpPort = CDebugUtils.getAttribute(attributes, IGDBLaunchConfigurationConstants.ATTR_PORT, - INVALID); - - fCommandControl.queueCommand(fCommandFactory.createMITargetSelect(fCommandControl.getContext(), - remoteTcpHost, remoteTcpPort, true), new ImmediateDataRequestMonitor(rm)); - } else { - String serialDevice = CDebugUtils.getAttribute(attributes, IGDBLaunchConfigurationConstants.ATTR_DEV, - INVALID); - - fCommandControl.queueCommand( - fCommandFactory.createMITargetSelect(fCommandControl.getContext(), serialDevice, true), - new ImmediateDataRequestMonitor(rm)); - } - } else { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Cannot reconnect to target.", //$NON-NLS-1$ - null)); - rm.done(); - } - } - @Override public void detachDebuggerFromProcess(IDMContext dmc, final RequestMonitor rm) { diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/BaseTestCase.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/BaseTestCase.java index 3a951facb64..c7b0f1b403d 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/BaseTestCase.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/BaseTestCase.java @@ -24,9 +24,11 @@ import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; @@ -192,7 +194,9 @@ public class BaseTestCase { */ public boolean isRemoteSession() { return launchAttributes.get(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE) - .equals(IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE); + .equals(IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE) + || launchAttributes.get(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE) + .equals(IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE_ATTACH); } /** @@ -553,9 +557,6 @@ public class BaseTestCase { protected GdbLaunch doLaunchInner() throws Exception { assertNotNull("The launch configuration has not been created. Call doLaunch first.", fLaunchConfiguration); - boolean postMortemLaunch = launchAttributes.get(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE) - .equals(ICDTLaunchConfigurationConstants.DEBUGGER_MODE_CORE); - SessionEventListener sessionEventListener = new SessionEventListener(fLaunchConfiguration); SessionStartedListener sessionStartedListener = new SessionStartedListener() { @Override @@ -586,12 +587,10 @@ public class BaseTestCase { // proceeding. All tests assume that stable initial state. Two // seconds is plenty; we typically get to that state in a few // hundred milliseconds with the tiny test programs we use. - if (!postMortemLaunch) { + if (isTargetExpectedToStopAfterLaunch()) { sessionEventListener.waitUntilTargetSuspended(); - } - // This should be a given if the above check passes - if (!postMortemLaunch) { + // This should be a given if the above check passes synchronized (this) { MIStoppedEvent initialStoppedEvent = sessionEventListener.getInitialStoppedEvent(); Assert.assertNotNull(initialStoppedEvent); @@ -617,6 +616,11 @@ public class BaseTestCase { return launch; } + protected boolean isTargetExpectedToStopAfterLaunch() { + return !launchAttributes.get(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE) + .equals(ICDTLaunchConfigurationConstants.DEBUGGER_MODE_CORE); + } + /** * Assert that the launch terminates. Callers should have already * terminated the launch in some way. @@ -680,7 +684,19 @@ public class BaseTestCase { String server = (String) launchAttributes.get(ATTR_DEBUG_SERVER_NAME); String port = (String) launchAttributes.get(IGDBLaunchConfigurationConstants.ATTR_PORT); String program = (String) launchAttributes.get(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME); - String[] commandLine = { server, ":" + port, program }; + boolean multi = Boolean.valueOf((String) launchAttributes.get(ITestConstants.LAUNCH_GDB_SERVER_MULTI)); + boolean noprogram = Boolean + .valueOf((String) launchAttributes.get(ITestConstants.LAUNCH_GDB_SERVER_WITHOUT_PROGRAM)); + List commandArgs = new ArrayList<>(); + commandArgs.add(server); + if (multi) { + commandArgs.add("--multi"); + } + commandArgs.add(":" + port); + if (!noprogram) { + commandArgs.add(program); + } + String[] commandLine = commandArgs.toArray(new String[0]); try { if (GdbDebugOptions.DEBUG) GdbDebugOptions diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/SyncUtil.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/SyncUtil.java index d155ab8f6e2..51dfc098bb0 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/SyncUtil.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/SyncUtil.java @@ -1111,6 +1111,43 @@ public class SyncUtil { return query.get(TestsPlugin.massageTimeout(2000), TimeUnit.MILLISECONDS); } + /** + * Utility method to return all the container DM contexts. + * + *

+ * This must NOT be called from the DSF executor. + * + * @return the process context + * @throws InterruptedException + * @throws TimeoutException + * @throws ExecutionException + */ + @ThreadSafeAndProhibitedFromDsfExecutor("fSession.getExecutor()") + public static IContainerDMContext[] getAllContainerContexts() + throws InterruptedException, ExecutionException, TimeoutException { + assert !fProcessesService.getExecutor().isInExecutorThread(); + + Query query = new Query<>() { + @Override + protected void execute(final DataRequestMonitor rm) { + fProcessesService.getProcessesBeingDebugged(fGdbControl.getContext(), + new ImmediateDataRequestMonitor() { + @Override + protected void handleCompleted() { + if (isSuccess()) { + rm.done((IContainerDMContext[]) getData()); + } else { + rm.done(getStatus()); + } + } + }); + } + }; + + fGdbControl.getExecutor().execute(query); + return query.get(TestsPlugin.massageTimeout(2000), TimeUnit.MILLISECONDS); + } + /** * Utility method to return all threads * @@ -1126,6 +1163,22 @@ public class SyncUtil { final IContainerDMContext containerDmc = SyncUtil.getContainerContext(); + return getExecutionContexts(containerDmc); + } + + /** + * Utility method to return all threads for given containerDmc + * @param containerDmc + * @return + * @throws InterruptedException + * @throws ExecutionException + * @throws TimeoutException + */ + @ThreadSafeAndProhibitedFromDsfExecutor("fSession.getExecutor()") + public static IMIExecutionDMContext[] getExecutionContexts(IContainerDMContext containerDmc) + throws InterruptedException, ExecutionException, TimeoutException { + assert !fProcessesService.getExecutor().isInExecutorThread(); + Query query = new Query<>() { @Override protected void execute(final DataRequestMonitor rm) { diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/ITestConstants.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/ITestConstants.java index 91713d1b44f..29679527261 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/ITestConstants.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/ITestConstants.java @@ -64,4 +64,9 @@ public class ITestConstants { // Attribute that allows a test to request not to start gdbserver even if the session is a remote one public static final String LAUNCH_GDB_SERVER = TestsPlugin.PLUGIN_ID + ".launchGdbServer"; + // Attribute that tells whether gdbserver should be launched without program + public static final String LAUNCH_GDB_SERVER_WITHOUT_PROGRAM = TestsPlugin.PLUGIN_ID + + ".launchGdbServerWithoutProgram"; + // Attribute that tells whether gdbserver should be launched with --multi argument + public static final String LAUNCH_GDB_SERVER_MULTI = TestsPlugin.PLUGIN_ID + ".launchGdbServerMulti"; } diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/SuiteGdb.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/SuiteGdb.java index c03ecd44b7b..a2b25d923ef 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/SuiteGdb.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/SuiteGdb.java @@ -17,6 +17,7 @@ import org.eclipse.cdt.tests.dsf.gdb.framework.BaseParametrizedTestCase; import org.eclipse.cdt.tests.dsf.gdb.tests.nonstop.GDBMultiNonStopRunControlTest; import org.eclipse.cdt.tests.dsf.gdb.tests.nonstop.MIExpressionsNonStopTest; import org.eclipse.cdt.tests.dsf.gdb.tests.nonstop.MIRunControlNonStopTargetAvailableTest; +import org.eclipse.cdt.tests.dsf.gdb.tests.nonstop.MultiProcessRemoteTest; import org.eclipse.cdt.tests.dsf.gdb.tests.nonstop.OperationsWhileTargetIsRunningNonStopTest; import org.eclipse.cdt.tests.dsf.gdb.tests.nonstop.StepIntoSelectionNonStopTest; import org.eclipse.cdt.tests.dsf.gdb.tests.nonstop.ThreadStackFrameSyncTest; @@ -46,7 +47,8 @@ import org.junit.runners.Suite; OperationsWhileTargetIsRunningNonStopTest.class, StepIntoSelectionNonStopTest.class, GDBRemoteTracepointsTest.class, TraceFileTest.class, GDBConsoleSynchronizingTest.class, MIMemoryTest.class, MIDisassemblyTest.class, GDBProcessesTest.class, PostMortemCoreTest.class, CommandTimeoutTest.class, - ThreadStackFrameSyncTest.class, CommandLineArgsTest.class, MIAsyncErrorProcessorTests.class + ThreadStackFrameSyncTest.class, CommandLineArgsTest.class, MIAsyncErrorProcessorTests.class, + MultiProcessRemoteTest.class /* Add your test class here */ }) public class SuiteGdb { diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/nonstop/MultiProcessRemoteTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/nonstop/MultiProcessRemoteTest.java new file mode 100644 index 00000000000..4660e3cdeae --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/nonstop/MultiProcessRemoteTest.java @@ -0,0 +1,243 @@ +package org.eclipse.cdt.tests.dsf.gdb.tests.nonstop; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.service.IGDBBackend; +import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses; +import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.gdb.framework.BaseParametrizedTestCase; +import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil; +import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil.DefaultTimeouts; +import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil.DefaultTimeouts.ETimeout; +import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; +import org.eclipse.cdt.tests.dsf.gdb.tests.ITestConstants; +import org.eclipse.cdt.utils.spawner.ProcessFactory; +import org.eclipse.core.runtime.IPath; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Test; + +public class MultiProcessRemoteTest extends BaseParametrizedTestCase { + private static final String EXEC_NAME = "MultiThread.exe"; + + private DsfServicesTracker fServicesTracker; + + private IGDBProcesses fGdbProcesses; + private ICommandControlService fCommandControl; + private IGDBBackend fGDBBackend; + + private List appProcesses = new ArrayList<>(); + + @BeforeClass + public static void beforeClass() { + Assume.assumeTrue(supportsNonStop()); + } + + @Override + public void doBeforeTest() throws Exception { + assumeRemoteSession(); + assumeGdbVersionAtLeast(ITestConstants.SUFFIX_GDB_7_0); + super.doBeforeTest(); + + final DsfSession session = getGDBLaunch().getSession(); + + Runnable runnable = () -> { + fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), session.getId()); + fGdbProcesses = fServicesTracker.getService(IGDBProcesses.class); + fCommandControl = fServicesTracker.getService(ICommandControlService.class); + fGDBBackend = fServicesTracker.getService(IGDBBackend.class); + }; + session.getExecutor().submit(runnable).get(); + } + + @Override + protected void setLaunchAttributes() { + super.setLaunchAttributes(); + + setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, true); + setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, getBinary()); + setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, false); + setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, + IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE_ATTACH); + setLaunchAttribute(ITestConstants.LAUNCH_GDB_SERVER_MULTI, Boolean.TRUE.toString()); + setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REMOTE_BINARY, + getLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME)); + if (testName.getMethodName().contains("_noProgram")) { + setLaunchAttribute(ITestConstants.LAUNCH_GDB_SERVER_WITHOUT_PROGRAM, Boolean.TRUE.toString()); + } + } + + @Override + public void doAfterTest() throws Exception { + if (fServicesTracker != null) + fServicesTracker.dispose(); + super.doAfterTest(); + for (Process p : appProcesses) { + if (p.isAlive()) + p.destroyForcibly(); + } + appProcesses.clear(); + } + + @Override + protected boolean isTargetExpectedToStopAfterLaunch() { + // For some tests, we'll get stop event but not for others. Checking stop event + // at start of launch is not required for this test class + return false; + } + + @Test + public void launchGdbServerWithProgramAndStartMulipleNewExecutables() throws Throwable { + assertEquals("Wrong number of containers at the start of launch", SyncUtil.getAllContainerContexts().length, + getInitialProcessCount()); + + final IPath execPath = fGDBBackend.getProgramPath(); + Map attributes = getLaunchConfiguration().getAttributes(); + + IMIExecutionDMContext[] ctx = debugNewProcess(fGDBBackend.getProgramPath(), attributes); + assertTrue("Process-1 is not started properly as no threads found for it", ctx != null && ctx.length > 0); + + ctx = debugNewProcess(execPath, attributes); + assertTrue("Process-2 is not started properly as no threads found for it", ctx != null && ctx.length > 0); + + ctx = debugNewProcess(execPath, attributes); + assertTrue("Process-3 is not started properly as no threads found for it", ctx != null && ctx.length > 0); + } + + @Test + public void launchGdbServerWithoutProgramAndStartMulipleNewExecutables_noProgram() throws Throwable { + launchGdbServerWithProgramAndStartMulipleNewExecutables(); + } + + @Test + public void launchGdbServerWithProgramAndConnectMultipleExecutables() throws Throwable { + assertEquals("Wrong number of containers at the start of launch", SyncUtil.getAllContainerContexts().length, + getInitialProcessCount()); + + Process process = launchApplication(); + IMIExecutionDMContext[] ctx = attachToProcess(Long.toString(process.pid())); + assertTrue("Process-1 is not attached properly as no threads found for it", ctx != null && ctx.length > 0); + + process = launchApplication(); + ctx = attachToProcess(Long.toString(process.pid())); + assertTrue("Process-2 is not attached properly as no threads found for it", ctx != null && ctx.length > 0); + + process = launchApplication(); + ctx = attachToProcess(Long.toString(process.pid())); + assertTrue("Process-3 is not attached properly as no threads found for it", ctx != null && ctx.length > 0); + } + + @Test + public void launchGdbServerWithoutProgramAndConnectMultipleExecutables_noProgram() throws Throwable { + launchGdbServerWithProgramAndConnectMultipleExecutables(); + } + + @Test + public void launchGdbServerWithProgram_DebugNewExecutable_And_ConnectExecutable() throws Throwable { + assertEquals("Wrong number of containers at the start of launch", SyncUtil.getAllContainerContexts().length, + getInitialProcessCount()); + + final IPath execPath = fGDBBackend.getProgramPath(); + Map attributes = getLaunchConfiguration().getAttributes(); + + IMIExecutionDMContext[] ctx = debugNewProcess(execPath, attributes); + assertTrue("Process-1 is not started properly as no threads found for it", ctx != null && ctx.length > 0); + + Process process = launchApplication(); + ctx = attachToProcess(Long.toString(process.pid())); + assertTrue("Process-2 is not attached properly as no threads found for it", ctx != null && ctx.length > 0); + } + + @Test + public void launchGdbServerWithoutProgram_DebugNewExecutable_And_ConnectExecutable_noProgram() throws Throwable { + launchGdbServerWithProgram_DebugNewExecutable_And_ConnectExecutable(); + } + + @Test + public void launchGdbServerWithProgram_ConnectExecutable_And_DebugNewExecutable() throws Throwable { + assertEquals("Wrong number of containers at the start of launch", SyncUtil.getAllContainerContexts().length, + getInitialProcessCount()); + + Process process = launchApplication(); + IMIExecutionDMContext[] ctx = attachToProcess(Long.toString(process.pid())); + assertTrue("Process-1 is not attached properly as no threads found for it", ctx != null && ctx.length > 0); + + final IPath execPath = fGDBBackend.getProgramPath(); + Map attributes = getLaunchConfiguration().getAttributes(); + + ctx = debugNewProcess(execPath, attributes); + assertTrue("Process-2 is not started properly as no threads found for it", ctx != null && ctx.length > 0); + } + + @Test + public void launchGdbServerWithoutProgram_ConnectExecutable_And_DebugNewExecutable_noProgram() throws Throwable { + launchGdbServerWithProgram_ConnectExecutable_And_DebugNewExecutable(); + } + + private int getInitialProcessCount() { + return Boolean.valueOf((String) getLaunchAttribute(ITestConstants.LAUNCH_GDB_SERVER_WITHOUT_PROGRAM)) ? 0 : 1; + } + + private Process launchApplication() throws IOException { + Process process = ProcessFactory.getFactory() + .exec(new String[] { Path.of(getBinary()).toAbsolutePath().toString() }); + if (process.isAlive()) { + appProcesses.add(process); + return process; + } + + throw new IOException("Unable to launch application"); + } + + private static String getBinary() { + return EXEC_PATH + EXEC_NAME; + } + + private IMIExecutionDMContext[] debugNewProcess(final IPath execPath, Map attributes) + throws InterruptedException, ExecutionException, TimeoutException { + Query query = new Query<>() { + @Override + protected void execute(DataRequestMonitor rm) { + fGdbProcesses.debugNewProcess(fCommandControl.getContext(), execPath.toOSString(), attributes, rm); + } + }; + fGDBBackend.getExecutor().execute(query); + IDMContext context = query.get(DefaultTimeouts.get(ETimeout.waitForStop), TimeUnit.MILLISECONDS); + return fGdbProcesses.getExecutionContexts(DMContexts.getAncestorOfType(context, IMIContainerDMContext.class)); + } + + private IMIExecutionDMContext[] attachToProcess(String pid) + throws InterruptedException, ExecutionException, TimeoutException { + Query query = new Query<>() { + @Override + protected void execute(DataRequestMonitor rm) { + IProcessDMContext procDmc = fGdbProcesses.createProcessContext(fCommandControl.getContext(), pid); + fGdbProcesses.attachDebuggerToProcess(procDmc, getBinary(), rm); + } + }; + fGDBBackend.getExecutor().execute(query); + IDMContext context = query.get(DefaultTimeouts.get(ETimeout.waitForStop), TimeUnit.MILLISECONDS); + return fGdbProcesses.getExecutionContexts(DMContexts.getAncestorOfType(context, IMIContainerDMContext.class)); + } +}