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

[242943] Support for breakpoint operations while the program is running. JUnit tests included.

This commit is contained in:
Marc Khouzam 2010-02-22 21:30:56 +00:00
parent 38e0388107
commit 1d80af0b96
12 changed files with 1307 additions and 354 deletions

View file

@ -17,11 +17,13 @@ import java.util.Map;
import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Sequence.Step;
import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.IBreakpoints; import org.eclipse.cdt.dsf.debug.service.IBreakpoints;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; import org.eclipse.cdt.dsf.debug.service.command.ICommandControl;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.mi.service.IMIRunControl;
import org.eclipse.cdt.dsf.mi.service.MIBreakpointDMData; import org.eclipse.cdt.dsf.mi.service.MIBreakpointDMData;
import org.eclipse.cdt.dsf.mi.service.MIBreakpoints; import org.eclipse.cdt.dsf.mi.service.MIBreakpoints;
import org.eclipse.cdt.dsf.mi.service.command.commands.CLIPasscount; import org.eclipse.cdt.dsf.mi.service.command.commands.CLIPasscount;
@ -46,6 +48,7 @@ import org.eclipse.core.runtime.Status;
public class GDBBreakpoints_7_0 extends MIBreakpoints public class GDBBreakpoints_7_0 extends MIBreakpoints
{ {
private ICommandControl fConnection; private ICommandControl fConnection;
private IMIRunControl fRunControl;
public GDBBreakpoints_7_0(DsfSession session) { public GDBBreakpoints_7_0(DsfSession session) {
super(session); super(session);
@ -67,6 +70,7 @@ public class GDBBreakpoints_7_0 extends MIBreakpoints
private void doInitialize(final RequestMonitor rm) { private void doInitialize(final RequestMonitor rm) {
// Get the services references // Get the services references
fConnection = getServicesTracker().getService(ICommandControl.class); fConnection = getServicesTracker().getService(ICommandControl.class);
fRunControl = getServicesTracker().getService(IMIRunControl.class);
// Register this service // Register this service
register(new String[] { IBreakpoints.class.getName(), register(new String[] { IBreakpoints.class.getName(),
@ -91,78 +95,83 @@ public class GDBBreakpoints_7_0 extends MIBreakpoints
* *
* @param context * @param context
* @param breakpoint * @param breakpoint
* @param drm * @param finalRm
*/ */
@Override @Override
protected void addBreakpoint(final IBreakpointsTargetDMContext context, final Map<String, Object> attributes, final DataRequestMonitor<IBreakpointDMContext> drm) protected void addBreakpoint(final IBreakpointsTargetDMContext context, final Map<String, Object> attributes,
final DataRequestMonitor<IBreakpointDMContext> finalRm)
{ {
// Select the context breakpoints map // Select the context breakpoints map
final Map<Integer, MIBreakpointDMData> contextBreakpoints = getBreakpointMap(context); final Map<Integer, MIBreakpointDMData> contextBreakpoints = getBreakpointMap(context);
if (contextBreakpoints == null) { if (contextBreakpoints == null) {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); finalRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
drm.done(); finalRm.done();
return; return;
} }
// Extract the relevant parameters (providing default values to avoid potential NPEs) // Extract the relevant parameters (providing default values to avoid potential NPEs)
String location = formatLocation(attributes); final String location = formatLocation(attributes);
if (location.equals(NULL_STRING)) { if (location.equals(NULL_STRING)) {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); finalRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
drm.done(); finalRm.done();
return; return;
} }
Boolean enabled = (Boolean) getProperty(attributes, MIBreakpoints.IS_ENABLED, true); final Boolean enabled = (Boolean) getProperty(attributes, MIBreakpoints.IS_ENABLED, true);
Boolean isTemporary = (Boolean) getProperty(attributes, MIBreakpointDMData.IS_TEMPORARY, false); final Boolean isTemporary = (Boolean) getProperty(attributes, MIBreakpointDMData.IS_TEMPORARY, false);
Boolean isHardware = (Boolean) getProperty(attributes, MIBreakpointDMData.IS_HARDWARE, false); final Boolean isHardware = (Boolean) getProperty(attributes, MIBreakpointDMData.IS_HARDWARE, false);
String condition = (String) getProperty(attributes, MIBreakpoints.CONDITION, NULL_STRING); final String condition = (String) getProperty(attributes, MIBreakpoints.CONDITION, NULL_STRING);
Integer ignoreCount = (Integer) getProperty(attributes, MIBreakpoints.IGNORE_COUNT, 0); final Integer ignoreCount = (Integer) getProperty(attributes, MIBreakpoints.IGNORE_COUNT, 0);
String threadId = (String) getProperty(attributes, MIBreakpointDMData.THREAD_ID, "0"); //$NON-NLS-1$ String threadId = (String) getProperty(attributes, MIBreakpointDMData.THREAD_ID, "0"); //$NON-NLS-1$
int tid = Integer.parseInt(threadId); final int tid = Integer.parseInt(threadId);
// The DataRequestMonitor for the add request final Step insertBreakpointStep = new Step() {
DataRequestMonitor<MIBreakInsertInfo> addBreakpointDRM = @Override
new DataRequestMonitor<MIBreakInsertInfo>(getExecutor(), drm) { public void execute(final RequestMonitor rm) {
@Override // Execute the command
protected void handleSuccess() { fConnection.queueCommand(
new MIBreakInsert(context, isTemporary, isHardware, condition, ignoreCount, location, tid, !enabled, false),
new DataRequestMonitor<MIBreakInsertInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
// With MI, an invalid location won't generate an error // With MI, an invalid location won't generate an error
if (getData().getMIBreakpoints().length == 0) { if (getData().getMIBreakpoints().length == 0) {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null)); rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null));
drm.done(); rm.done();
return; return;
} }
// Create a breakpoint object and store it in the map // Create a breakpoint object and store it in the map
final MIBreakpointDMData newBreakpoint = new MIBreakpointDMData(getData().getMIBreakpoints()[0]); final MIBreakpointDMData newBreakpoint = new MIBreakpointDMData(getData().getMIBreakpoints()[0]);
int reference = newBreakpoint.getNumber(); int reference = newBreakpoint.getNumber();
if (reference == -1) { if (reference == -1) {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null)); rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null));
drm.done(); rm.done();
return; return;
} }
contextBreakpoints.put(reference, newBreakpoint); contextBreakpoints.put(reference, newBreakpoint);
// Format the return value // Format the return value
MIBreakpointDMContext dmc = new MIBreakpointDMContext(GDBBreakpoints_7_0.this, new IDMContext[] { context }, reference); MIBreakpointDMContext dmc = new MIBreakpointDMContext(GDBBreakpoints_7_0.this, new IDMContext[] { context }, reference);
drm.setData(dmc); finalRm.setData(dmc);
// Flag the event // Flag the event
getSession().dispatchEvent(new BreakpointAddedEvent(dmc), getProperties()); getSession().dispatchEvent(new BreakpointAddedEvent(dmc), getProperties());
drm.done(); rm.done();
} }
@Override @Override
protected void handleError() { protected void handleError() {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null)); rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null));
drm.done(); rm.done();
} }
}; });
}
// Execute the command };
fConnection.queueCommand(
new MIBreakInsert(context, isTemporary, isHardware, condition, ignoreCount, location, tid, !enabled, false), addBreakpointDRM); fRunControl.executeWithTargetAvailable(context, new Step[] { insertBreakpointStep }, finalRm);
} }
/** /**

View file

@ -362,8 +362,14 @@ public class GDBRunControl extends MIRunControl {
// Don't send the stop event since we are resuming again. // Don't send the stop event since we are resuming again.
return; return;
} else { } else {
// Stopped at another breakpoint or not a breakpoint at all. Just remove our temporary one // Stopped at another breakpoint that we should not skip.
// since we don't want it to hit later // Or got an interrupt signal from a suspend command.
// Or got an interrupt signal because the user set/changed a breakpoint. This last case is tricky.
// We could let the run-to-line continue its job, however, I'm thinking that if the user creates
// a new breakpoint, she may want to force the program to stop, in a way to abort the run-to-line.
// So, let's cancel the run-to-line in this case.
//
// Just remove our temporary one since we don't want it to hit later
IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(fRunToLineActiveOperation.getThreadContext(), IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(fRunToLineActiveOperation.getThreadContext(),
IBreakpointsTargetDMContext.class); IBreakpointsTargetDMContext.class);

View file

@ -575,8 +575,14 @@ public class GDBRunControl_7_0 extends MIRunControl implements IReverseRunContro
// Don't send the stop event since we are resuming again. // Don't send the stop event since we are resuming again.
return; return;
} else { } else {
// Stopped at another breakpoint. Just remove our temporary one // Stopped at another breakpoint that we should not skip.
// since we don't want it to hit later // Or got an interrupt signal from a suspend command.
// Or got an interrupt signal because the user set/changed a breakpoint. This last case is tricky.
// We could let the run-to-line continue its job, however, I'm thinking that if the user creates
// a new breakpoint, she may want to force the program to stop, in a way to abort the run-to-line.
// So, let's cancel the run-to-line in this case.
//
// Just remove our temporary one since we don't want it to hit later
IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(fRunToLineActiveOperation.getThreadContext(), IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(fRunToLineActiveOperation.getThreadContext(),
IBreakpointsTargetDMContext.class); IBreakpointsTargetDMContext.class);

View file

@ -15,15 +15,19 @@ package org.eclipse.cdt.dsf.gdb.service;
import java.util.HashMap; import java.util.HashMap;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Map; import java.util.Map;
import java.util.Vector;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Immutable; import org.eclipse.cdt.dsf.concurrent.Immutable;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Sequence;
import org.eclipse.cdt.dsf.concurrent.Sequence.Step;
import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent;
import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.DMContexts;
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.ICachingService; import org.eclipse.cdt.dsf.debug.service.ICachingService;
import org.eclipse.cdt.dsf.debug.service.IProcesses;
import org.eclipse.cdt.dsf.debug.service.IRunControl; import org.eclipse.cdt.dsf.debug.service.IRunControl;
import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext;
import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext;
@ -236,6 +240,15 @@ 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.
*/
private class DisableRunningAndStoppedEvents {
public IMIExecutionDMContext executionDmc = null;
public int count = 0;
};
private DisableRunningAndStoppedEvents fDisableRunningAndStoppedEvents = new DisableRunningAndStoppedEvents();
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Initialization and shutdown // Initialization and shutdown
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -293,15 +306,14 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
return (threadState == null) ? false : !fTerminated && threadState.fSuspended; return (threadState == null) ? false : !fTerminated && threadState.fSuspended;
} }
// Container case // Container case. The container is considered suspended as long
// as one of its thread is suspended
if (context instanceof IContainerDMContext) { if (context instanceof IContainerDMContext) {
boolean isSuspended = false;
for (IMIExecutionDMContext threadContext : fThreadRunStates.keySet()) { for (IMIExecutionDMContext threadContext : fThreadRunStates.keySet()) {
if (DMContexts.isAncestorOf(threadContext, context)) { if (DMContexts.isAncestorOf(threadContext, context)) {
isSuspended |= isSuspended(threadContext); if (isSuspended(threadContext)) return true;
} }
} }
return isSuspended;
} }
// Default case // Default case
@ -792,6 +804,142 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
threadState.fStateChangeReason = reason; threadState.fStateChangeReason = reason;
} }
/**
* @since 3.0
*/
public void executeWithTargetAvailable(IDMContext ctx, Sequence.Step[] steps, RequestMonitor rm) {
Vector<Step> totalStepsVector = new Vector<Step>();
totalStepsVector.add(new IsTargetAvailableStep(ctx));
totalStepsVector.add(new MakeTargetAvailableStep());
for (Step step : steps) {
totalStepsVector.add(step);
}
totalStepsVector.add(new RestoreTargetStateStep());
final Step[] totalSteps = totalStepsVector.toArray(new Step[totalStepsVector.size()]);
getExecutor().execute(new Sequence(getExecutor(), rm) {
@Override public Step[] getSteps() { return totalSteps; }
});
}
/* ******************************************************************************
* Section to support making operations even when the target is unavailable.
*
* Although one would expect to be able to make commands all the time when
* in non-stop mode, it turns out that GDB has trouble with some commands
* like breakpoints. The safe way to do it is to make sure we have at least
* one thread suspended.
*
* Basically, we must make sure one container is suspended before making
* certain operations (currently breakpoints). If that is not the case, we must
* first suspend one thread, then perform the specified operations,
* and finally resume that thread..
* See https://bugs.eclipse.org/bugs/show_bug.cgi?id=242943
* and https://bugs.eclipse.org/bugs/show_bug.cgi?id=282273
*
* ******************************************************************************/
private IContainerDMContext fContainerDmcToSuspend = null;
private IMIExecutionDMContext fExecutionDmcToSuspend = null;
private boolean fTargetAvailable = false;
/**
* @since 3.0
*/
protected class IsTargetAvailableStep extends Sequence.Step {
final IDMContext fCtx;
public IsTargetAvailableStep(IDMContext ctx) {
fCtx = ctx;
}
@Override
public void execute(final RequestMonitor rm) {
IProcesses processControl = getServicesTracker().getService(IProcesses.class);
processControl.getProcessesBeingDebugged(
fCtx,
new DataRequestMonitor<IDMContext[]>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
assert getData() != null;
if (getData().length == 0) {
// Happens at startup, starting with GDB 7.0
// This means the target is available
fTargetAvailable = true;
} else {
fTargetAvailable = false;
// Choose the first process as the one to suspend if needed
fContainerDmcToSuspend = (IContainerDMContext)(getData()[0]);
for (IDMContext containerDmc : getData()) {
if (isSuspended((IContainerDMContext)containerDmc)) {
fTargetAvailable = true;
break;
}
}
}
rm.done();
}
});
}
};
/**
* @since 3.0
*/
protected class MakeTargetAvailableStep extends Sequence.Step {
@Override
public void execute(final RequestMonitor rm) {
if (!fTargetAvailable) {
// Instead of suspending the entire process, let's find its first thread and use that
IProcesses processControl = getServicesTracker().getService(IProcesses.class);
processControl.getProcessesBeingDebugged(
fContainerDmcToSuspend,
new DataRequestMonitor<IDMContext[]>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
assert getData() != null;
assert getData().length > 0;
fExecutionDmcToSuspend = (IMIExecutionDMContext)getData()[0];
// Don't broadcast the coming stopped/running events
assert fDisableRunningAndStoppedEvents.count == 0;
fDisableRunningAndStoppedEvents.count++;
fDisableRunningAndStoppedEvents.executionDmc = fExecutionDmcToSuspend;
suspend(fExecutionDmcToSuspend, rm);
}
});
} else {
rm.done();
}
}
};
/**
* @since 3.0
*/
protected class RestoreTargetStateStep extends Sequence.Step {
@Override
public void execute(final RequestMonitor rm) {
if (!fTargetAvailable) {
assert fDisableRunningAndStoppedEvents.count == 0 || fDisableRunningAndStoppedEvents.count == 1;
fDisableRunningAndStoppedEvents.count++;
fDisableRunningAndStoppedEvents.executionDmc = fExecutionDmcToSuspend;
// Can't use the resume() call because we 'silently' stopped
// so resume() will not know we are actually stopped
fConnection.queueCommand(
new MIExecContinue(fExecutionDmcToSuspend),
new DataRequestMonitor<MIInfo>(getExecutor(), rm));
} else {
// We didn't suspend the thread, so we don't need to resume it
rm.done();
}
}
};
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Event handlers // Event handlers
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -802,6 +950,14 @@ 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 &&
fDisableRunningAndStoppedEvents.executionDmc.equals(e.getDMContext())) {
fDisableRunningAndStoppedEvents.count--;
// Don't broadcast the running event
return;
}
getSession().dispatchEvent(new ResumedEvent(e.getDMContext(), e), getProperties()); getSession().dispatchEvent(new ResumedEvent(e.getDMContext(), e), getProperties());
} }
@ -849,6 +1005,10 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
} else { } else {
// Stopped for any other reasons. Just remove our temporary one // Stopped for any other reasons. Just remove our temporary one
// since we don't want it to hit later // since we don't want it to hit later
//
// Note that in Non-stop, we don't cancel a run-to-line when a new
// breakpoint is inserted. This is because the new breakpoint could
// be for another thread altogether and should not affect the current thread.
IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(fRunToLineActiveOperation.getThreadContext(), IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(fRunToLineActiveOperation.getThreadContext(),
IBreakpointsTargetDMContext.class); IBreakpointsTargetDMContext.class);
@ -859,6 +1019,14 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
} }
} }
} }
if (fDisableRunningAndStoppedEvents.count > 0 &&
fDisableRunningAndStoppedEvents.executionDmc.equals(e.getDMContext())) {
fDisableRunningAndStoppedEvents.count--;
// Don't broadcast the stopped event
return;
}
getSession().dispatchEvent(new SuspendedEvent(e.getDMContext(), e), getProperties()); getSession().dispatchEvent(new SuspendedEvent(e.getDMContext(), e), getProperties());
} }

