1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-09 10:46:02 +02:00

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
This commit is contained in:
Marc Khouzam 2016-10-14 15:58:23 -04:00 committed by Gerrit Code Review @ Eclipse.org
parent e8480ca0f8
commit 39c781f81a
14 changed files with 491 additions and 116 deletions

View file

@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2 Bundle-ManifestVersion: 2
Bundle-Name: %pluginName Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.cdt.core.native;singleton:=true Bundle-SymbolicName: org.eclipse.cdt.core.native;singleton:=true
Bundle-Version: 5.9.0.qualifier Bundle-Version: 5.10.0.qualifier
Bundle-Activator: org.eclipse.cdt.internal.core.natives.CNativePlugin Bundle-Activator: org.eclipse.cdt.internal.core.natives.CNativePlugin
Bundle-Vendor: %providerName Bundle-Vendor: %providerName
Bundle-Localization: plugin Bundle-Localization: plugin

View file

@ -11,7 +11,7 @@
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<version>5.9.0-SNAPSHOT</version> <version>5.10.0-SNAPSHOT</version>
<artifactId>org.eclipse.cdt.core.native</artifactId> <artifactId>org.eclipse.cdt.core.native</artifactId>
<packaging>eclipse-plugin</packaging> <packaging>eclipse-plugin</packaging>
</project> </project>

View file

@ -0,0 +1,104 @@
/*******************************************************************************
* 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.utils.pty;
import java.io.IOException;
import org.eclipse.core.runtime.Platform;
/**
* A type of PTY that is persistent. This means that closing
* its streams (e.g., once the connection to the process is lost)
* will not close the PTY or the streams; instead, they will
* remain open to be used again by reconnecting to the streams.
* {@link PersistentPTY#closeStreams()} must be called to properly
* cleanup the streams once the PersistentPTY is known not be needed
* anymore.
*
* @since 5.10
*/
public class PersistentPTY extends PTY {
private class PersistentPTYInputStream extends PTYInputStream {
public PersistentPTYInputStream(MasterFD fd) {
super(fd);
}
@Override
public void close() throws IOException {
// This is the change to bring persistence.
// Don't actually close the stream.
}
public void realClose() throws IOException {
// This method should be called to actually close
// the stream once we know it won't be needed anymore
super.close();
}
@Override
protected void finalize() throws IOException {
realClose();
}
}
private class PersistentPTYOutputStream extends PTYOutputStream {
public PersistentPTYOutputStream(MasterFD fd, boolean sendEotBeforeClose) {
super(fd, sendEotBeforeClose);
}
@Override
public void close() throws IOException {
// This is the change to bring persistence.
// Don't actually close the stream.
}
public void realClose() throws IOException {
// This method should be called to actually close
// the stream once we know it won't be needed anymore
super.close();
}
@Override
protected void finalize() throws IOException {
realClose();
}
}
final PersistentPTYInputStream in2;
final PersistentPTYOutputStream out2;
public PersistentPTY() throws IOException {
this(Mode.CONSOLE);
}
public PersistentPTY(Mode mode) throws IOException {
super(mode);
in2 = new PersistentPTYInputStream(new MasterFD());
out2 = new PersistentPTYOutputStream(new MasterFD(), !Platform.OS_WIN32.equals(Platform.getOS()));
}
@Override
public PTYInputStream getInputStream() {
return in2;
}
@Override
public PTYOutputStream getOutputStream() {
return out2;
}
/**
* This method must be called once the PersistentPTY is
* no longer needed, so that its streams can be closed.
*/
public void closeStreams() throws IOException {
in2.realClose();
out2.realClose();
}
}

View file

@ -20,6 +20,14 @@
</message_arguments> </message_arguments>
</filter> </filter>
</resource> </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"> <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"> <filter comment="CDT allows adding default methods in a minor release" id="404000815">
<message_arguments> <message_arguments>

View file

