From e8f17beeb52654bb13f7d41aae255cedcac4a4bd Mon Sep 17 00:00:00 2001 From: Umair Sair Date: Tue, 11 Apr 2023 03:32:11 +0500 Subject: [PATCH] Bug 528145 - Breakpoints are not working with remote attach launch (#336) Looking at the logs, it seems that the regression is caused at 8bec791 where support for multi-process was added. We removed breakpoints tracking support from final launch sequence and moved it to debug new process and attach to process logic but none of these are run for remote attach launch, hence breakpoint tracking is not started for remote attach launch. To fix the problem, IGDBProcesses.attachDebuggerToProcess(..) is updated to handle remote attach launch as well instead of final launch sequence handling it. This commit is created after reverting 7bddb5f and 96839a0 which is the older fix done to fix this issue and the other commit was to fix the regression caused by the old fix. The problem with older fix was that for non-stop mode, attach to process was not working for remote launches when there is already a process being debugged. Note that to use this feature, gdbserver should be started with --multi option. * Revert "Bug 580259: Not all remote session have a connected process" This reverts commit 96839a029d7de3016bd63787a55b1095d7806d4b. * Revert "Bug 528145 - Attach debugger to a gdbserver remote session" This reverts commit 7bddb5f4cb24c7946d8dccd7c4d3c6b8be98b77a. --- .github/workflows/build-test.yml | 1 + NewAndNoteworthy/CDT-11.2.md | 4 + NewAndNoteworthy/CHANGELOG-API.md | 8 + .../META-INF/MANIFEST.MF | 2 +- .../gdb/launching/FinalLaunchSequence.java | 75 +++--- .../cdt/dsf/gdb/service/GDBProcesses_7_0.java | 74 +++++- .../cdt/dsf/gdb/service/GDBProcesses_7_2.java | 92 +------ .../tests/dsf/gdb/framework/BaseTestCase.java | 34 ++- .../cdt/tests/dsf/gdb/framework/SyncUtil.java | 53 ++++ .../tests/dsf/gdb/tests/ITestConstants.java | 5 + .../cdt/tests/dsf/gdb/tests/SuiteGdb.java | 4 +- .../tests/nonstop/MultiProcessRemoteTest.java | 243 ++++++++++++++++++ 12 files changed, 459 insertions(+), 136 deletions(-) create mode 100644 dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/nonstop/MultiProcessRemoteTest.java 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.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.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 @@ protected String[] getExecutionOrder(String group) { // // "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 void stepInitializeFinalLaunchSequence(RequestMonitor requestMonitor) { 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 @@ protected void handleError() { 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 void stepRemoteConnection(final RequestMonitor rm) { @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 @@ protected void handleCancel() { } /** - * 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 void stepAttachToProcess(final RequestMonitor requestMonitor) { 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 void stepAttachToProcess(final RequestMonitor requestMonitor) { * 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 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.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.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 void attachDebuggerToProcess(final IProcessDMContext procCtx, final Strin 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 void execute(RequestMonitor rm) { 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 void execute(RequestMonitor rm) { // 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 Step[] getSteps() { } } + /** + * @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.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.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 ContainerCreatedDMEvent(IContainerDMContext context) { 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 IMIContainerDMContext createContainerContextFromGroupId(ICommandControlDM @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 @@ protected boolean doIsDebuggerAttachSupported() { 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 void attachDebuggerToProcess(final IProcessDMContext procCtx, final Strin 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 @@ protected void handleCompleted() { 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 @@ protected boolean targetAttachRequiresTrailingNewline() { 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.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 synchronized MIStoppedEvent getInitialStoppedEvent() { */ 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 @@ protected void doLaunch() throws Exception { 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 void sessionStarted(DsfSession session) { // 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 void sessionStarted(DsfSession session) { 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 @@ private void launchGdbServer() throws Exception { 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 @@ protected void handleCompleted() { 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 static IMIExecutionDMContext[] getExecutionContexts() 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.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 @@ 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)); + } +}