View file

@ -11,6 +11,8 @@
package org.eclipse.cdt.dsf.mi.service; package org.eclipse.cdt.dsf.mi.service;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Sequence;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl; import org.eclipse.cdt.dsf.debug.service.IRunControl;
/** /**
@ -40,5 +42,18 @@ public interface IMIRunControl extends IRunControl
*/ */
void resumeAtLocation(IExecutionDMContext context, String location, RequestMonitor rm); void resumeAtLocation(IExecutionDMContext context, String location, RequestMonitor rm);
/**
* Request that the specified steps be executed by first ensuring the target is available
* to receive commands. Once the specified steps are executed, the target should be
* returned to its original availability.
*
* This can is of value for breakpoints commands; e.g., breakpoints need to be inserted
* even when the target is running, so this call would suspend the target, insert the
* breakpoint, and resume the target again.
*
* @since 3.0
*/
public void executeWithTargetAvailable(IDMContext ctx, Sequence.Step[] stepsToExecute, RequestMonitor rm);
} }

View file

@ -19,11 +19,17 @@ import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Immutable; import org.eclipse.cdt.dsf.concurrent.Immutable;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Sequence;
import org.eclipse.cdt.dsf.concurrent.Sequence.Step;
import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; import org.eclipse.cdt.dsf.datamodel.AbstractDMContext;
import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent;
import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.IBreakpoints; import org.eclipse.cdt.dsf.debug.service.IBreakpoints;
import org.eclipse.cdt.dsf.debug.service.IProcesses;
import org.eclipse.cdt.dsf.debug.service.IRunControl;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; import org.eclipse.cdt.dsf.debug.service.command.ICommandControl;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
@ -88,7 +94,8 @@ public class MIBreakpoints extends AbstractDsfService implements IBreakpoints
public static final String WRITE = PREFIX + ".write"; //$NON-NLS-1$ public static final String WRITE = PREFIX + ".write"; //$NON-NLS-1$
// Services // Services
ICommandControl fConnection; private ICommandControl fConnection;
private IMIRunControl fRunControl;
// Service breakpoints tracking // Service breakpoints tracking
// The breakpoints are stored per context and keyed on the back-end breakpoint reference // The breakpoints are stored per context and keyed on the back-end breakpoint reference
@ -260,6 +267,7 @@ public class MIBreakpoints extends AbstractDsfService implements IBreakpoints
// Get the services references // Get the services references
fConnection = getServicesTracker().getService(ICommandControl.class); fConnection = getServicesTracker().getService(ICommandControl.class);
fRunControl = getServicesTracker().getService(IMIRunControl.class);
// Register for the useful events // Register for the useful events
getSession().addServiceEventListener(this, null); getSession().addServiceEventListener(this, null);
@ -522,82 +530,85 @@ public class MIBreakpoints extends AbstractDsfService implements IBreakpoints
* *
* @param context * @param context
* @param breakpoint * @param breakpoint
* @param drm * @param finalRm
* *
* @since 3.0 * @since 3.0
*/ */
protected void addBreakpoint(final IBreakpointsTargetDMContext context, final Map<String, Object> attributes, final DataRequestMonitor<IBreakpointDMContext> drm) protected void addBreakpoint(final IBreakpointsTargetDMContext context, final Map<String, Object> attributes, final DataRequestMonitor<IBreakpointDMContext> finalRm)
{ {
// Select the context breakpoints map // Select the context breakpoints map
final Map<Integer, MIBreakpointDMData> contextBreakpoints = getBreakpointMap(context); final Map<Integer, MIBreakpointDMData> contextBreakpoints = getBreakpointMap(context);
if (contextBreakpoints == null) { if (contextBreakpoints == null) {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); finalRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
drm.done(); finalRm.done();
return; return;
} }
// Extract the relevant parameters (providing default values to avoid potential NPEs) // Extract the relevant parameters (providing default values to avoid potential NPEs)
String location = formatLocation(attributes); final String location = formatLocation(attributes);
if (location.equals(NULL_STRING)) { if (location.equals(NULL_STRING)) {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); finalRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
drm.done(); finalRm.done();
return; return;
} }
Boolean isTemporary = (Boolean) getProperty(attributes, MIBreakpointDMData.IS_TEMPORARY, false); final Boolean isTemporary = (Boolean) getProperty(attributes, MIBreakpointDMData.IS_TEMPORARY, false);
Boolean isHardware = (Boolean) getProperty(attributes, MIBreakpointDMData.IS_HARDWARE, false); final Boolean isHardware = (Boolean) getProperty(attributes, MIBreakpointDMData.IS_HARDWARE, false);
final String condition = (String) getProperty(attributes, CONDITION, NULL_STRING); final String condition = (String) getProperty(attributes, CONDITION, NULL_STRING);
Integer ignoreCount = (Integer) getProperty(attributes, IGNORE_COUNT, 0 ); final Integer ignoreCount = (Integer) getProperty(attributes, IGNORE_COUNT, 0 );
String threadId = (String) getProperty(attributes, MIBreakpointDMData.THREAD_ID, "0"); //$NON-NLS-1$ final String threadId = (String) getProperty(attributes, MIBreakpointDMData.THREAD_ID, "0"); //$NON-NLS-1$
int tid = Integer.parseInt(threadId); final int tid = Integer.parseInt(threadId);
// The DataRequestMonitor for the add request final Step insertBreakpointStep = new Step() {
DataRequestMonitor<MIBreakInsertInfo> addBreakpointDRM = @Override
new DataRequestMonitor<MIBreakInsertInfo>(getExecutor(), drm) { public void execute(final RequestMonitor rm) {
@Override fConnection.queueCommand(
protected void handleSuccess() { new MIBreakInsert(context, isTemporary, isHardware, condition, ignoreCount, location, tid),
new DataRequestMonitor<MIBreakInsertInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
// With MI, an invalid location won't generate an error // With MI, an invalid location won't generate an error
if (getData().getMIBreakpoints().length == 0) { if (getData().getMIBreakpoints().length == 0) {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null)); rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null));
drm.done(); rm.done();
return; return;
} }
// Create a breakpoint object and store it in the map // Create a breakpoint object and store it in the map
final MIBreakpointDMData newBreakpoint = new MIBreakpointDMData(getData().getMIBreakpoints()[0]); final MIBreakpointDMData newBreakpoint = new MIBreakpointDMData(getData().getMIBreakpoints()[0]);
int reference = newBreakpoint.getNumber(); int reference = newBreakpoint.getNumber();
if (reference == -1) { if (reference == -1) {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null)); rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null));
drm.done(); rm.done();
return; return;
} }
contextBreakpoints.put(reference, newBreakpoint); contextBreakpoints.put(reference, newBreakpoint);
// Format the return value // Format the return value
MIBreakpointDMContext dmc = new MIBreakpointDMContext(MIBreakpoints.this, new IDMContext[] { context }, reference); MIBreakpointDMContext dmc = new MIBreakpointDMContext(MIBreakpoints.this, new IDMContext[] { context }, reference);
drm.setData(dmc); finalRm.setData(dmc);
// Flag the event // Flag the event
getSession().dispatchEvent(new BreakpointAddedEvent(dmc), getProperties()); getSession().dispatchEvent(new BreakpointAddedEvent(dmc), getProperties());
// By default the breakpoint is enabled at creation // By default the breakpoint is enabled at creation
// If it wasn't supposed to be, then disable it right away // If it wasn't supposed to be, then disable it right away
Map<String,Object> delta = new HashMap<String,Object>(); Map<String,Object> delta = new HashMap<String,Object>();
delta.put(IS_ENABLED, getProperty(attributes, IS_ENABLED, true)); delta.put(IS_ENABLED, getProperty(attributes, IS_ENABLED, true));
modifyBreakpoint(dmc, delta, drm, false); modifyBreakpoint(dmc, delta, rm, false);
} }
@Override @Override
protected void handleError() { protected void handleError() {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null)); rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null));
drm.done(); rm.done();
} }
});
}
}; };
// Execute the command fRunControl.executeWithTargetAvailable(context, new Step[] { insertBreakpointStep }, finalRm);
fConnection.queueCommand(
new MIBreakInsert(context, isTemporary, isHardware, condition, ignoreCount, location, tid), addBreakpointDRM);
} }
/** /**
@ -696,12 +707,12 @@ public class MIBreakpoints extends AbstractDsfService implements IBreakpoints
/* (non-Javadoc) /* (non-Javadoc)
* @see org.eclipse.cdt.dsf.debug.service.IBreakpoints#removeBreakpoint(org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext, org.eclipse.cdt.dsf.concurrent.RequestMonitor) * @see org.eclipse.cdt.dsf.debug.service.IBreakpoints#removeBreakpoint(org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext, org.eclipse.cdt.dsf.concurrent.RequestMonitor)
*/ */
public void removeBreakpoint(final IBreakpointDMContext dmc, final RequestMonitor rm) { public void removeBreakpoint(final IBreakpointDMContext dmc, final RequestMonitor finalRm) {
// Validate the breakpoint context // Validate the breakpoint context
if (dmc == null) { if (dmc == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); finalRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
rm.done(); finalRm.done();
return; return;
} }
@ -711,24 +722,24 @@ public class MIBreakpoints extends AbstractDsfService implements IBreakpoints
breakpointCtx = (MIBreakpointDMContext) dmc; breakpointCtx = (MIBreakpointDMContext) dmc;
} }
else { else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_TYPE, null)); finalRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_TYPE, null));
rm.done(); finalRm.done();
return; return;
} }
// Validate the target context // Validate the target context
IBreakpointsTargetDMContext context = DMContexts.getAncestorOfType(dmc, IBreakpointsTargetDMContext.class); final IBreakpointsTargetDMContext context = DMContexts.getAncestorOfType(dmc, IBreakpointsTargetDMContext.class);
if (context == null) { if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_EXECUTION_CONTEXT, null)); finalRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_EXECUTION_CONTEXT, null));
rm.done(); finalRm.done();
return; return;
} }
// Pick the context breakpoints map // Pick the context breakpoints map
final Map<Integer,MIBreakpointDMData> contextBreakpoints = getBreakpointMap(context); final Map<Integer,MIBreakpointDMData> contextBreakpoints = getBreakpointMap(context);
if (contextBreakpoints == null) { if (contextBreakpoints == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); finalRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
rm.done(); finalRm.done();
return; return;
} }
@ -736,24 +747,31 @@ public class MIBreakpoints extends AbstractDsfService implements IBreakpoints
final int reference = breakpointCtx.getReference(); final int reference = breakpointCtx.getReference();
MIBreakpointDMData breakpoint = contextBreakpoints.get(reference); MIBreakpointDMData breakpoint = contextBreakpoints.get(reference);
if (breakpoint == null) { if (breakpoint == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); finalRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
rm.done(); finalRm.done();
return; return;
} }
// Queue the command final Step deleteBreakpointStep = new Step() {
fConnection.queueCommand( @Override
new MIBreakDelete(context, new int[] { reference }), public void execute(final RequestMonitor rm) {
new DataRequestMonitor<MIInfo>(getExecutor(), rm) { // Queue the command
@Override fConnection.queueCommand(
protected void handleCompleted() { new MIBreakDelete(context, new int[] { reference }),
if (isSuccess()) { new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
getSession().dispatchEvent(new BreakpointRemovedEvent(dmc), getProperties()); @Override
contextBreakpoints.remove(reference); protected void handleCompleted() {
} if (isSuccess()) {
rm.done(); getSession().dispatchEvent(new BreakpointRemovedEvent(dmc), getProperties());
} contextBreakpoints.remove(reference);
}); }
rm.done();
}
});
}
};
fRunControl.executeWithTargetAvailable(context, new Step[] { deleteBreakpointStep }, finalRm);
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
@ -895,64 +913,71 @@ public class MIBreakpoints extends AbstractDsfService implements IBreakpoints
* @param context * @param context
* @param dmc * @param dmc
* @param condition * @param condition
* @param rm * @param finalRm
* *
* @since 3.0 * @since 3.0
*/ */
protected void changeCondition(final IBreakpointsTargetDMContext context, protected void changeCondition(final IBreakpointsTargetDMContext context,
final int reference, final String condition, final RequestMonitor rm) final int reference, final String condition, final RequestMonitor finalRm)
{ {
// Pick the context breakpoints map // Pick the context breakpoints map
final Map<Integer, MIBreakpointDMData> contextBreakpoints = getBreakpointMap(context); final Map<Integer, MIBreakpointDMData> contextBreakpoints = getBreakpointMap(context);
if (contextBreakpoints == null) { if (contextBreakpoints == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); finalRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
rm.done(); finalRm.done();
return; return;
} }
// Queue the command final Step changeConditionStep = new Step() {
fConnection.queueCommand( @Override
new MIBreakCondition(context, reference, condition), public void execute(final RequestMonitor rm) {
new DataRequestMonitor<MIInfo>(getExecutor(), rm) { // Queue the command
@Override fConnection.queueCommand(
protected void handleSuccess() { new MIBreakCondition(context, reference, condition),
MIBreakpointDMData breakpoint = contextBreakpoints.get(reference); new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
if (breakpoint == null) { @Override
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); protected void handleSuccess() {
rm.done(); MIBreakpointDMData breakpoint = contextBreakpoints.get(reference);
return; if (breakpoint == null) {
} rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
breakpoint.setCondition(condition); rm.done();
rm.done(); return;
} }
breakpoint.setCondition(condition);
rm.done();
}
// In case of error (new condition could not be installed for whatever reason), // In case of error (new condition could not be installed for whatever reason),
// GDB "offers" different behaviours depending on its version: it can either keep // GDB "offers" different behaviours depending on its version: it can either keep
// the original condition (the right thing to do) or keep the invalid condition. // the original condition (the right thing to do) or keep the invalid condition.
// Our sole option is to remove the condition in case of error and rely on the // Our sole option is to remove the condition in case of error and rely on the
// upper layer to re-install the right condition. // upper layer to re-install the right condition.
@Override @Override
protected void handleError() { protected void handleError() {
MIBreakpointDMData breakpoint = contextBreakpoints.get(reference); MIBreakpointDMData breakpoint = contextBreakpoints.get(reference);
if (breakpoint == null) { if (breakpoint == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
rm.done(); rm.done();
return; return;
} }
// Remove invalid condition from the back-end breakpoint // Remove invalid condition from the back-end breakpoint
breakpoint.setCondition(NULL_STRING); breakpoint.setCondition(NULL_STRING);
fConnection.queueCommand( fConnection.queueCommand(
new MIBreakCondition(context, reference, NULL_STRING), new MIBreakCondition(context, reference, NULL_STRING),
new DataRequestMonitor<MIInfo>(getExecutor(), rm) { new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
@Override @Override
// The report the initial problem // The report the initial problem
protected void handleCompleted() { protected void handleCompleted() {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, INVALID_CONDITION, null)); rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, INVALID_CONDITION, null));
rm.done(); rm.done();
} }
}); });
} }
}); });
}
};
fRunControl.executeWithTargetAvailable(context, new Step[] { changeConditionStep }, finalRm);
} }
@ -962,37 +987,44 @@ public class MIBreakpoints extends AbstractDsfService implements IBreakpoints
* @param context * @param context
* @param reference * @param reference
* @param ignoreCount * @param ignoreCount
* @param rm * @param finalRm
* *
* @since 3.0 * @since 3.0
*/ */
protected void changeIgnoreCount(IBreakpointsTargetDMContext context, protected void changeIgnoreCount(final IBreakpointsTargetDMContext context,
final int reference, final int ignoreCount, final RequestMonitor rm) final int reference, final int ignoreCount, final RequestMonitor finalRm)
{ {
// Pick the context breakpoints map // Pick the context breakpoints map
final Map<Integer, MIBreakpointDMData> contextBreakpoints = getBreakpointMap(context); final Map<Integer, MIBreakpointDMData> contextBreakpoints = getBreakpointMap(context);
if (contextBreakpoints == null) { if (contextBreakpoints == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); finalRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
rm.done(); finalRm.done();
return; return;
} }
// Queue the command final Step changeIgnoreCountStep = new Step() {
fConnection.queueCommand( @Override
new MIBreakAfter(context, reference, ignoreCount), public void execute(final RequestMonitor rm) {
new DataRequestMonitor<MIInfo>(getExecutor(), rm) { // Queue the command
@Override fConnection.queueCommand(
protected void handleSuccess() { new MIBreakAfter(context, reference, ignoreCount),
MIBreakpointDMData breakpoint = contextBreakpoints.get(reference); new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
if (breakpoint == null) { @Override
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); protected void handleSuccess() {
rm.done(); MIBreakpointDMData breakpoint = contextBreakpoints.get(reference);
return; if (breakpoint == null) {
} rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
breakpoint.setIgnoreCount(ignoreCount); rm.done();
rm.done(); return;
} }
}); breakpoint.setIgnoreCount(ignoreCount);
rm.done();
}
});
}
};
fRunControl.executeWithTargetAvailable(context, new Step[] { changeIgnoreCountStep }, finalRm);
} }
/** /**
@ -1000,37 +1032,44 @@ public class MIBreakpoints extends AbstractDsfService implements IBreakpoints
* *
* @param context * @param context
* @param reference * @param reference
* @param rm * @param finalRm
* *
* @since 3.0 * @since 3.0
*/ */
protected void enableBreakpoint(IBreakpointsTargetDMContext context, protected void enableBreakpoint(final IBreakpointsTargetDMContext context,
final int reference, final RequestMonitor rm) final int reference, final RequestMonitor finalRm)
{ {
// Pick the context breakpoints map // Pick the context breakpoints map
final Map<Integer, MIBreakpointDMData> contextBreakpoints = getBreakpointMap(context); final Map<Integer, MIBreakpointDMData> contextBreakpoints = getBreakpointMap(context);
if (contextBreakpoints == null) { if (contextBreakpoints == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); finalRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
rm.done(); finalRm.done();
return; return;
} }
// Queue the command final Step enableBreakpointStep = new Step() {
fConnection.queueCommand( @Override
new MIBreakEnable(context, new int[] { reference }), public void execute(final RequestMonitor rm) {
new DataRequestMonitor<MIInfo>(getExecutor(), rm) { // Queue the command
@Override fConnection.queueCommand(
protected void handleSuccess() { new MIBreakEnable(context, new int[] { reference }),
MIBreakpointDMData breakpoint = contextBreakpoints.get(reference); new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
if (breakpoint == null) { @Override
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); protected void handleSuccess() {
rm.done(); MIBreakpointDMData breakpoint = contextBreakpoints.get(reference);
return; if (breakpoint == null) {
} rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
breakpoint.setEnabled(true); rm.done();
rm.done(); return;
} }
}); breakpoint.setEnabled(true);
rm.done();
}
});
}
};
fRunControl.executeWithTargetAvailable(context, new Step[] { enableBreakpointStep }, finalRm);
} }
/** /**
@ -1038,37 +1077,168 @@ public class MIBreakpoints extends AbstractDsfService implements IBreakpoints
* *
* @param context * @param context
* @param dmc * @param dmc
* @param rm * @param finalRm
* *
* @since 3.0 * @since 3.0
*/ */
protected void disableBreakpoint(IBreakpointsTargetDMContext context, protected void disableBreakpoint(final IBreakpointsTargetDMContext context,
final int reference, final RequestMonitor rm) final int reference, final RequestMonitor finalRm)
{ {
// Pick the context breakpoints map // Pick the context breakpoints map
final Map<Integer, MIBreakpointDMData> contextBreakpoints = getBreakpointMap(context); final Map<Integer, MIBreakpointDMData> contextBreakpoints = getBreakpointMap(context);
if (contextBreakpoints == null) { if (contextBreakpoints == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); finalRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
rm.done(); finalRm.done();
return; return;
} }
// Queue the command final Step disableBreakpointStep = new Step() {
fConnection.queueCommand( @Override
new MIBreakDisable(context, new int[] { reference }), public void execute(final RequestMonitor rm) {
new DataRequestMonitor<MIInfo>(getExecutor(), rm) { // Queue the command
@Override fConnection.queueCommand(
protected void handleSuccess() { new MIBreakDisable(context, new int[] { reference }),
MIBreakpointDMData breakpoint = contextBreakpoints.get(reference); new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
if (breakpoint == null) { @Override
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); protected void handleSuccess() {
rm.done(); MIBreakpointDMData breakpoint = contextBreakpoints.get(reference);
return; if (breakpoint == null) {
} rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
breakpoint.setEnabled(false); rm.done();
rm.done(); return;
} }
}); breakpoint.setEnabled(false);
rm.done();
}
});
}
};
fRunControl.executeWithTargetAvailable(context, new Step[] { disableBreakpointStep }, finalRm);
} }
/* ******************************************************************************
* Section to support making breakpoint operations while the target is running.
*
* Basically, we must make sure we have one thread suspended before making
* a breakpoint operation. If we don't we must suspend a thread, do the
* breakpoint operation and then resume the thread.
* See https://bugs.eclipse.org/bugs/show_bug.cgi?id=242943
* and https://bugs.eclipse.org/bugs/show_bug.cgi?id=282273
*
* ******************************************************************************/
/** The top level context describing our target **/
private IDMContext fTargetDmc = null;
/** The container context used to determine if the target is suspended **/
private IContainerDMContext fContainerDmc = null;
/** Must we suspend the target **/
private boolean fMustSuspend = false;
/**
* Returns the context of our target. It will be used to find the container we need
* to use to check to see if the target is suspended.
* @since 3.0
*/
protected IDMContext getTargetDmc() {
return fTargetDmc;
}
/**
* Returns the container context that should be used to determine if target is suspended.
* This container will also be the one suspended if needed.
* @since 3.0
*/
protected IContainerDMContext getContainerDmc() {
return fContainerDmc;
}
/**
* Returns whether the target must be suspended before performing the breakpoint operation
* @since 3.0
*/
protected boolean getMustSuspended() {
return fMustSuspend;
}
/**
* Returns whether the target must be suspended before performing the breakpoint operation
* @since 3.0
*/
protected IExecutionDMContext getContextToSuspend() {
return fContainerDmc;
}
/**
* Find the context for the container we should use to know
* if the target is already suspended.
* @since 3.0
*/
protected class FindContainerDmcStep extends Sequence.Step {
@Override
public void execute(final RequestMonitor rm) {
IProcesses processControl = getServicesTracker().getService(IProcesses.class);
processControl.getProcessesBeingDebugged(
getTargetDmc(),
new DataRequestMonitor<IDMContext[]>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
if (getData() != null && getData().length != 0) {
fContainerDmc = (IContainerDMContext)(getData()[0]);
}
// else Not debugging any process yet, so no need to suspend target
rm.done();
}
});
}
};
/**
* Check if the container returned by getContainerDmc() is suspended.
* If it is, we can do breakpoint operations. If it is not, we must first
* suspend it, and then we'll have to resume it.
*
* @since 3.0
*/
protected class IsContainerSuspendedStep extends Sequence.Step {
@Override
public void execute(RequestMonitor rm) {
IRunControl runControl = getServicesTracker().getService(IRunControl.class);
fMustSuspend = !runControl.isSuspended(getContainerDmc());
rm.done();
}
};
/**
* If needed, suspend the target that is returned by getContextToSuspend().
* @since 3.0
*/
protected class SuspendTargetStep extends Sequence.Step {
@Override
public void execute(final RequestMonitor rm) {
if (getMustSuspended()) {
IRunControl runControl = getServicesTracker().getService(IRunControl.class);
runControl.suspend(getContextToSuspend(), rm);
} else {
rm.done();
}
}
};
/**
* If needed, resume the thread that is returned by getThreadToSuspend().
* @since 3.0
*/
protected class ResumeThreadStep extends Sequence.Step {
@Override
public void execute(final RequestMonitor rm) {
if (getMustSuspended()) {
IRunControl runControl = getServicesTracker().getService(IRunControl.class);
runControl.resume(getContextToSuspend(), rm);
} else {
// We didn't suspend the thread, so we don't need to resume it
rm.done();
}
}
};
} }

