mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-23 06:32:10 +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:
parent
e8480ca0f8
commit
39c781f81a
14 changed files with 491 additions and 116 deletions
|
@ -2,7 +2,7 @@ Manifest-Version: 1.0
|
|||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: %pluginName
|
||||
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-Vendor: %providerName
|
||||
Bundle-Localization: plugin
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<version>5.9.0-SNAPSHOT</version>
|
||||
<version>5.10.0-SNAPSHOT</version>
|
||||
<artifactId>org.eclipse.cdt.core.native</artifactId>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
@ -38,6 +42,16 @@ public class GDBProcesses_7_3 extends GDBProcesses_7_2_1 {
|
|||
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
|
||||
@DsfServiceEventHandler
|
||||
public void eventDispatched(MIThreadGroupExitedEvent e) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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$
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// 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();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue