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

[305178] Handling of failure cases when interrupting the target.

This commit is contained in:
Marc Khouzam 2010-03-09 19:59:33 +00:00
parent 2edac02083
commit 2ff6a31767
6 changed files with 209 additions and 43 deletions

View file

@ -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.launching.LaunchUtils;
import org.eclipse.cdt.dsf.gdb.service.command.GDBControl.InitializationShutdownStep; 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.IMIBackend;
import org.eclipse.cdt.dsf.mi.service.command.events.MISignalEvent;
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.DsfSession; import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.utils.spawner.ProcessFactory; import org.eclipse.cdt.utils.spawner.ProcessFactory;
import org.eclipse.cdt.utils.spawner.Spawner; import org.eclipse.cdt.utils.spawner.Spawner;
@ -99,6 +101,14 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend {
private int fGDBExitValue; private int fGDBExitValue;
private int fGDBLaunchTimeout = 30; 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) { public GDBBackend(DsfSession session, ILaunchConfiguration lc) {
super(session); super(session);
fBackendId = "gdb[" +Integer.toString(fgInstanceCounter++) + "]"; //$NON-NLS-1$//$NON-NLS-2$ 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() { public void destroy() {
// destroy() should be supported even if it's not spawner. // destroy() should be supported even if it's not spawner.
if (getState() == State.STARTED) { if (getState() == State.STARTED) {
@ -558,6 +582,8 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend {
IGDBBackend.class.getName() }, IGDBBackend.class.getName() },
new Hashtable<String,String>()); new Hashtable<String,String>());
getSession().addServiceEventListener(GDBBackend.this, null);
/* /*
* This event is not consumed by any one at present, instead it's * This event is not consumed by any one at present, instead it's
* the GDBControlInitializedDMEvent that's used to indicate that GDB * the GDBControlInitializedDMEvent that's used to indicate that GDB
@ -574,6 +600,7 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend {
@Override @Override
protected void shutdown(RequestMonitor requestMonitor) { protected void shutdown(RequestMonitor requestMonitor) {
unregister(); unregister();
getSession().removeServiceEventListener(GDBBackend.this);
requestMonitor.done(); 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;
}
}
} }

View file

@ -138,11 +138,11 @@ public class GDBRunControl extends MIRunControl {
@Override @Override
protected void handleSuccess() { protected void handleSuccess() {
if (getData()) { if (getData()) {
fGdb.interrupt(); fGdb.interrupt(rm);
} else { } else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Context cannot be suspended.", null)); //$NON-NLS-1$ rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Context cannot be suspended.", null)); //$NON-NLS-1$
rm.done();
} }
rm.done();
} }
}); });
} }

View file

@ -144,11 +144,11 @@ public class GDBRunControl_7_0 extends MIRunControl implements IReverseRunContro
@Override @Override
protected void handleSuccess() { protected void handleSuccess() {
if (getData()) { if (getData()) {
fGdb.interrupt(); fGdb.interrupt(rm);
} else { } else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Context cannot be suspended.", null)); //$NON-NLS-1$ rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Context cannot be suspended.", null)); //$NON-NLS-1$
rm.done();
} }
rm.done();
} }
}); });
} }

View file

@ -265,13 +265,17 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
private RunToLineActiveOperation fRunToLineActiveOperation = null; 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 { private IMIExecutionDMContext fDisableNextRunningEventDmc;
public IMIExecutionDMContext executionDmc = null; /**
public int count = 0; * Indicates that the next MISignal (MIStopped) event for this thread should be silenced.
}; */
private DisableRunningAndStoppedEvents fDisableRunningAndStoppedEvents = new DisableRunningAndStoppedEvents(); private IMIExecutionDMContext fDisableNextSignalEventDmc;
/**
* Stores the silenced MIStopped event in case we need to use it for a failure.
*/
private MIStoppedEvent fSilencedSignalEvent;
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Initialization and shutdown // Initialization and shutdown
@ -925,12 +929,22 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
fExecutionDmcToSuspend = (IMIExecutionDMContext)getData()[0]; fExecutionDmcToSuspend = (IMIExecutionDMContext)getData()[0];
// Don't broadcast the coming stopped/running events assert fDisableNextRunningEventDmc == null;
assert fDisableRunningAndStoppedEvents.count == 0; assert fDisableNextSignalEventDmc == null;
fDisableRunningAndStoppedEvents.count++;
fDisableRunningAndStoppedEvents.executionDmc = fExecutionDmcToSuspend; // 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();
};
});
suspend(fExecutionDmcToSuspend, rm);
} }
}); });
} else { } else {
@ -946,15 +960,39 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
@Override @Override
public void execute(final RequestMonitor rm) { public void execute(final RequestMonitor rm) {
if (!fTargetAvailable) { if (!fTargetAvailable) {
assert fDisableRunningAndStoppedEvents.count == 0 || fDisableRunningAndStoppedEvents.count == 1; assert fDisableNextRunningEventDmc == null;
fDisableRunningAndStoppedEvents.count++; fDisableNextRunningEventDmc = fExecutionDmcToSuspend;
fDisableRunningAndStoppedEvents.executionDmc = fExecutionDmcToSuspend;
// Can't use the resume() call because we 'silently' stopped // Can't use the resume() call because we 'silently' stopped
// so resume() will not know we are actually stopped // so resume() will not know we are actually stopped
fConnection.queueCommand( fConnection.queueCommand(
fCommandFactory.createMIExecContinue(fExecutionDmcToSuspend), 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 { } else {
// We didn't suspend the thread, so we don't need to resume it // We didn't suspend the thread, so we don't need to resume it
rm.done(); rm.done();
@ -972,10 +1010,10 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
*/ */
@DsfServiceEventHandler @DsfServiceEventHandler
public void eventDispatched(final MIRunningEvent e) { public void eventDispatched(final MIRunningEvent e) {
if (fDisableRunningAndStoppedEvents.count > 0 && if (fDisableNextRunningEventDmc != null &&
fDisableRunningAndStoppedEvents.executionDmc.equals(e.getDMContext())) { fDisableNextRunningEventDmc.equals(e.getDMContext())) {
fDisableRunningAndStoppedEvents.count--; fDisableNextRunningEventDmc = null;
// Don't broadcast the running event // Don't broadcast the running event
return; return;
@ -1041,10 +1079,11 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
} }
} }
} }
if (fDisableRunningAndStoppedEvents.count > 0 && if (fDisableNextSignalEventDmc != null && e instanceof MISignalEvent &&
fDisableRunningAndStoppedEvents.executionDmc.equals(e.getDMContext())) { fDisableNextSignalEventDmc.equals(e.getDMContext())) {
fDisableRunningAndStoppedEvents.count--; fDisableNextSignalEventDmc = null;
fSilencedSignalEvent = e;
// Don't broadcast the stopped event // Don't broadcast the stopped event
return; return;

View file

@ -14,6 +14,7 @@ package org.eclipse.cdt.dsf.gdb.service;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.mi.service.IMIBackend; import org.eclipse.cdt.dsf.mi.service.IMIBackend;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IPath;
@ -101,6 +102,16 @@ public interface IGDBBackend extends IMIBackend {
*/ */
public void interrupt(); 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 * @return The type of the session currently ongoing with the backend
*/ */

View file

@ -324,10 +324,18 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
private StateChangeReason fStateChangeReason; private StateChangeReason fStateChangeReason;
private IExecutionDMContext fStateChangeTriggeringContext; private IExecutionDMContext fStateChangeTriggeringContext;
/** /**
* A counter of MIRunning/MIStopped events that should * Indicates that the next MIRunning event should be silenced.
* be kept silent.
*/ */
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; private static final int FAKE_THREAD_ID = 0;
@ -396,9 +404,9 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
*/ */
@DsfServiceEventHandler @DsfServiceEventHandler
public void eventDispatched(final MIRunningEvent e) { public void eventDispatched(final MIRunningEvent e) {
if (fDisableRunningAndStoppedEventsCount > 0) { if (fDisableNextRunningEvent) {
fDisableRunningAndStoppedEventsCount--; fDisableNextRunningEvent = false;
// We don't broadcast running events right now // We don't broadcast this running event
return; return;
} }
@ -421,9 +429,10 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
*/ */
@DsfServiceEventHandler @DsfServiceEventHandler
public void eventDispatched(final MIStoppedEvent e) { public void eventDispatched(final MIStoppedEvent e) {
if (fDisableRunningAndStoppedEventsCount > 0) { if (fDisableNextSignalEvent && e instanceof MISignalEvent) {
fDisableRunningAndStoppedEventsCount--; fDisableNextSignalEvent = false;
// We don't broadcast stopped events right now fSilencedSignalEvent = e;
// We don't broadcast this stopped event
return; 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. // 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; IExecutionDMContext triggeringCtx = !e.getDMContext().equals(containerDmc) ? e.getDMContext() : null;
if (triggeringCtx == 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 // We need a proper thread id for the debug view to select the right thread
// Bug 300096 comment #15 and Bug 302597 // Bug 300096 comment #15 and Bug 302597
getConnection().queueCommand( getConnection().queueCommand(
@ -934,10 +943,20 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
@Override @Override
public void execute(final RequestMonitor rm) { public void execute(final RequestMonitor rm) {
if (!isTargetAvailable()) { if (!isTargetAvailable()) {
// Don't broadcast the coming stopped/running events assert fDisableNextRunningEvent == false;
assert fDisableRunningAndStoppedEventsCount == 0; assert fDisableNextSignalEvent == false;
fDisableRunningAndStoppedEventsCount++;
suspend(getContextToSuspend(), rm); // 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 { } else {
rm.done(); rm.done();
} }
@ -951,14 +970,38 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
@Override @Override
public void execute(final RequestMonitor rm) { public void execute(final RequestMonitor rm) {
if (!isTargetAvailable()) { if (!isTargetAvailable()) {
assert fDisableRunningAndStoppedEventsCount == 0 || fDisableRunningAndStoppedEventsCount == 1; assert fDisableNextRunningEvent == false;
fDisableRunningAndStoppedEventsCount++; fDisableNextRunningEvent = true;
// Can't use the resume() call because we 'silently' stopped // Can't use the resume() call because we 'silently' stopped
// so resume() will not know we are actually stopped // so resume() will not know we are actually stopped
fConnection.queueCommand( fConnection.queueCommand(
fCommandFactory.createMIExecContinue(getContextToSuspend()), 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 { } else {
// We didn't suspend the container, so we don't need to resume it // We didn't suspend the container, so we don't need to resume it
rm.done(); rm.done();