View file

@ -11,15 +11,20 @@
*******************************************************************************/ *******************************************************************************/
package org.eclipse.cdt.dsf.mi.service; package org.eclipse.cdt.dsf.mi.service;
import java.util.Vector;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Immutable; import org.eclipse.cdt.dsf.concurrent.Immutable;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Sequence;
import org.eclipse.cdt.dsf.concurrent.Sequence.Step;
import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; import org.eclipse.cdt.dsf.datamodel.AbstractDMContext;
import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent;
import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.DMContexts;
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.ICachingService; import org.eclipse.cdt.dsf.debug.service.ICachingService;
import org.eclipse.cdt.dsf.debug.service.IProcesses;
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
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;
@ -271,6 +276,11 @@ 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
* be kept silent.
*/
private int fDisableRunningAndStoppedEventsCount = 0;
private static final int FAKE_THREAD_ID = 0; private static final int FAKE_THREAD_ID = 0;
@ -338,6 +348,12 @@ 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) {
fDisableRunningAndStoppedEventsCount--;
// We don't broadcast running events right now
return;
}
IDMEvent<?> event = null; IDMEvent<?> event = null;
// Find the container context, which is used in multi-threaded debugging. // Find the container context, which is used in multi-threaded debugging.
IContainerDMContext containerDmc = DMContexts.getAncestorOfType(e.getDMContext(), IContainerDMContext.class); IContainerDMContext containerDmc = DMContexts.getAncestorOfType(e.getDMContext(), IContainerDMContext.class);
@ -357,7 +373,13 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
*/ */
@DsfServiceEventHandler @DsfServiceEventHandler
public void eventDispatched(final MIStoppedEvent e) { public void eventDispatched(final MIStoppedEvent e) {
IDMEvent<?> event = null; if (fDisableRunningAndStoppedEventsCount > 0) {
fDisableRunningAndStoppedEventsCount--;
// We don't broadcast stopped events right now
return;
}
IDMEvent<?> event = null;
// Find the container context, which is used in multi-threaded debugging. // Find the container context, which is used in multi-threaded debugging.
IContainerDMContext containerDmc = DMContexts.getAncestorOfType(e.getDMContext(), IContainerDMContext.class); IContainerDMContext containerDmc = DMContexts.getAncestorOfType(e.getDMContext(), IContainerDMContext.class);
if (containerDmc != null) { if (containerDmc != null) {
@ -738,6 +760,127 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
rm.done(); rm.done();
} }
} }
/**
* @since 3.0
*/
public void executeWithTargetAvailable(IDMContext ctx, Sequence.Step[] steps, RequestMonitor rm) {
Vector<Step> totalStepsVector = new Vector<Step>();
totalStepsVector.add(new IsTargetAvailableStep(ctx));
totalStepsVector.add(new MakeTargetAvailableStep());
for (Step step : steps) {
totalStepsVector.add(step);
}
totalStepsVector.add(new RestoreTargetStateStep());
final Step[] totalSteps = totalStepsVector.toArray(new Step[totalStepsVector.size()]);
getExecutor().execute(new Sequence(getExecutor(), rm) {
@Override public Step[] getSteps() { return totalSteps; }
});
}
/* ******************************************************************************
* Section to support making operations even when the target is unavailable.
*
* Basically, we must make sure the container is suspended before making
* certain operations (currently breakpoints). If we don't, we must first
* suspend the container, then perform the specified operations,
* and finally resume the container.
* See https://bugs.eclipse.org/bugs/show_bug.cgi?id=242943
* and https://bugs.eclipse.org/bugs/show_bug.cgi?id=282273
*
* ******************************************************************************/
private IContainerDMContext fContainerDmc = null;
private boolean fTargetAvailable = false;
/**
* Returns whether the target is available to perform operations
* @since 3.0
*/
protected boolean isTargetAvailable() {
return fTargetAvailable;
}
/**
* Returns whether the target must be suspended before performing the breakpoint operation
* @since 3.0
*/
protected IExecutionDMContext getContextToSuspend() {
return fContainerDmc;
}
/**
* @since 3.0
*/
protected class IsTargetAvailableStep extends Sequence.Step {
final IDMContext fCtx;
public IsTargetAvailableStep(IDMContext ctx) {
fCtx = ctx;
}
@Override
public void execute(final RequestMonitor rm) {
IProcesses processControl = getServicesTracker().getService(IProcesses.class);
processControl.getProcessesBeingDebugged(
fCtx,
new DataRequestMonitor<IDMContext[]>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
assert getData() != null;
if (getData().length == 0) {
// Happens at startup, starting with GDB 7.0
// This means the target is available
fTargetAvailable = true;
} else {
fContainerDmc = (IContainerDMContext)(getData()[0]);
fTargetAvailable = isSuspended(fContainerDmc);
}
rm.done();
}
});
}
};
/**
* @since 3.0
*/
protected class MakeTargetAvailableStep extends Sequence.Step {
@Override
public void execute(final RequestMonitor rm) {
if (!isTargetAvailable()) {
// Don't broadcast the coming stopped/running events
assert fDisableRunningAndStoppedEventsCount == 0;
fDisableRunningAndStoppedEventsCount++;
suspend(getContextToSuspend(), rm);
} else {
rm.done();
}
}
};
/**
* @since 3.0
*/
protected class RestoreTargetStateStep extends Sequence.Step {
@Override
public void execute(final RequestMonitor rm) {
if (!isTargetAvailable()) {
assert fDisableRunningAndStoppedEventsCount == 0 || fDisableRunningAndStoppedEventsCount == 1;
fDisableRunningAndStoppedEventsCount++;
// Can't use the resume() call because we 'silently' stopped
// so resume() will not know we are actually stopped
fConnection.queueCommand(
new MIExecContinue(getContextToSuspend()),
new DataRequestMonitor<MIInfo>(getExecutor(), rm));
} else {
// We didn't suspend the container, so we don't need to resume it
rm.done();
}
}
};
/** /**
* {@inheritDoc} * {@inheritDoc}

View file

@ -80,7 +80,7 @@ public class MIBreakInsert extends MICommand<MIBreakInsertInfo>
* @since 3.0 * @since 3.0
*/ */
public MIBreakInsert(IBreakpointsTargetDMContext ctx, boolean isTemporary, boolean isHardware, public MIBreakInsert(IBreakpointsTargetDMContext ctx, boolean isTemporary, boolean isHardware,
String condition, int ignoreCount, String line, int tid, boolean disabled, boolean isTracepoint) { String condition, int ignoreCount, String location, int tid, boolean disabled, boolean isTracepoint) {
super(ctx, "-break-insert"); //$NON-NLS-1$ super(ctx, "-break-insert"); //$NON-NLS-1$
// For a tracepoint, force certain parameters to what is allowed // For a tracepoint, force certain parameters to what is allowed
@ -160,7 +160,7 @@ public class MIBreakInsert extends MICommand<MIBreakInsertInfo>
if (opts.length > 0) { if (opts.length > 0) {
setOptions(opts); setOptions(opts);
} }
setParameters(new Adjustable[]{ new PathAdjustable(line)}); setParameters(new Adjustable[]{ new PathAdjustable(location)});
} }
@Override @Override