@ -17,6 +17,8 @@
*******************************************************************************/ *******************************************************************************/
package org.eclipse.cdt.dsf.gdb.service; package org.eclipse.cdt.dsf.gdb.service;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -29,12 +31,14 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.IProcessInfo; import org.eclipse.cdt.core.IProcessInfo;
import org.eclipse.cdt.core.IProcessList; import org.eclipse.cdt.core.IProcessList;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfExecutor; 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.ImmediateDataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor; 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.BufferedCommandControl;
import org.eclipse.cdt.dsf.debug.service.command.CommandCache; 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.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.debug.service.command.IEventListener;
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants; 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.MIBreakpointsManager;
import org.eclipse.cdt.dsf.mi.service.MIProcesses; 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.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.MIThreadGroupCreatedEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadGroupExitedEvent; import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadGroupExitedEvent;
import org.eclipse.cdt.dsf.mi.service.command.output.MIConst; 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.AbstractDsfService;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession; 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.CoreException;
import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.model.IProcess;
import org.osgi.framework.BundleContext; import org.osgi.framework.BundleContext;
@ -588,6 +597,15 @@ public class GDBProcesses_7_0 extends AbstractDsfService
// be running at the time. Bug 303503 // be running at the time. Bug 303503
private Map<String, String> fDebuggedProcessesAndNames = new HashMap<>(); 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 * Information about an exited process
* @since 4.7 * @since 4.7
@ -665,14 +683,6 @@ public class GDBProcesses_7_0 extends AbstractDsfService
*/ */
private boolean fInitialProcess = true; 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) { public GDBProcesses_7_0(DsfSession session) {
super(session); super(session);
} }
@ -1680,8 +1690,6 @@ public class GDBProcesses_7_0 extends AbstractDsfService
/** @since 4.0 */ /** @since 4.0 */
@Override @Override
public void restart(IContainerDMContext containerDmc, final Map<String, Object> attributes, final DataRequestMonitor<IContainerDMContext> rm) { 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. // Before performing the restart, check if the process is properly suspended.
// For such a case, we usually use IMIRunControl.isTargetAcceptingCommands(). // For such a case, we usually use IMIRunControl.isTargetAcceptingCommands().
// However, in non-stop, although the target is accepting command, a restart // 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) { startOrRestart(newContainerDmc, attributes, true, new ImmediateDataRequestMonitor<IContainerDMContext>(rm) {
@Override @Override
protected void handleCompleted() { protected void handleCompleted() {
if (!isSuccess()) {
fProcRestarting = false;
}
// In case the process we restarted was already exited, remove it from our list // 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); getExitedProcesses().remove(groupId);
setData(getData()); setData(getData());
@ -1757,11 +1764,12 @@ public class GDBProcesses_7_0 extends AbstractDsfService
return new StartOrRestartProcessSequence_7_0(executor, containerDmc, attributes, restart, rm); return new StartOrRestartProcessSequence_7_0(executor, containerDmc, attributes, restart, rm);
} }
/** /**
* Removes the process with the specified groupId from the launch. * 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); ILaunch launch = (ILaunch)getSession().getModelAdapter(ILaunch.class);
IProcess[] launchProcesses = launch.getProcesses(); IProcess[] launchProcesses = launch.getProcesses();
for (IProcess process : launchProcesses) { for (IProcess process : launchProcesses) {
@ -1774,12 +1782,59 @@ public class GDBProcesses_7_0 extends AbstractDsfService
if (groupAttribute == null || groupAttribute.equals(MIProcesses.UNIQUE_GROUP_ID) || if (groupAttribute == null || groupAttribute.equals(MIProcesses.UNIQUE_GROUP_ID) ||
groupAttribute.equals(groupId)) { groupAttribute.equals(groupId)) {
launch.removeProcess(process); 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 @DsfServiceEventHandler
public void eventDispatched(final MIThreadGroupCreatedEvent e) { public void eventDispatched(final MIThreadGroupCreatedEvent e) {
IProcessDMContext procDmc = e.getDMContext(); 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 @DsfServiceEventHandler
public void eventDispatched(ISuspendedDMEvent e) { public void eventDispatched(ISuspendedDMEvent e) {
@ -1836,10 +1910,15 @@ public class GDBProcesses_7_0 extends AbstractDsfService
// Event handler when a thread or threadGroup starts // Event handler when a thread or threadGroup starts
@DsfServiceEventHandler @DsfServiceEventHandler
public void eventDispatched(IStartedDMEvent e) { 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(); fContainerCommandCache.reset();
fNumConnected++; fNumConnected++;
fProcRestarting = false;
} else { } else {
fThreadCommandCache.reset(); fThreadCommandCache.reset();
} }
@ -1848,30 +1927,58 @@ public class GDBProcesses_7_0 extends AbstractDsfService
// Event handler when a thread or a threadGroup exits // Event handler when a thread or a threadGroup exits
@DsfServiceEventHandler @DsfServiceEventHandler
public void eventDispatched(IExitedDMEvent e) { public void eventDispatched(IExitedDMEvent e) {
if (e instanceof ContainerExitedDMEvent) { if (e.getDMContext() instanceof IMIContainerDMContext) {
fExitedGroupId.add(((IMIContainerDMContext)e.getDMContext()).getGroupId());
fContainerCommandCache.reset(); fContainerCommandCache.reset();
assert fNumConnected > 0; assert fNumConnected > 0;
fNumConnected--; fNumConnected--;
if (Platform.getPreferencesService().getBoolean(GdbPlugin.PLUGIN_ID, if (fNumConnected == 0 &&
IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, Platform.getPreferencesService().getBoolean(GdbPlugin.PLUGIN_ID,
true, null)) { IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB,
if (fNumConnected == 0 && !fProcRestarting) { true, null)) {
// If the last process we are debugging finishes, and we are not restarting it, // If the last process we are debugging finishes and does not restart
// let's terminate GDB. // 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 // 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, // is enabled. If users want to keep the session alive to attach to another process,
// they can simply disable that preference // they can simply disable that preference
fCommandControl.terminate(new ImmediateRequestMonitor()); 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 { } else {
fThreadCommandCache.reset(); 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) { public void flushCache(IDMContext context) {
fContainerCommandCache.reset(context); fContainerCommandCache.reset(context);
fThreadCommandCache.reset(context); fThreadCommandCache.reset(context);
@ -1944,6 +2051,16 @@ public class GDBProcesses_7_0 extends AbstractDsfService
} }
if (groupId != null) { 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); getGroupToPidMap().put(groupId, pId);
// Mark that we know this new process, but don't fetch its // 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 // GDB is no longer debugging this process. Remove it from our list
String name = fDebuggedProcessesAndNames.remove(pId); String name = fDebuggedProcessesAndNames.remove(pId);
if (!fProcRestarting && !getDetachedProcesses().remove(groupId)) { if (!getDetachedProcesses().remove(groupId)) {
// If the process is not restarting and was not detached, // If the process was not detached,
// store it in the list of exited processes. // store it in the list of exited processes.
getExitedProcesses().put(groupId, new ExitedProcInfo(pId, name)); getExitedProcesses().put(groupId, new ExitedProcInfo(pId, name));
} }

View file

@ -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);
}
}
}

View file

@ -10,15 +10,19 @@
*******************************************************************************/ *******************************************************************************/
package org.eclipse.cdt.dsf.gdb.service; package org.eclipse.cdt.dsf.gdb.service;
import java.io.OutputStream;
import java.util.Map; import java.util.Map;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfExecutor; import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
import org.eclipse.cdt.dsf.concurrent.Sequence; import org.eclipse.cdt.dsf.concurrent.Sequence;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; 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.mi.service.command.events.MIThreadGroupExitedEvent;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.utils.pty.PTY;
/** /**
* Version for GDB 7.3 * Version for GDB 7.3
@ -38,6 +42,16 @@ public class GDBProcesses_7_3 extends GDBProcesses_7_2_1 {
return new StartOrRestartProcessSequence_7_3(executor, containerDmc, attributes, restart, rm); return new StartOrRestartProcessSequence_7_3(executor, containerDmc, attributes, restart, rm);
} }
@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 @Override
@DsfServiceEventHandler @DsfServiceEventHandler
public void eventDispatched(MIThreadGroupExitedEvent e) { public void eventDispatched(MIThreadGroupExitedEvent e) {

View file

@ -275,6 +275,9 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory {
@Override @Override
protected IProcesses createProcessesService(DsfSession session) { 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) { if (compareVersionWith(GDB_7_10_VERSION) >= 0) {
return new GDBProcesses_7_10(session); return new GDBProcesses_7_10(session);
} }

View file

@ -15,12 +15,18 @@ package org.eclipse.cdt.dsf.gdb.service;
import java.util.Map; import java.util.Map;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; 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.IDMContext;
import org.eclipse.cdt.dsf.datamodel.IDMEvent; import org.eclipse.cdt.dsf.datamodel.IDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; 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.IMIContainerDMContext;
import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext;
import org.eclipse.cdt.dsf.mi.service.IMIProcesses; 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 { public interface IGDBProcesses extends IMIProcesses {
@ -127,4 +133,17 @@ public interface IGDBProcesses extends IMIProcesses {
*/ */
void attachDebuggerToProcess(IProcessDMContext procCtx, String file, DataRequestMonitor<IDMContext> rm); 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$
}
} }

