mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-06-06 09:16:02 +02:00
[305178] Handling of failure cases when interrupting the target.
This commit is contained in:
parent
2edac02083
commit
2ff6a31767
6 changed files with 209 additions and 43 deletions
|
@ -36,7 +36,9 @@ import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
|||
import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils;
|
||||
import org.eclipse.cdt.dsf.gdb.service.command.GDBControl.InitializationShutdownStep;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMIBackend;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MISignalEvent;
|
||||
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.spawner.ProcessFactory;
|
||||
import org.eclipse.cdt.utils.spawner.Spawner;
|
||||
|
@ -99,6 +101,14 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend {
|
|||
private int fGDBExitValue;
|
||||
private int fGDBLaunchTimeout = 30;
|
||||
|
||||
/**
|
||||
* A Job that will set a failed status
|
||||
* in the proper request monitor, if the interrupt
|
||||
* did not succeed after a certain time.
|
||||
*/
|
||||
private MonitorInterruptJob fInterruptFailedJob;
|
||||
|
||||
|
||||
public GDBBackend(DsfSession session, ILaunchConfiguration lc) {
|
||||
super(session);
|
||||
fBackendId = "gdb[" +Integer.toString(fgInstanceCounter++) + "]"; //$NON-NLS-1$//$NON-NLS-2$
|
||||
|
@ -364,6 +374,20 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0
|
||||
*/
|
||||
public void interrupt(final RequestMonitor rm) {
|
||||
if (fProcess instanceof Spawner) {
|
||||
Spawner gdbSpawner = (Spawner) fProcess;
|
||||
gdbSpawner.interrupt();
|
||||
fInterruptFailedJob = new MonitorInterruptJob(rm);
|
||||
} else {
|
||||
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Cannot interrupt.", null)); //$NON-NLS-1$
|
||||
rm.done();
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
// destroy() should be supported even if it's not spawner.
|
||||
if (getState() == State.STARTED) {
|
||||
|
@ -558,6 +582,8 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend {
|
|||
IGDBBackend.class.getName() },
|
||||
new Hashtable<String,String>());
|
||||
|
||||
getSession().addServiceEventListener(GDBBackend.this, null);
|
||||
|
||||
/*
|
||||
* This event is not consumed by any one at present, instead it's
|
||||
* the GDBControlInitializedDMEvent that's used to indicate that GDB
|
||||
|
@ -574,6 +600,7 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend {
|
|||
@Override
|
||||
protected void shutdown(RequestMonitor requestMonitor) {
|
||||
unregister();
|
||||
getSession().removeServiceEventListener(GDBBackend.this);
|
||||
requestMonitor.done();
|
||||
}
|
||||
}
|
||||
|
@ -625,4 +652,50 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitors an ongoing interrupt to be able to properly
|
||||
* deal with the request monitor.
|
||||
*/
|
||||
private class MonitorInterruptJob extends Job {
|
||||
private final RequestMonitor fRequestMonitor;
|
||||
|
||||
public MonitorInterruptJob(RequestMonitor rm) {
|
||||
super("Interrupt monitor job.");
|
||||
setSystem(true);
|
||||
fRequestMonitor = rm;
|
||||
schedule(5000); // Give the interrupt 5 seconds to succeed
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IStatus run(IProgressMonitor monitor) {
|
||||
getExecutor().submit(
|
||||
new DsfRunnable() {
|
||||
public void run() {
|
||||
fRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Interrupt failed.", null)); //$NON-NLS-1$
|
||||
fRequestMonitor.done();
|
||||
}
|
||||
});
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
|
||||
public RequestMonitor getRequestMonitor() { return fRequestMonitor; }
|
||||
}
|
||||
|
||||
/*
|
||||
* We must listen for an MI event and not a higher-level ISuspendedEvent.
|
||||
* The reason is that some ISuspendedEvent are not sent when the target stops,
|
||||
* in cases where we don't want to views to update.
|
||||
* For example, if we want to interrupt the target to set a breakpoint,
|
||||
* this interruption is done silently; we will receive the MI event though.
|
||||
*/
|
||||
/** @since 3.0 */
|
||||
@DsfServiceEventHandler
|
||||
public void eventDispatched(final MISignalEvent e) {
|
||||
if (fInterruptFailedJob != null) {
|
||||
if (fInterruptFailedJob.cancel()) {
|
||||
fInterruptFailedJob.getRequestMonitor().done();
|
||||
}
|
||||
fInterruptFailedJob = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,11 +138,11 @@ public class GDBRunControl extends MIRunControl {
|
|||
@Override
|
||||
protected void handleSuccess() {
|
||||
if (getData()) {
|
||||
fGdb.interrupt();
|
||||
fGdb.interrupt(rm);
|
||||
} else {
|
||||
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Context cannot be suspended.", null)); //$NON-NLS-1$
|
||||
rm.done();
|
||||
}
|
||||
rm.done();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -144,11 +144,11 @@ public class GDBRunControl_7_0 extends MIRunControl implements IReverseRunContro
|
|||
@Override
|
||||
protected void handleSuccess() {
|
||||
if (getData()) {
|
||||
fGdb.interrupt();
|
||||
fGdb.interrupt(rm);
|
||||
} else {
|
||||
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Context cannot be suspended.", null)); //$NON-NLS-1$
|
||||
rm.done();
|
||||
}
|
||||
rm.done();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -265,13 +265,17 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
|
|||
private RunToLineActiveOperation fRunToLineActiveOperation = null;
|
||||
|
||||
/**
|
||||
* A counter of MIRunning/MIStopped events that should be kept silent for the specified thread.
|
||||
* Indicates that the next MIRunning event for this thread should be silenced.
|
||||
*/
|
||||
private class DisableRunningAndStoppedEvents {
|
||||
public IMIExecutionDMContext executionDmc = null;
|
||||
public int count = 0;
|
||||
};
|
||||
private DisableRunningAndStoppedEvents fDisableRunningAndStoppedEvents = new DisableRunningAndStoppedEvents();
|
||||
private IMIExecutionDMContext fDisableNextRunningEventDmc;
|
||||
/**
|
||||
* Indicates that the next MISignal (MIStopped) event for this thread should be silenced.
|
||||
*/
|
||||
private IMIExecutionDMContext fDisableNextSignalEventDmc;
|
||||
/**
|
||||
* Stores the silenced MIStopped event in case we need to use it for a failure.
|
||||
*/
|
||||
private MIStoppedEvent fSilencedSignalEvent;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Initialization and shutdown
|
||||
|
@ -925,12 +929,22 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
|
|||
|
||||
fExecutionDmcToSuspend = (IMIExecutionDMContext)getData()[0];
|
||||
|
||||
// Don't broadcast the coming stopped/running events
|
||||
assert fDisableRunningAndStoppedEvents.count == 0;
|
||||
fDisableRunningAndStoppedEvents.count++;
|
||||
fDisableRunningAndStoppedEvents.executionDmc = fExecutionDmcToSuspend;
|
||||
assert fDisableNextRunningEventDmc == null;
|
||||
assert fDisableNextSignalEventDmc == null;
|
||||
|
||||
suspend(fExecutionDmcToSuspend, rm);
|
||||
// Don't broadcast the next stopped signal event
|
||||
fDisableNextSignalEventDmc = fExecutionDmcToSuspend;
|
||||
|
||||
suspend(fExecutionDmcToSuspend,
|
||||
new RequestMonitor(getExecutor(), rm) {
|
||||
@Override
|
||||
protected void handleFailure() {
|
||||
// We weren't able to suspend, so abort the operation
|
||||
fDisableNextSignalEventDmc = null;
|
||||
super.handleFailure();
|
||||
};
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
@ -946,15 +960,39 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
|
|||
@Override
|
||||
public void execute(final RequestMonitor rm) {
|
||||
if (!fTargetAvailable) {
|
||||
assert fDisableRunningAndStoppedEvents.count == 0 || fDisableRunningAndStoppedEvents.count == 1;
|
||||
fDisableRunningAndStoppedEvents.count++;
|
||||
fDisableRunningAndStoppedEvents.executionDmc = fExecutionDmcToSuspend;
|
||||
assert fDisableNextRunningEventDmc == null;
|
||||
fDisableNextRunningEventDmc = fExecutionDmcToSuspend;
|
||||
|
||||
// Can't use the resume() call because we 'silently' stopped
|
||||
// so resume() will not know we are actually stopped
|
||||
fConnection.queueCommand(
|
||||
fCommandFactory.createMIExecContinue(fExecutionDmcToSuspend),
|
||||
new DataRequestMonitor<MIInfo>(getExecutor(), rm));
|
||||
new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
|
||||
@Override
|
||||
protected void handleSuccess() {
|
||||
fSilencedSignalEvent = null;
|
||||
super.handleSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleFailure() {
|
||||
// Darn, we're unable to restart the target. Must cleanup!
|
||||
fDisableNextRunningEventDmc = null;
|
||||
|
||||
// We must also sent the Stopped event that we had kept silent
|
||||
if (fSilencedSignalEvent != null) {
|
||||
eventDispatched(fSilencedSignalEvent);
|
||||
fSilencedSignalEvent = null;
|
||||
} else {
|
||||
// Maybe the stopped event didn't arrive yet.
|
||||
// We don't want to silence it anymore
|
||||
fDisableNextSignalEventDmc = null;
|
||||
}
|
||||
|
||||
super.handleFailure();
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
// We didn't suspend the thread, so we don't need to resume it
|
||||
rm.done();
|
||||
|
@ -972,10 +1010,10 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
|
|||
*/
|
||||
@DsfServiceEventHandler
|
||||
public void eventDispatched(final MIRunningEvent e) {
|
||||
if (fDisableRunningAndStoppedEvents.count > 0 &&
|
||||
fDisableRunningAndStoppedEvents.executionDmc.equals(e.getDMContext())) {
|
||||
if (fDisableNextRunningEventDmc != null &&
|
||||
fDisableNextRunningEventDmc.equals(e.getDMContext())) {
|
||||
|
||||
fDisableRunningAndStoppedEvents.count--;
|
||||
fDisableNextRunningEventDmc = null;
|
||||
|
||||
// Don't broadcast the running event
|
||||
return;
|
||||
|
@ -1041,10 +1079,11 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
|
|||
}
|
||||
}
|
||||
}
|
||||
if (fDisableRunningAndStoppedEvents.count > 0 &&
|
||||
fDisableRunningAndStoppedEvents.executionDmc.equals(e.getDMContext())) {
|
||||
if (fDisableNextSignalEventDmc != null && e instanceof MISignalEvent &&
|
||||
fDisableNextSignalEventDmc.equals(e.getDMContext())) {
|
||||
|
||||
fDisableRunningAndStoppedEvents.count--;
|
||||
fDisableNextSignalEventDmc = null;
|
||||
fSilencedSignalEvent = e;
|
||||
|
||||
// Don't broadcast the stopped event
|
||||
return;
|
||||
|
|
|
@ -14,6 +14,7 @@ package org.eclipse.cdt.dsf.gdb.service;
|
|||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMIBackend;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
|
@ -100,7 +101,17 @@ public interface IGDBBackend extends IMIBackend {
|
|||
* Sends an interrupt signal to the GDB process.
|
||||
*/
|
||||
public void interrupt();
|
||||
|
||||
|
||||
/**
|
||||
* Interrupts the backend.
|
||||
* The request monitor indicates if the interrupt succeeded or not;
|
||||
* it may or may not indicate that the backend is already interrupted,
|
||||
* but does indicate if the backend will become interrupted.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public void interrupt(RequestMonitor rm);
|
||||
|
||||
/**
|
||||
* @return The type of the session currently ongoing with the backend
|
||||
*/
|
||||
|
|
|
@ -324,10 +324,18 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
|
|||
private StateChangeReason fStateChangeReason;
|
||||
private IExecutionDMContext fStateChangeTriggeringContext;
|
||||
/**
|
||||
* A counter of MIRunning/MIStopped events that should
|
||||
* be kept silent.
|
||||
* Indicates that the next MIRunning event should be silenced.
|
||||
*/
|
||||
private int fDisableRunningAndStoppedEventsCount = 0;
|
||||
private boolean fDisableNextRunningEvent;
|
||||
/**
|
||||
* Indicates that the next MISignal (MIStopped) event should be silenced.
|
||||
*/
|
||||
private boolean fDisableNextSignalEvent;
|
||||
/**
|
||||
* Stores the silenced MIStopped event in case we need to use it
|
||||
* for a failure.
|
||||
*/
|
||||
private MIStoppedEvent fSilencedSignalEvent;
|
||||
|
||||
private static final int FAKE_THREAD_ID = 0;
|
||||
|
||||
|
@ -396,9 +404,9 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
|
|||
*/
|
||||
@DsfServiceEventHandler
|
||||
public void eventDispatched(final MIRunningEvent e) {
|
||||
if (fDisableRunningAndStoppedEventsCount > 0) {
|
||||
fDisableRunningAndStoppedEventsCount--;
|
||||
// We don't broadcast running events right now
|
||||
if (fDisableNextRunningEvent) {
|
||||
fDisableNextRunningEvent = false;
|
||||
// We don't broadcast this running event
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -421,9 +429,10 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
|
|||
*/
|
||||
@DsfServiceEventHandler
|
||||
public void eventDispatched(final MIStoppedEvent e) {
|
||||
if (fDisableRunningAndStoppedEventsCount > 0) {
|
||||
fDisableRunningAndStoppedEventsCount--;
|
||||
// We don't broadcast stopped events right now
|
||||
if (fDisableNextSignalEvent && e instanceof MISignalEvent) {
|
||||
fDisableNextSignalEvent = false;
|
||||
fSilencedSignalEvent = e;
|
||||
// We don't broadcast this stopped event
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -443,7 +452,7 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
|
|||
// Set the triggering context only if it's not the container context, since we are looking for a thread.
|
||||
IExecutionDMContext triggeringCtx = !e.getDMContext().equals(containerDmc) ? e.getDMContext() : null;
|
||||
if (triggeringCtx == null) {
|
||||
// Still no thread. Let's ask the bakend for one.
|
||||
// Still no thread. Let's ask the backend for one.
|
||||
// We need a proper thread id for the debug view to select the right thread
|
||||
// Bug 300096 comment #15 and Bug 302597
|
||||
getConnection().queueCommand(
|
||||
|
@ -934,10 +943,20 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
|
|||
@Override
|
||||
public void execute(final RequestMonitor rm) {
|
||||
if (!isTargetAvailable()) {
|
||||
// Don't broadcast the coming stopped/running events
|
||||
assert fDisableRunningAndStoppedEventsCount == 0;
|
||||
fDisableRunningAndStoppedEventsCount++;
|
||||
suspend(getContextToSuspend(), rm);
|
||||
assert fDisableNextRunningEvent == false;
|
||||
assert fDisableNextSignalEvent == false;
|
||||
|
||||
// Don't broadcast the coming stopped signal event
|
||||
fDisableNextSignalEvent = true;
|
||||
suspend(getContextToSuspend(),
|
||||
new RequestMonitor(getExecutor(), rm) {
|
||||
@Override
|
||||
protected void handleFailure() {
|
||||
// We weren't able to suspend, so abort the operation
|
||||
fDisableNextSignalEvent = false;
|
||||
super.handleFailure();
|
||||
};
|
||||
});
|
||||
} else {
|
||||
rm.done();
|
||||
}
|
||||
|
@ -951,14 +970,38 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
|
|||
@Override
|
||||
public void execute(final RequestMonitor rm) {
|
||||
if (!isTargetAvailable()) {
|
||||
assert fDisableRunningAndStoppedEventsCount == 0 || fDisableRunningAndStoppedEventsCount == 1;
|
||||
fDisableRunningAndStoppedEventsCount++;
|
||||
assert fDisableNextRunningEvent == false;
|
||||
fDisableNextRunningEvent = true;
|
||||
|
||||
// Can't use the resume() call because we 'silently' stopped
|
||||
// so resume() will not know we are actually stopped
|
||||
fConnection.queueCommand(
|
||||
fCommandFactory.createMIExecContinue(getContextToSuspend()),
|
||||
new DataRequestMonitor<MIInfo>(getExecutor(), rm));
|
||||
new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
|
||||
@Override
|
||||
protected void handleSuccess() {
|
||||
fSilencedSignalEvent = null;
|
||||
super.handleSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleFailure() {
|
||||
// Darn, we're unable to restart the target. Must cleanup!
|
||||
fDisableNextRunningEvent = false;
|
||||
|
||||
// We must also sent the Stopped event that we had kept silent
|
||||
if (fSilencedSignalEvent != null) {
|
||||
eventDispatched(fSilencedSignalEvent);
|
||||
fSilencedSignalEvent = null;
|
||||
} else {
|
||||
// Maybe the stopped event didn't arrive yet.
|
||||
// We don't want to silence it anymore
|
||||
fDisableNextSignalEvent = false;
|
||||
}
|
||||
|
||||
super.handleFailure();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// We didn't suspend the container, so we don't need to resume it
|
||||
rm.done();
|
||||
|
|
Loading…
Add table
Reference in a new issue