View file

@ -5,7 +5,7 @@
// Copyright : Ericsson AB // Copyright : Ericsson AB
// Description : Breakpoint test application // Description : Breakpoint test application
//============================================================================ //============================================================================
#include <unistd.h>
#include <iostream> #include <iostream>
using namespace std; using namespace std;
@ -32,16 +32,20 @@ void setBlocks()
void loop() void loop()
{ {
int j; int j = 10;
int i = 0;
for (int i = 0; i < ARRAY_SIZE; i++) for (i = 0; i < ARRAY_SIZE; i++)
j = i; j = i;
} }
int main() int main()
{ {
int a = 10;
zeroBlocks(1); zeroBlocks(1);
loop(); loop();
setBlocks(); setBlocks();
sleep(1);
a++;
return 0; return 0;
} }

View file

@ -1,5 +1,5 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2007 Ericsson and others. * Copyright (c) 2007, 2010 Ericsson and others.
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
@ -44,6 +44,7 @@ import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecFinish;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecNext; import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecNext;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecStep; import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecStep;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecUntil; import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecUntil;
import org.eclipse.cdt.dsf.mi.service.command.events.MIRunningEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent;
import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakListInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakListInfo;
@ -277,6 +278,40 @@ public class SyncUtil {
return SyncResumeUntilStopped(fGdbContainerDmc); return SyncResumeUntilStopped(fGdbContainerDmc);
} }
public static MIRunningEvent SyncResume(final IExecutionDMContext dmc) throws Throwable {
final ServiceEventWaitor<MIRunningEvent> eventWaitor =
new ServiceEventWaitor<MIRunningEvent>(
fSession,
MIRunningEvent.class);
fRunControl.getExecutor().submit(new Runnable() {
public void run() {
// No need for a RequestMonitor since we will wait for the
// ServiceEvent telling us the program has been resumed
fCommandControl.queueCommand(
new MIExecContinue(dmc),
null);
}
});
// Wait for the execution to suspend after the step
return eventWaitor.waitForEvent(ServiceEventWaitor.WAIT_FOREVER);
}
public static MIRunningEvent SyncResume() throws Throwable {
return SyncResume(fGdbContainerDmc);
}
public static MIStoppedEvent SyncWaitForStop() throws Throwable {
final ServiceEventWaitor<MIStoppedEvent> eventWaitor =
new ServiceEventWaitor<MIStoppedEvent>(
fSession,
MIStoppedEvent.class);
// Wait for the execution to suspend
return eventWaitor.waitForEvent(ServiceEventWaitor.WAIT_FOREVER);
}
public static MIStoppedEvent SyncRunToLocation(final String location) throws Throwable { public static MIStoppedEvent SyncRunToLocation(final String location) throws Throwable {
// Set a temporary breakpoint and run to it. // Set a temporary breakpoint and run to it.
// Note that if there were other breakpoints set ahead of this one, // Note that if there were other breakpoints set ahead of this one,

View file

@ -1,5 +1,5 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2009 Ericsson and others. * Copyright (c) 2009, 2010 Ericsson and others.
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
@ -15,6 +15,8 @@ import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner;
import org.eclipse.cdt.tests.dsf.gdb.framework.BaseTestCase; import org.eclipse.cdt.tests.dsf.gdb.framework.BaseTestCase;
import org.eclipse.cdt.tests.dsf.gdb.tests.MIBreakpointsTest; import org.eclipse.cdt.tests.dsf.gdb.tests.MIBreakpointsTest;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@RunWith(BackgroundRunner.class) @RunWith(BackgroundRunner.class)
@ -24,4 +26,9 @@ public class MIBreakpointsTest_6_8 extends MIBreakpointsTest {
BaseTestCase.setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, "gdb.6.8"); BaseTestCase.setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, "gdb.6.8");
BaseTestCase.setLaunchAttribute(ATTR_DEBUG_SERVER_NAME, "gdbserver.6.8"); BaseTestCase.setLaunchAttribute(ATTR_DEBUG_SERVER_NAME, "gdbserver.6.8");
} }
@Override
@Ignore("This GDB 6.8 only has a bug which ignores watchpoint conditions")
@Test
public void breakpointHit_watchpointUpdateCondition() throws Throwable {
}
} }