View file

@ -24,6 +24,7 @@ import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfExecutor; import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor; 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.ReflectionSequence;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.DMContexts; 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.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.debug.service.command.ICommand; import org.eclipse.cdt.dsf.debug.service.command.ICommand;
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; 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.internal.GdbPlugin;
import org.eclipse.cdt.dsf.gdb.launching.InferiorRuntimeProcess;
import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils; import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils;
import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl;
import org.eclipse.cdt.dsf.mi.service.IMICommandControl; import org.eclipse.cdt.dsf.mi.service.IMICommandControl;
import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; 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.CommandFactory;
import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess; import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess;
import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo; 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.mi.service.command.output.MIInfo;
import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.utils.pty.PTY; 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.IStatus;
import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status; 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 * 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 * 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 * 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 * the PTY null, which indicates that the input/output streams are handled externally;
* be used instead; this decision is based on the type of session. * this decision is based on the type of session.
*/ */
@Execute @Execute
public void stepInitializeInputOutput(final RequestMonitor rm) { public void stepInitializeInputOutput(final RequestMonitor rm) {
@ -273,10 +269,22 @@ public class StartOrRestartProcessSequence_7_0 extends ReflectionSequence {
fPty = null; fPty = null;
rm.done(); rm.done();
} else { } 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 // Every other type of session that can get to this code, is starting a new process
// and requires a pty for it. // and requires a pty for it.
try { 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(); fPty.validateSlaveName();
// Tell GDB to use this PTY // 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) { protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, OutputStream outputStream) {
return new MIInferiorProcess(container, 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) { protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, PTY pty) {
return new MIInferiorProcess(container, 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. * Before running the program, we must create its console for IO.
*/ */
@Execute @Execute
public void stepCreateConsole(final RequestMonitor rm) { public void stepCreateConsole(RequestMonitor rm) {
if (fBackend.getSessionType() == SessionType.REMOTE) { if (fBackend.getSessionType() == SessionType.REMOTE) {
// The program output for a remote session is handled by gdbserver. Therefore, // 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. // no need to create an inferior process and add it to the launch.
rm.done(); rm.done();
return; return;
} }
Process inferiorProcess; if (fRestart) {
if (fPty == null) { // For a restart, the IGDBProcesses service already handles creating the new
inferiorProcess = createInferiorProcess(fContainerDmc, fBackend.getMIOutputStream()); // console. We do it this way because a restart can be triggered by the user
} else { // on the GDB console, so this class isn't even called in that case.
inferiorProcess = createInferiorProcess(fContainerDmc, fPty); rm.done();
return;
} }
final Process inferior = inferiorProcess; // For multi-process, we cannot simply use the name given by the backend service
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();
// 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. // 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 // Instead, we can look in the attributes for the binary name, which we stored
// there for this case, specifically. // there for this case, specifically.
@ -350,46 +360,12 @@ public class StartOrRestartProcessSequence_7_0 extends ReflectionSequence {
defaultPathName); defaultPathName);
final String pathLabel = new Path(progPathName).lastSegment(); final String pathLabel = new Path(progPathName).lastSegment();
// Add the inferior to the launch. // Adds the inferior to the launch which will also create the console
// This cannot be done on the executor or things deadlock. fProcService.addInferiorToLaunch(fContainerDmc, pathLabel, fPty, new ImmediateRequestMonitor() {
DebugPlugin.getDefault().asyncExec(new Runnable() {
@Override @Override
public void run() { protected void handleCompleted() {
String label = pathLabel; // 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()
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);
rm.done(); rm.done();
} }
}); });

View file

@ -31,11 +31,15 @@ public class StartOrRestartProcessSequence_7_3 extends StartOrRestartProcessSequ
super(executor, containerDmc, attributes, restart, rm); super(executor, containerDmc, attributes, restart, rm);
} }
/** @deprecated The creation of MIInferiorProcess has been moved to the IGDBProcesses service */
@Deprecated
@Override @Override
protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, OutputStream outputStream) { protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, OutputStream outputStream) {
return new MIInferiorProcess_7_3(container, outputStream); return new MIInferiorProcess_7_3(container, outputStream);
} }
/** @deprecated The creation of MIInferiorProcess has been moved to the IGDBProcesses service */
@Deprecated
@Override @Override
protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, PTY pty) { protected MIInferiorProcess createInferiorProcess(IContainerDMContext container, PTY pty) {
return new MIInferiorProcess_7_3(container, pty); return new MIInferiorProcess_7_3(container, pty);

View file

@ -8,7 +8,7 @@
package org.eclipse.cdt.dsf.gdb.service.extensions; package org.eclipse.cdt.dsf.gdb.service.extensions;
import org.eclipse.cdt.dsf.debug.service.IProcesses; 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.gdb.service.GdbDebugServicesFactory;
import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.dsf.service.DsfSession;
@ -36,14 +36,14 @@ import org.eclipse.cdt.dsf.service.DsfSession;
* *
* @since 4.8 * @since 4.8
*/ */
public class GDBProcesses_HEAD extends GDBProcesses_7_10 { public class GDBProcesses_HEAD extends GDBProcesses_7_12 {
public GDBProcesses_HEAD(DsfSession session) { public GDBProcesses_HEAD(DsfSession session) {
super(session); super(session);
validateGdbVersion(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) { protected void validateGdbVersion(DsfSession session) {
GdbDebugServicesFactory.validateGdbVersion(session, getMinGDBVersionSupported(), this); GdbDebugServicesFactory.validateGdbVersion(session, getMinGDBVersionSupported(), this);

View file

@ -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.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.mi.service.IMICommandControl; import org.eclipse.cdt.dsf.mi.service.IMICommandControl;
import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; 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.commands.CLICommand;
import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadGroupExitedEvent; import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadGroupExitedEvent;
import org.eclipse.cdt.dsf.mi.service.command.output.MIGDBShowExitCodeInfo; 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 // Indicates that the inferior has been started
// This is important for the case of a restart // It implies the ContainerDMContext is fully-formed
// where we need to make sure not to terminate // with the pid of the process (as a parent dmc)
// the new inferior, which was not started yet.
private boolean fStarted; private boolean fStarted;
// Indicates that the inferior has been terminated // Indicates that the inferior has been terminated
@ -115,10 +116,8 @@ public class MIInferiorProcess extends Process
Integer fExitCode = null; Integer fExitCode = null;
/** /**
* @returns if that the inferior has been started. * @returns whether the inferior has been started, which means
* This is important for the case of a restart * we can obtain its process id.
* where we need to make sure not to terminate
* the new inferior, which was not started yet.
* @since 4.7 * @since 4.7
*/ */
protected boolean isStarted() { protected boolean isStarted() {
@ -168,6 +167,14 @@ public class MIInferiorProcess extends Process
fSession.addServiceEventListener(this, null); fSession.addServiceEventListener(this, null);
fContainerDMContext = container; 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()); DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSession.getId());
fCommandControl = tracker.getService(IMICommandControl.class); fCommandControl = tracker.getService(IMICommandControl.class);
@ -466,11 +473,18 @@ public class MIInferiorProcess extends Process
// For multi-process, make sure the exited event // For multi-process, make sure the exited event
// is actually for this inferior. // is actually for this inferior.
if (e.getDMContext().equals(fContainerDMContext)) { 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) { if (fStarted) {
// Only mark this process as terminated if it was already // Only mark this process as terminated if it was already
// started. This is to protect ourselves in the case of // started. This was to protect ourselves in the case of
// a restart, where the new inferior is already created // a restart, where we used to create the new inferior before
// and gets the exited event for the old inferior. // killing the old one. In that case we would get the exited
// event of the old inferior, which we had to ignore.
//
setTerminated(); setTerminated();
} }
} }
@ -489,7 +503,7 @@ public class MIInferiorProcess extends Process
public void eventDispatched(IStartedDMEvent e) { public void eventDispatched(IStartedDMEvent e) {
if (e.getDMContext() instanceof IMIContainerDMContext) { if (e.getDMContext() instanceof IMIContainerDMContext) {
// Mark the inferior started if the event is for this inferior. // 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) { if (!fStarted) {
// For multi-process, make sure the started event // For multi-process, make sure the started event
// is actually for this inferior. // is actually for this inferior.

View file

@ -77,6 +77,12 @@ public class MIInferiorProcess_7_3 extends MIInferiorProcess
public void eventDispatched(MIThreadGroupExitedEvent e) { public void eventDispatched(MIThreadGroupExitedEvent e) {
if (getContainer() instanceof IMIContainerDMContext) { if (getContainer() instanceof IMIContainerDMContext) {
if (((IMIContainerDMContext)getContainer()).getGroupId().equals(e.getGroupId())) { 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()) { if (isStarted()) {
// Only handle this event if this process was already // Only handle this event if this process was already
// started. This is to protect ourselves in the case of // started. This is to protect ourselves in the case of