Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Khouzam2016-10-14 19:58:23 +0000
committerGerrit Code Review @ Eclipse.org2016-11-14 20:30:23 +0000
commit39c781f81a1687260024c03dc30b8c92e19c327d (patch)
treea5a9a73307f16e870e5d0330694af33baba969c6 /dsf-gdb/org.eclipse.cdt.dsf.gdb
parente8480ca0f8c72f1eb1a7d31f6c48789eef1bf9ba (diff)
downloadorg.eclipse.cdt-39c781f81a1687260024c03dc30b8c92e19c327d.tar.gz
org.eclipse.cdt-39c781f81a1687260024c03dc30b8c92e19c327d.tar.xz
org.eclipse.cdt-39c781f81a1687260024c03dc30b8c92e19c327d.zip
Bug 497166: Support the user using the 'run' command in the gdb console
This commit introduces a PersistentPTY. By using it, we now allow the user to restart the process from the GDB console (by pressing 'run'). In this case, the I/O will continue using the PersistentPTY. Previously, the PTY would have been closed, and GDB would fail to restart the process because it would fail to use the closed PTY. Change-Id: I395b402e297a2043af8fce33df163eddef9e6c7a
Diffstat (limited to 'dsf-gdb/org.eclipse.cdt.dsf.gdb')
-rw-r--r--dsf-gdb/org.eclipse.cdt.dsf.gdb/.settings/.api_filters8
-rw-r--r--dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_0.java187
-rw-r--r--dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_12.java110
-rw-r--r--dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_3.java14
-rw-r--r--dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java3
-rw-r--r--dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBProcesses.java19
-rw-r--r--dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/StartOrRestartProcessSequence_7_0.java106
-rw-r--r--dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/StartOrRestartProcessSequence_7_3.java4
-rw-r--r--dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/extensions/GDBProcesses_HEAD.java6
-rw-r--r--dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess.java36
-rw-r--r--dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess_7_3.java6
11 files changed, 385 insertions, 114 deletions
diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/.settings/.api_filters b/dsf-gdb/org.eclipse.cdt.dsf.gdb/.settings/.api_filters
index b8ded6d151f..0b6030e35e1 100644
--- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/.settings/.api_filters
+++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/.settings/.api_filters
@@ -20,6 +20,14 @@
</message_arguments>
</filter>
</resource>
+ <resource path="src/org/eclipse/cdt/dsf/gdb/service/IGDBProcesses.java" type="org.eclipse.cdt.dsf.gdb.service.IGDBProcesses">
+ <filter comment="CDT allows default methods to existing interfaces" id="404000815">
+ <message_arguments>
+ <message_argument value="org.eclipse.cdt.dsf.gdb.service.IGDBProcesses"/>
+ <message_argument value="addInferiorToLaunch(IRunControl.IContainerDMContext, String, PTY, RequestMonitor)"/>
+ </message_arguments>
+ </filter>
+ </resource>
<resource path="src/org/eclipse/cdt/dsf/gdb/service/command/IGDBControl.java" type="org.eclipse.cdt.dsf.gdb.service.command.IGDBControl">
<filter comment="CDT allows adding default methods in a minor release" id="404000815">
<message_arguments>
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 18db6225464..1434dc3f877 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
@@ -17,6 +17,8 @@
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.service;
+import java.io.IOException;
+import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -29,12 +31,14 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+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.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
@@ -62,6 +66,7 @@ import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent;
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.ICommandControlService.ICommandControlShutdownDMEvent;
import org.eclipse.cdt.dsf.debug.service.command.IEventListener;
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants;
@@ -79,6 +84,7 @@ import org.eclipse.cdt.dsf.mi.service.IMIRunControl.MIRunMode;
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;
import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadGroupCreatedEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadGroupExitedEvent;
import org.eclipse.cdt.dsf.mi.service.command.output.MIConst;
@@ -95,10 +101,13 @@ import org.eclipse.cdt.dsf.mi.service.command.output.MIValue;
import org.eclipse.cdt.dsf.service.AbstractDsfService;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.cdt.utils.pty.PTY;
+import org.eclipse.cdt.utils.pty.PersistentPTY;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IProcess;
import org.osgi.framework.BundleContext;
@@ -588,6 +597,15 @@ public class GDBProcesses_7_0 extends AbstractDsfService
// be running at the time. Bug 303503
private Map<String, String> fDebuggedProcessesAndNames = new HashMap<>();
+ /**
+ * A map that keeps track of the PTY associated with an inferior (groupId)
+ */
+ private Map<String, PTY> fGroupIdToPTYMap = new HashMap<>();
+ /**
+ * A list of groupIds that have exited.
+ */
+ private List<String> fExitedGroupId = new ArrayList<>();
+
/**
* Information about an exited process
* @since 4.7
@@ -665,14 +683,6 @@ public class GDBProcesses_7_0 extends AbstractDsfService
*/
private boolean fInitialProcess = true;
- /**
- * Keeps track of the fact that we are restarting a process or not.
- * This is important so that we know if we should automatically terminate
- * GDB or not. If the process is being restarted, we have to make sure
- * not to kill GDB.
- */
- private boolean fProcRestarting;
-
public GDBProcesses_7_0(DsfSession session) {
super(session);
}
@@ -1680,8 +1690,6 @@ public class GDBProcesses_7_0 extends AbstractDsfService
/** @since 4.0 */
@Override
public void restart(IContainerDMContext containerDmc, final Map<String, Object> attributes, final DataRequestMonitor<IContainerDMContext> rm) {
- fProcRestarting = true;
-
// Before performing the restart, check if the process is properly suspended.
// For such a case, we usually use IMIRunControl.isTargetAcceptingCommands().
// However, in non-stop, although the target is accepting command, a restart
@@ -1708,11 +1716,10 @@ public class GDBProcesses_7_0 extends AbstractDsfService
startOrRestart(newContainerDmc, attributes, true, new ImmediateDataRequestMonitor<IContainerDMContext>(rm) {
@Override
protected void handleCompleted() {
- if (!isSuccess()) {
- fProcRestarting = false;
- }
-
// In case the process we restarted was already exited, remove it from our list
+ // We do this here for GDB 7.1, because we know the proper groupId here which
+ // will change when the new restarted process will start. For GDB >= 7.2
+ // the groupId is fixed so we don't have to do this right away, but it won't hurt.
getExitedProcesses().remove(groupId);
setData(getData());
@@ -1757,11 +1764,12 @@ public class GDBProcesses_7_0 extends AbstractDsfService
return new StartOrRestartProcessSequence_7_0(executor, containerDmc, attributes, restart, rm);
}
-
/**
* Removes the process with the specified groupId from the launch.
+ *
+ * @return The label used for the console of that process.
*/
- private void removeProcessFromLaunch(String groupId) {
+ private String removeProcessFromLaunch(String groupId) {
ILaunch launch = (ILaunch)getSession().getModelAdapter(ILaunch.class);
IProcess[] launchProcesses = launch.getProcesses();
for (IProcess process : launchProcesses) {
@@ -1774,12 +1782,59 @@ public class GDBProcesses_7_0 extends AbstractDsfService
if (groupAttribute == null || groupAttribute.equals(MIProcesses.UNIQUE_GROUP_ID) ||
groupAttribute.equals(groupId)) {
launch.removeProcess(process);
- break;
+ return process.getLabel();
}
}
}
+ return null;
}
+ /**
+ * Add the specified process to the launch.
+ */
+ private void addProcessToLaunch(Process inferior, String groupId, String label) {
+ // Add the inferior to the launch.
+ // This cannot be done on the executor or things deadlock.
+ DebugPlugin.getDefault().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ // Add the inferior
+ // Need to go through DebugPlugin.newProcess so that we can use
+ // the overrideable process factory to allow others to override.
+ // First set attribute to specify we want to create an inferior process.
+ // Bug 210366
+ ILaunch launch = (ILaunch)getSession().getModelAdapter(ILaunch.class);
+ Map<String, String> attributes = new HashMap<String, String>();
+ attributes.put(IGdbDebugConstants.PROCESS_TYPE_CREATION_ATTR,
+ IGdbDebugConstants.INFERIOR_PROCESS_CREATION_VALUE);
+ IProcess runtimeInferior = DebugPlugin.newProcess(launch, inferior, label != null ? label : "", attributes); //$NON-NLS-1$
+ // Now set the inferior groupId
+ runtimeInferior.setAttribute(IGdbDebugConstants.INFERIOR_GROUPID_ATTR, groupId);
+ }
+ });
+ }
+
+ /**
+ * @since 5.2
+ */
+ @Override
+ public void addInferiorToLaunch(IContainerDMContext containerDmc, String label, PTY pty, RequestMonitor rm) {
+ if (containerDmc instanceof IMIContainerDMContext) {
+ String groupId = ((IMIContainerDMContext)containerDmc).getGroupId();
+ // Create an MIInferiorProcess to track the new instance of the process,
+ // remove the old one from the launch, and add the new one to the launch.
+ Process inferiorProcess;
+ if (pty == null) {
+ inferiorProcess = createInferiorProcess(containerDmc, fBackend.getMIOutputStream());
+ } else {
+ fGroupIdToPTYMap.put(groupId, pty);
+ inferiorProcess = createInferiorProcess(containerDmc, pty);
+ }
+ addProcessToLaunch(inferiorProcess, groupId, label);
+ }
+ rm.done();
+ }
+
@DsfServiceEventHandler
public void eventDispatched(final MIThreadGroupCreatedEvent e) {
IProcessDMContext procDmc = e.getDMContext();
@@ -1807,6 +1862,25 @@ public class GDBProcesses_7_0 extends AbstractDsfService
}
}
+ /** @since 5.2 */
+ protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, OutputStream outputStream) {
+ return new MIInferiorProcess(container, outputStream);
+ }
+
+ /** @since 5.2 */
+ protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, PTY pty) {
+ return new MIInferiorProcess(container, pty);
+ }
+
+ private void handleRestartingProcess(IMIContainerDMContext containerDmc) {
+ String label = removeProcessFromLaunch(containerDmc.getGroupId());
+ if (label != null) {
+ // We only add the process to the launch if the original process was part of the launch.
+ // For example, in the attach case, there is no process added to the launch
+ // We re-use the same PTY as the one used before the restart.
+ addInferiorToLaunch(containerDmc, label, fGroupIdToPTYMap.get(containerDmc.getGroupId()), new ImmediateRequestMonitor());
+ }
+ }
@DsfServiceEventHandler
public void eventDispatched(ISuspendedDMEvent e) {
@@ -1836,10 +1910,15 @@ public class GDBProcesses_7_0 extends AbstractDsfService
// Event handler when a thread or threadGroup starts
@DsfServiceEventHandler
public void eventDispatched(IStartedDMEvent e) {
- if (e instanceof ContainerStartedDMEvent) {
+ if (e.getDMContext() instanceof IMIContainerDMContext) {
+ String groupId = ((IMIContainerDMContext)e.getDMContext()).getGroupId();
+ if (fExitedGroupId.remove(groupId)) {
+ // The process in question is restarting.
+ handleRestartingProcess((IMIContainerDMContext)e.getDMContext());
+ }
+
fContainerCommandCache.reset();
fNumConnected++;
- fProcRestarting = false;
} else {
fThreadCommandCache.reset();
}
@@ -1848,30 +1927,58 @@ public class GDBProcesses_7_0 extends AbstractDsfService
// Event handler when a thread or a threadGroup exits
@DsfServiceEventHandler
public void eventDispatched(IExitedDMEvent e) {
- if (e instanceof ContainerExitedDMEvent) {
+ if (e.getDMContext() instanceof IMIContainerDMContext) {
+ fExitedGroupId.add(((IMIContainerDMContext)e.getDMContext()).getGroupId());
+
fContainerCommandCache.reset();
assert fNumConnected > 0;
fNumConnected--;
- if (Platform.getPreferencesService().getBoolean(GdbPlugin.PLUGIN_ID,
- IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB,
- true, null)) {
- if (fNumConnected == 0 && !fProcRestarting) {
- // If the last process we are debugging finishes, and we are not restarting it,
- // let's terminate GDB.
- // We also do this for a remote attach session, since the 'auto terminate' preference
- // is enabled. If users want to keep the session alive to attach to another process,
- // they can simply disable that preference
- fCommandControl.terminate(new ImmediateRequestMonitor());
- }
+ if (fNumConnected == 0 &&
+ Platform.getPreferencesService().getBoolean(GdbPlugin.PLUGIN_ID,
+ IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB,
+ true, null)) {
+ // If the last process we are debugging finishes and does not restart
+ // let's terminate GDB. We wait a small delay to see if the process will restart.
+ // We also do this for a remote attach session, since the 'auto terminate' preference
+ // is enabled. If users want to keep the session alive to attach to another process,
+ // they can simply disable that preference
+ getExecutor().schedule(new DsfRunnable() {
+ @Override
+ public void run() {
+ // Verify the process didn't restart by checking that we still have nothing connected
+ if (fNumConnected == 0) {
+ fCommandControl.terminate(new ImmediateRequestMonitor());
+ }
+ }
+ }, 500, TimeUnit.MILLISECONDS);
}
} else {
fThreadCommandCache.reset();
}
}
-
- @Override
+
+ /**
+ * @since 5.2
+ */
+ @DsfServiceEventHandler
+ public void eventDispatched(ICommandControlShutdownDMEvent e) {
+ // Now that the debug session is over, close the persistent PTY streams
+ for (PTY pty : fGroupIdToPTYMap.values()) {
+ if (pty instanceof PersistentPTY) {
+ try {
+ ((PersistentPTY)pty).closeStreams();
+ } catch (IOException e1) {
+ }
+ }
+ }
+ fGroupIdToPTYMap.clear();
+
+ fExitedGroupId.clear();
+ }
+
+ @Override
public void flushCache(IDMContext context) {
fContainerCommandCache.reset(context);
fThreadCommandCache.reset(context);
@@ -1944,6 +2051,16 @@ public class GDBProcesses_7_0 extends AbstractDsfService
}
if (groupId != null) {
+ // In case the process that just started was already exited (so we are dealing
+ // with a restart), remove it from our list.
+ // Do this here to handle the restart case triggered by GDB itself
+ // (user typing 'run' from the GDB console). In this case, we don't know yet
+ // we are dealing with a restart, but when we see the process come back, we
+ // know to remove it from the exited list. Note that this won't work
+ // for GDB 7.1 because the groupId of the new process is not the same as the old
+ // one. Not worth fixing for such an old version.
+ getExitedProcesses().remove(groupId);
+
getGroupToPidMap().put(groupId, pId);
// Mark that we know this new process, but don't fetch its
@@ -1969,8 +2086,8 @@ public class GDBProcesses_7_0 extends AbstractDsfService
// GDB is no longer debugging this process. Remove it from our list
String name = fDebuggedProcessesAndNames.remove(pId);
- if (!fProcRestarting && !getDetachedProcesses().remove(groupId)) {
- // If the process is not restarting and was not detached,
+ if (!getDetachedProcesses().remove(groupId)) {
+ // If the process was not detached,
// store it in the list of exited processes.
getExitedProcesses().put(groupId, new ExitedProcInfo(pId, name));
}
diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_12.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_12.java
new file mode 100644
index 00000000000..0e361157b7a
--- /dev/null
+++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_12.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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
+ *******************************************************************************/
+package org.eclipse.cdt.dsf.gdb.service;
+
+import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
+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.gdb.internal.GdbPlugin;
+import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext;
+import org.eclipse.cdt.dsf.mi.service.IMIProcessDMContext;
+import org.eclipse.cdt.dsf.mi.service.IMIRunControl;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * @since 5.2
+ */
+public class GDBProcesses_7_12 extends GDBProcesses_7_10 {
+
+ public GDBProcesses_7_12(DsfSession session) {
+ super(session);
+ }
+
+ @Override
+ public void terminate(IThreadDMContext thread, RequestMonitor rm) {
+ IGDBBackend backend = getServicesTracker().getService(IGDBBackend.class);
+ if (!backend.isFullGdbConsoleSupported()) {
+ super.terminate(thread, rm);
+ return;
+ }
+
+ // If we are running the full GDB console, there is a bug with GDB 7.12
+ // where after we terminate the process, the GDB prompt does not come
+ // back in the console. As a workaround, we first interrupt the process
+ // to get the prompt back, and only then kill the process.
+ // https://sourceware.org/bugzilla/show_bug.cgi?id=20766
+ if (thread instanceof IMIProcessDMContext) {
+ getDebuggingContext(
+ thread,
+ new ImmediateDataRequestMonitor<IDMContext>(rm) {
+ @Override
+ protected void handleSuccess() {
+ if (getData() instanceof IMIContainerDMContext) {
+ IMIContainerDMContext containerDmc = (IMIContainerDMContext)getData();
+ IMIRunControl runControl = getServicesTracker().getService(IMIRunControl.class);
+ if (runControl != null && !runControl.isSuspended(containerDmc)) {
+ runControl.suspend(containerDmc, new ImmediateRequestMonitor(rm) {
+ @Override
+ protected void handleCompleted() {
+ GDBProcesses_7_12.super.terminate(thread, rm);
+ }
+ });
+ } else {
+ GDBProcesses_7_12.super.terminate(thread, rm);
+ }
+ } else {
+ rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid process context.", null)); //$NON-NLS-1$
+ }
+ }
+ });
+ } else {
+ super.terminate(thread, rm);
+ }
+ }
+
+ @Override
+ public void detachDebuggerFromProcess(IDMContext dmc, RequestMonitor rm) {
+ if (DMContexts.getAncestorOfType(dmc, MIExitedProcessDMC.class) != null) {
+ super.detachDebuggerFromProcess(dmc, rm);
+ return;
+ }
+
+ IGDBBackend backend = getServicesTracker().getService(IGDBBackend.class);
+ if (!backend.isFullGdbConsoleSupported()) {
+ super.detachDebuggerFromProcess(dmc, rm);
+ return;
+ }
+
+ // If we are running the full GDB console, there is a bug with GDB 7.12
+ // where after we detach the process, the GDB prompt does not come
+ // back in the console. As a workaround, we first interrupt the process
+ // to get the prompt back, and only then detach the process.
+ // https://sourceware.org/bugzilla/show_bug.cgi?id=20766
+ if (!doCanDetachDebuggerFromProcess()) {
+ rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Detach not supported.", null)); //$NON-NLS-1$
+ return;
+ }
+
+ IMIContainerDMContext containerDmc = DMContexts.getAncestorOfType(dmc, IMIContainerDMContext.class);
+ IMIRunControl runControl = getServicesTracker().getService(IMIRunControl.class);
+ if (containerDmc != null && runControl != null && !runControl.isSuspended(containerDmc)) {
+ runControl.suspend(containerDmc, new ImmediateRequestMonitor(rm) {
+ @Override
+ protected void handleCompleted() {
+ GDBProcesses_7_12.super.detachDebuggerFromProcess(dmc, rm);
+ }
+ });
+ } else {
+ super.detachDebuggerFromProcess(dmc, rm);
+ }
+ }
+}
diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_3.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_3.java
index dbf8fe657d9..d180666cb94 100644
--- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_3.java
+++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_3.java
@@ -10,15 +10,19 @@
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.service;
+import java.io.OutputStream;
import java.util.Map;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
import org.eclipse.cdt.dsf.concurrent.Sequence;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
+import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess;
+import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess_7_3;
import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadGroupExitedEvent;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.cdt.utils.pty.PTY;
/**
* Version for GDB 7.3
@@ -39,6 +43,16 @@ public class GDBProcesses_7_3 extends GDBProcesses_7_2_1 {
}
@Override
+ protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, OutputStream outputStream) {
+ return new MIInferiorProcess_7_3(container, outputStream);
+ }
+
+ @Override
+ protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, PTY pty) {
+ return new MIInferiorProcess_7_3(container, pty);
+ }
+
+ @Override
@DsfServiceEventHandler
public void eventDispatched(MIThreadGroupExitedEvent e) {
super.eventDispatched(e);
diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java
index 62248f99972..77e40705ba7 100644
--- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java
+++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java
@@ -275,6 +275,9 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory {
@Override
protected IProcesses createProcessesService(DsfSession session) {
+ if (compareVersionWith(GDB_7_12_VERSION) >= 0) {
+ return new GDBProcesses_7_12(session);
+ }
if (compareVersionWith(GDB_7_10_VERSION) >= 0) {
return new GDBProcesses_7_10(session);
}
diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBProcesses.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBProcesses.java
index 17d7eb933f0..da6ddda56ac 100644
--- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBProcesses.java
+++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBProcesses.java
@@ -15,12 +15,18 @@ package org.eclipse.cdt.dsf.gdb.service;
import java.util.Map;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.datamodel.IDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
+import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext;
import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext;
import org.eclipse.cdt.dsf.mi.service.IMIProcesses;
+import org.eclipse.cdt.utils.pty.PTY;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
public interface IGDBProcesses extends IMIProcesses {
@@ -127,4 +133,17 @@ public interface IGDBProcesses extends IMIProcesses {
*/
void attachDebuggerToProcess(IProcessDMContext procCtx, String file, DataRequestMonitor<IDMContext> rm);
+ /**
+ * Adds a process representing the inferior to the launch. An I/O console will be created if necessary.
+ *
+ * @param containerDmc The inferior for which a a process will be added to the launch.
+ * @param label The name to use for the console if created.
+ * @param pty The PTY to be used by the console for I/O
+ * @param rm The requestMonitor that indicates that the request has been completed.
+ *
+ * @since 5.2
+ */
+ default void addInferiorToLaunch(IContainerDMContext containerDmc, String label, PTY pty, RequestMonitor rm) {
+ rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Not supported", null)); //$NON-NLS-1$
+ }
}
diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/StartOrRestartProcessSequence_7_0.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/StartOrRestartProcessSequence_7_0.java
index 89c04add883..9886bf8d86e 100644
--- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/StartOrRestartProcessSequence_7_0.java
+++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/StartOrRestartProcessSequence_7_0.java
@@ -24,6 +24,7 @@ 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.ImmediateDataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ReflectionSequence;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
@@ -31,14 +32,11 @@ import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContex
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.debug.service.command.ICommand;
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
-import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
-import org.eclipse.cdt.dsf.gdb.launching.InferiorRuntimeProcess;
import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils;
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.MIProcesses;
import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess;
import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo;
@@ -46,12 +44,10 @@ import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakpoint;
import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.utils.pty.PTY;
+import org.eclipse.cdt.utils.pty.PersistentPTY;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
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;
/**
* This class causes a process to start (run for the first time), or to
@@ -262,8 +258,8 @@ public class StartOrRestartProcessSequence_7_0 extends ReflectionSequence {
/**
* This method does the necessary work to setup the input/output streams for the
* inferior process, by either preparing the PTY to be used, or by simply leaving
- * the PTY null, which indicates that the input/output streams of the CLI should
- * be used instead; this decision is based on the type of session.
+ * the PTY null, which indicates that the input/output streams are handled externally;
+ * this decision is based on the type of session.
*/
@Execute
public void stepInitializeInputOutput(final RequestMonitor rm) {
@@ -273,10 +269,22 @@ public class StartOrRestartProcessSequence_7_0 extends ReflectionSequence {
fPty = null;
rm.done();
} else {
+ if (fRestart) {
+ // For a restart we re-use the previous PersistentPTY (or no pty if we couldn't tell GDB about it)
+ assert fPty instanceof PersistentPTY || fPty == null;
+ rm.done();
+ return;
+ }
+
// Every other type of session that can get to this code, is starting a new process
// and requires a pty for it.
try {
- fPty = new PTY();
+ // Use a PersistentPTY so it can be re-used for restarts.
+ // It is possible that the inferior will be restarted by the user from
+ // the GDB console, in which case, we are not able to create a new PTY
+ // for it; using a persistentPTY allows this to work since the persistentPTY
+ // does not need to be replaced but can continue to be used.
+ fPty = new PersistentPTY();
fPty.validateSlaveName();
// Tell GDB to use this PTY
@@ -298,12 +306,20 @@ public class StartOrRestartProcessSequence_7_0 extends ReflectionSequence {
}
}
- /** @since 4.7 */
+ /**
+ * @since 4.7
+ * @deprecated The creation of MIInferiorProcess has been moved to the IGDBProcesses service
+ */
+ @Deprecated
protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, OutputStream outputStream) {
return new MIInferiorProcess(container, outputStream);
}
- /** @since 4.7 */
+ /**
+ * @since 4.7
+ * @deprecated The creation of MIInferiorProcess has been moved to the IGDBProcesses service
+ */
+ @Deprecated
protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, PTY pty) {
return new MIInferiorProcess(container, pty);
}
@@ -312,29 +328,23 @@ public class StartOrRestartProcessSequence_7_0 extends ReflectionSequence {
* Before running the program, we must create its console for IO.
*/
@Execute
- public void stepCreateConsole(final RequestMonitor rm) {
+ public void stepCreateConsole(RequestMonitor rm) {
if (fBackend.getSessionType() == SessionType.REMOTE) {
// The program output for a remote session is handled by gdbserver. Therefore,
// no need to create an inferior process and add it to the launch.
rm.done();
return;
}
-
- Process inferiorProcess;
- if (fPty == null) {
- inferiorProcess = createInferiorProcess(fContainerDmc, fBackend.getMIOutputStream());
- } else {
- inferiorProcess = createInferiorProcess(fContainerDmc, fPty);
- }
-
- final Process inferior = inferiorProcess;
- final ILaunch launch = getContainerContext().getAdapter(ILaunch.class);
- // This is the groupId of the new process that will be started, even in the
- // case of a restart.
- final String groupId = ((IMIContainerDMContext)getContainerContext()).getGroupId();
+ if (fRestart) {
+ // For a restart, the IGDBProcesses service already handles creating the new
+ // console. We do it this way because a restart can be triggered by the user
+ // on the GDB console, so this class isn't even called in that case.
+ rm.done();
+ return;
+ }
- // For multi-process, we cannot simply use the name given by the backend service
+ // For multi-process, we cannot simply use the name given by the backend service
// because we may not be starting that process, but another one.
// Instead, we can look in the attributes for the binary name, which we stored
// there for this case, specifically.
@@ -350,46 +360,12 @@ public class StartOrRestartProcessSequence_7_0 extends ReflectionSequence {
defaultPathName);
final String pathLabel = new Path(progPathName).lastSegment();
- // Add the inferior to the launch.
- // This cannot be done on the executor or things deadlock.
- DebugPlugin.getDefault().asyncExec(new Runnable() {
+ // Adds the inferior to the launch which will also create the console
+ fProcService.addInferiorToLaunch(fContainerDmc, pathLabel, fPty, new ImmediateRequestMonitor() {
@Override
- public void run() {
- String label = pathLabel;
-
- if (fRestart) {
- // For a restart, remove the old inferior
- IProcess[] launchProcesses = launch.getProcesses();
- for (IProcess process : launchProcesses) {
- if (process instanceof InferiorRuntimeProcess) {
- String groupAttribute = process.getAttribute(IGdbDebugConstants.INFERIOR_GROUPID_ATTR);
-
- // if the groupAttribute is not set in the process we know we are dealing
- // with single process debugging so the one process is the one we want.
- // If the groupAttribute is set, then we must make sure it is the proper inferior
- if (groupAttribute == null || groupAttribute.equals(MIProcesses.UNIQUE_GROUP_ID) ||
- groupAttribute.equals(groupId)) {
- launch.removeProcess(process);
- // Use the exact same label as before
- label = process.getLabel();
- break;
- }
- }
- }
- }
-
- // Add the inferior
- // Need to go through DebugPlugin.newProcess so that we can use
- // the overrideable process factory to allow others to override.
- // First set attribute to specify we want to create an inferior process.
- // Bug 210366
- Map<String, String> attributes = new HashMap<String, String>();
- attributes.put(IGdbDebugConstants.PROCESS_TYPE_CREATION_ATTR,
- IGdbDebugConstants.INFERIOR_PROCESS_CREATION_VALUE);
- IProcess runtimeInferior = DebugPlugin.newProcess(launch, inferior, label, attributes);
- // Now set the inferior groupId
- runtimeInferior.setAttribute(IGdbDebugConstants.INFERIOR_GROUPID_ATTR, groupId);
-
+ protected void handleCompleted() {
+ // Accept errors. The console won't be created but the rest will mostly work
+ // This can especially happen if extenders did not implement IGDBProcesses.addInferiorToLaunch()
rm.done();
}
});
diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/StartOrRestartProcessSequence_7_3.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/StartOrRestartProcessSequence_7_3.java
index 5ea07188529..02ed62ce410 100644
--- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/StartOrRestartProcessSequence_7_3.java
+++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/StartOrRestartProcessSequence_7_3.java
@@ -31,11 +31,15 @@ public class StartOrRestartProcessSequence_7_3 extends StartOrRestartProcessSequ
super(executor, containerDmc, attributes, restart, rm);
}
+ /** @deprecated The creation of MIInferiorProcess has been moved to the IGDBProcesses service */
+ @Deprecated
@Override
protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, OutputStream outputStream) {
return new MIInferiorProcess_7_3(container, outputStream);
}
+ /** @deprecated The creation of MIInferiorProcess has been moved to the IGDBProcesses service */
+ @Deprecated
@Override
protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, PTY pty) {
return new MIInferiorProcess_7_3(container, pty);
diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/extensions/GDBProcesses_HEAD.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/extensions/GDBProcesses_HEAD.java
index fd49522341a..596d4b49092 100644
--- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/extensions/GDBProcesses_HEAD.java
+++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/extensions/GDBProcesses_HEAD.java
@@ -8,7 +8,7 @@
package org.eclipse.cdt.dsf.gdb.service.extensions;
import org.eclipse.cdt.dsf.debug.service.IProcesses;
-import org.eclipse.cdt.dsf.gdb.service.GDBProcesses_7_10;
+import org.eclipse.cdt.dsf.gdb.service.GDBProcesses_7_12;
import org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactory;
import org.eclipse.cdt.dsf.service.DsfSession;
@@ -36,14 +36,14 @@ import org.eclipse.cdt.dsf.service.DsfSession;
*
* @since 4.8
*/
-public class GDBProcesses_HEAD extends GDBProcesses_7_10 {
+public class GDBProcesses_HEAD extends GDBProcesses_7_12 {
public GDBProcesses_HEAD(DsfSession session) {
super(session);
validateGdbVersion(session);
}
- protected String getMinGDBVersionSupported() { return GdbDebugServicesFactory.GDB_7_10_VERSION; }
+ protected String getMinGDBVersionSupported() { return GdbDebugServicesFactory.GDB_7_12_VERSION; }
protected void validateGdbVersion(DsfSession session) {
GdbDebugServicesFactory.validateGdbVersion(session, getMinGDBVersionSupported(), this);
diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess.java
index d6e2b5eba01..b2dc5f2fbae 100644
--- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess.java
+++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess.java
@@ -45,6 +45,8 @@ import org.eclipse.cdt.dsf.debug.service.command.IEventListener;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.mi.service.IMICommandControl;
import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext;
+import org.eclipse.cdt.dsf.mi.service.IMIProcessDMContext;
+import org.eclipse.cdt.dsf.mi.service.MIProcesses;
import org.eclipse.cdt.dsf.mi.service.command.commands.CLICommand;
import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadGroupExitedEvent;
import org.eclipse.cdt.dsf.mi.service.command.output.MIGDBShowExitCodeInfo;
@@ -71,9 +73,8 @@ public class MIInferiorProcess extends Process
{
// Indicates that the inferior has been started
- // This is important for the case of a restart
- // where we need to make sure not to terminate
- // the new inferior, which was not started yet.
+ // It implies the ContainerDMContext is fully-formed
+ // with the pid of the process (as a parent dmc)
private boolean fStarted;
// Indicates that the inferior has been terminated
@@ -115,10 +116,8 @@ public class MIInferiorProcess extends Process
Integer fExitCode = null;
/**
- * @returns if that the inferior has been started.
- * This is important for the case of a restart
- * where we need to make sure not to terminate
- * the new inferior, which was not started yet.
+ * @returns whether the inferior has been started, which means
+ * we can obtain its process id.
* @since 4.7
*/
protected boolean isStarted() {
@@ -168,6 +167,14 @@ public class MIInferiorProcess extends Process
fSession.addServiceEventListener(this, null);
fContainerDMContext = container;
+ IMIProcessDMContext processDmc = DMContexts.getAncestorOfType(fContainerDMContext, IMIProcessDMContext.class);
+ if (processDmc != null && processDmc.getProcId() != MIProcesses.UNKNOWN_PROCESS_ID) {
+ // If we already know the pid, it implies the process is already started.
+ // It also means we won't get the IStartedDMEvent for the process.
+ // This happens when the inferior is restarted, in which case, this class
+ // is created after the process is running and the IStartedDMEvent was sent.
+ fStarted = true;
+ }
DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSession.getId());
fCommandControl = tracker.getService(IMICommandControl.class);
@@ -466,11 +473,18 @@ public class MIInferiorProcess extends Process
// For multi-process, make sure the exited event
// is actually for this inferior.
if (e.getDMContext().equals(fContainerDMContext)) {
+ // With recent changes, we notice a restart by the exited/started
+ // events, and only then create the new inferior; this means the new
+ // inferior will no longer receive the exited event for the old inferior.
+ // But it does not hurt to leave the below if check just in case.
+ assert fStarted : "Exited event should only be received for a started inferior"; //$NON-NLS-1$
if (fStarted) {
// Only mark this process as terminated if it was already
- // started. This is to protect ourselves in the case of
- // a restart, where the new inferior is already created
- // and gets the exited event for the old inferior.
+ // started. This was to protect ourselves in the case of
+ // a restart, where we used to create the new inferior before
+ // killing the old one. In that case we would get the exited
+ // event of the old inferior, which we had to ignore.
+ //
setTerminated();
}
}
@@ -489,7 +503,7 @@ public class MIInferiorProcess extends Process
public void eventDispatched(IStartedDMEvent e) {
if (e.getDMContext() instanceof IMIContainerDMContext) {
// Mark the inferior started if the event is for this inferior.
- // We may get other started events in the cases of a restarts
+ // We may get other started events in the case of a restart
if (!fStarted) {
// For multi-process, make sure the started event
// is actually for this inferior.
diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess_7_3.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess_7_3.java
index 99fc21ebf8a..4fd92f117e8 100644
--- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess_7_3.java
+++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess_7_3.java
@@ -77,6 +77,12 @@ public class MIInferiorProcess_7_3 extends MIInferiorProcess
public void eventDispatched(MIThreadGroupExitedEvent e) {
if (getContainer() instanceof IMIContainerDMContext) {
if (((IMIContainerDMContext)getContainer()).getGroupId().equals(e.getGroupId())) {
+ // With recent changes, we notice a restart by the exited/started
+ // events, and only then create the new inferior; this means the new
+ // inferior will no longer receive the exited event for the old inferior.
+ // But it does not hurt to leave the below if check just in case.
+ assert isStarted() : "Exited event should only be received for a started inferior"; //$NON-NLS-1$
+
if (isStarted()) {
// Only handle this event if this process was already
// started. This is to protect ourselves in the case of

Back to the top