mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-29 11:55:40 +02:00
Bug 337893 - Support concurrent setting of bp on a running target
This commit is contained in:
parent
09499bd32c
commit
ccfa88d37b
1 changed files with 281 additions and 34 deletions
|
@ -12,13 +12,15 @@
|
||||||
package org.eclipse.cdt.dsf.mi.service;
|
package org.eclipse.cdt.dsf.mi.service;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import org.eclipse.cdt.core.IAddress;
|
import org.eclipse.cdt.core.IAddress;
|
||||||
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
||||||
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
|
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
|
||||||
|
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
|
||||||
import org.eclipse.cdt.dsf.concurrent.Immutable;
|
import org.eclipse.cdt.dsf.concurrent.Immutable;
|
||||||
|
import org.eclipse.cdt.dsf.concurrent.MultiRequestMonitor;
|
||||||
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;
|
||||||
import org.eclipse.cdt.dsf.concurrent.Sequence.Step;
|
import org.eclipse.cdt.dsf.concurrent.Sequence.Step;
|
||||||
|
@ -907,39 +909,69 @@ 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.
|
* Section to support operations even when the target is unavailable.
|
||||||
*
|
*
|
||||||
* Basically, we must make sure the container is suspended before making
|
* Basically, we must make sure the container is suspended before making
|
||||||
* certain operations (currently breakpoints). If we don't, we must first
|
* certain operations (currently breakpoints). If we don't, we must first
|
||||||
* suspend the container, then perform the specified operations,
|
* suspend the container, then perform the specified operations,
|
||||||
* and finally resume the container.
|
* and finally resume the container.
|
||||||
* See https://bugs.eclipse.org/bugs/show_bug.cgi?id=242943
|
* See http://bugs.eclipse.org/242943
|
||||||
* and https://bugs.eclipse.org/bugs/show_bug.cgi?id=282273
|
* and http://bugs.eclipse.org/282273
|
||||||
|
*
|
||||||
|
* Note that for multi-process, the correct container must be suspended for the
|
||||||
|
* breakpoint to be inserted on that container. Not a big deal though, since
|
||||||
|
* a breakpointDmc is mapped to a specific container. Also, since we are in
|
||||||
|
* all-stop mode here, it does not really matter what we stop, everything will
|
||||||
|
* stop.
|
||||||
|
* See http://bugs.eclipse.org/337893
|
||||||
*
|
*
|
||||||
* ******************************************************************************/
|
* ******************************************************************************/
|
||||||
private IContainerDMContext fContainerDmc = null;
|
|
||||||
private boolean fTargetAvailable = false;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class to store the parameters of the executeWithTargetAvailable() operations.
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
protected static class TargetAvailableOperationInfo {
|
||||||
|
public IDMContext ctx;
|
||||||
|
public Sequence.Step[] steps;
|
||||||
|
public RequestMonitor rm;
|
||||||
|
|
||||||
|
public TargetAvailableOperationInfo(IDMContext ctx, Step[] steps, RequestMonitor rm) {
|
||||||
|
super();
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.steps = steps;
|
||||||
|
this.rm = rm;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Keep track of if the target was available or not when we started the operation
|
||||||
|
private boolean fTargetAvailable;
|
||||||
|
// The execution context that need to be available.
|
||||||
|
private IExecutionDMContext fExecutionDmc;
|
||||||
|
// Do we currently have an executeWithTargetAvailable() operation ongoing?
|
||||||
|
private boolean fOngoingOperation;
|
||||||
|
// Are we currently executing steps passed into executeWithTargetAvailable()?
|
||||||
|
// This allows us to know if we can add more steps to execute or if we missed
|
||||||
|
// our opportunity
|
||||||
|
private boolean fCurrentlyExecutingSteps;
|
||||||
|
|
||||||
|
// MultiRequestMonitor that allows us to track all the different steps we are
|
||||||
|
// executing. Once all steps are executed, we can complete this MultiRM and
|
||||||
|
// allow the global sequence to continue.
|
||||||
|
// Note that we couldn't use a CountingRequestMonitor because that type of RM
|
||||||
|
// needs to know in advance how many subRms it will track; the MultiRM allows us
|
||||||
|
// to receive more steps to execute continuously, and be able to upate the MultiRM.
|
||||||
|
private MultiRequestMonitor<RequestMonitor> fExecuteQueuedOpsStepMonitor;
|
||||||
|
// The number of batches of steps that are still being executing for potentially
|
||||||
|
// concurrent executeWithTargetAvailable() operations.
|
||||||
|
// Once this gets to zero, we know we have executed all the steps we were aware of
|
||||||
|
// and we can complete the operation.
|
||||||
|
private int fNumStepsStillExecuting;
|
||||||
|
// Queue of executeWithTargetAvailable() operations that need to be processed.
|
||||||
|
private LinkedList<TargetAvailableOperationInfo> fOperationsPending = new LinkedList<TargetAvailableOperationInfo>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the target is available to perform operations
|
* Returns whether the target is available to perform operations
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
|
@ -948,15 +980,186 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
|
||||||
return fTargetAvailable;
|
return fTargetAvailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 4.0 */
|
||||||
|
protected void setTargetAvailable(boolean available) {
|
||||||
|
fTargetAvailable = available;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the target must be suspended before performing the breakpoint operation
|
* Returns the execution context that needs to be suspended to perform the
|
||||||
|
* required operation.
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
protected IExecutionDMContext getContextToSuspend() {
|
protected IExecutionDMContext getContextToSuspend() {
|
||||||
return fContainerDmc;
|
return fExecutionDmc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 4.0 */
|
||||||
|
protected void setContextToSuspend(IExecutionDMContext context) {
|
||||||
|
fExecutionDmc = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether there is currently an ExecuteWithTargetAvailable() operation ongoing.
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
protected boolean isTargetAvailableOperationOngoing() {
|
||||||
|
return fOngoingOperation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 4.0 */
|
||||||
|
protected void setTargetAvailableOperationOngoing(boolean ongoing) {
|
||||||
|
fOngoingOperation = ongoing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether we are current in the process of executing the steps
|
||||||
|
* that were passed to ExecuteWithTargetAvailable().
|
||||||
|
* When this value is true, we can send more steps to be executed.
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
protected boolean isCurrentlyExecutingSteps() {
|
||||||
|
return fCurrentlyExecutingSteps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 4.0 */
|
||||||
|
protected void setCurrentlyExecutingSteps(boolean executing) {
|
||||||
|
fCurrentlyExecutingSteps = executing;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Returns the requestMonitor that will be run once all steps sent to
|
||||||
|
* ExecuteWithTargetAvailable() have been executed.
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
protected MultiRequestMonitor<RequestMonitor> getExecuteQueuedStepsRM() {
|
||||||
|
return fExecuteQueuedOpsStepMonitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 4.0 */
|
||||||
|
protected void setExecuteQueuedStepsRM(MultiRequestMonitor<RequestMonitor> rm) {
|
||||||
|
fExecuteQueuedOpsStepMonitor = rm;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of batches of steps sent to ExecuteWithTargetAvailable()
|
||||||
|
* that are still executing. Once this number reaches zero, we can complete
|
||||||
|
* the overall ExecuteWithTargetAvailable() operation.
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
protected int getNumStepsStillExecuting() {
|
||||||
|
return fNumStepsStillExecuting;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 4.0 */
|
||||||
|
protected void setNumStepsStillExecuting(int num) {
|
||||||
|
fNumStepsStillExecuting = num;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the queue of executeWithTargetAvailable() operations that still need to be processed
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
protected LinkedList<TargetAvailableOperationInfo> getOperationsPending() {
|
||||||
|
return fOperationsPending;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method takes care of executing a batch of steps that were passed to
|
||||||
|
* ExecuteWithTargetAvailable(). The method is used to track the progress
|
||||||
|
* of all these batches of steps, so that we know exactly when all of them
|
||||||
|
* have been completed and the global sequence can be completed.
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
protected void executeSteps(final TargetAvailableOperationInfo info) {
|
||||||
|
fNumStepsStillExecuting++;
|
||||||
|
|
||||||
|
// This RM propagates any error to the original rm of the actual steps.
|
||||||
|
// Even in case of errors for these steps, we want to continue the overall sequence
|
||||||
|
RequestMonitor stepsRm = new RequestMonitor(ImmediateExecutor.getInstance(), null) {
|
||||||
|
@Override
|
||||||
|
protected void handleCompleted() {
|
||||||
|
info.rm.setStatus(getStatus());
|
||||||
|
// It is important to call rm.done() right away.
|
||||||
|
// This is because some other operation we are performing might be waiting
|
||||||
|
// for this one to be done. If we try to wait for the entire sequence to be
|
||||||
|
// done, then we will never finish because one monitor will never show as
|
||||||
|
// done, waiting for the second one.
|
||||||
|
info.rm.done();
|
||||||
|
|
||||||
|
fExecuteQueuedOpsStepMonitor.requestMonitorDone(this);
|
||||||
|
fNumStepsStillExecuting--;
|
||||||
|
if (fNumStepsStillExecuting == 0) {
|
||||||
|
fExecuteQueuedOpsStepMonitor.doneAdding();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fExecuteQueuedOpsStepMonitor.add(stepsRm);
|
||||||
|
|
||||||
|
getExecutor().execute(new Sequence(getExecutor(), stepsRm) {
|
||||||
|
@Override public Step[] getSteps() { return info.steps; }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public void executeWithTargetAvailable(IDMContext ctx, final Sequence.Step[] steps, final RequestMonitor rm) {
|
||||||
|
if (!fOngoingOperation) {
|
||||||
|
// We are the first operation of this kind currently requested
|
||||||
|
// so we need to start the sequence
|
||||||
|
fOngoingOperation = true;
|
||||||
|
|
||||||
|
// We always go through our queue, even if we only have a single call to this method
|
||||||
|
fOperationsPending.add(new TargetAvailableOperationInfo(ctx, steps, rm));
|
||||||
|
|
||||||
|
// Steps that need to be executed to perform the operation
|
||||||
|
final Step[] sequenceSteps = new Step[] {
|
||||||
|
new IsTargetAvailableStep(ctx),
|
||||||
|
new MakeTargetAvailableStep(),
|
||||||
|
new ExecuteQueuedOperationsStep(),
|
||||||
|
new RestoreTargetStateStep(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Once all the sequence is completed, we need to see if we have received
|
||||||
|
// another request that we now need to process
|
||||||
|
RequestMonitor sequenceCompletedRm = new RequestMonitor(getExecutor(), null) {
|
||||||
|
@Override
|
||||||
|
protected void handleCompleted() {
|
||||||
|
fOngoingOperation = false;
|
||||||
|
|
||||||
|
if (fOperationsPending.size() > 0) {
|
||||||
|
// Darn, more operations came in. Trigger their processing
|
||||||
|
// by calling executeWithTargetAvailable() on the last one
|
||||||
|
TargetAvailableOperationInfo info = fOperationsPending.removeLast();
|
||||||
|
executeWithTargetAvailable(info.ctx, info.steps, info.rm);
|
||||||
|
}
|
||||||
|
// no other rm.done() needs to be called, they have all been handled already
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getExecutor().execute(new Sequence(getExecutor(), sequenceCompletedRm) {
|
||||||
|
@Override public Step[] getSteps() { return sequenceSteps; }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// We are currently already executing such an operation
|
||||||
|
// If we are still in the process of executing steps, let's include this new set of steps.
|
||||||
|
// This is important because some steps may depend on these new ones.
|
||||||
|
if (fCurrentlyExecutingSteps) {
|
||||||
|
executeSteps(new TargetAvailableOperationInfo(ctx, steps, rm));
|
||||||
|
} else {
|
||||||
|
// Too late to execute the new steps, so queue them for later
|
||||||
|
fOperationsPending.add(new TargetAvailableOperationInfo(ctx, steps, rm));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This part of the sequence verifies if the execution context of interest
|
||||||
|
* is suspended or not.
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
protected class IsTargetAvailableStep extends Sequence.Step {
|
protected class IsTargetAvailableStep extends Sequence.Step {
|
||||||
|
@ -968,11 +1171,9 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(final RequestMonitor rm) {
|
public void execute(final RequestMonitor rm) {
|
||||||
fContainerDmc = DMContexts.getAncestorOfType(fCtx, IContainerDMContext.class);
|
fExecutionDmc = DMContexts.getAncestorOfType(fCtx, IMIContainerDMContext.class);
|
||||||
if (fContainerDmc != null) {
|
if (fExecutionDmc != null) {
|
||||||
// In all-stop, if any process is suspended, then all of them are suspended
|
fTargetAvailable = isSuspended(fExecutionDmc);
|
||||||
// so we only need to check this process.
|
|
||||||
fTargetAvailable = isSuspended(fContainerDmc);
|
|
||||||
rm.done();
|
rm.done();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -987,12 +1188,14 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
|
||||||
assert getData() != null;
|
assert getData() != null;
|
||||||
|
|
||||||
if (getData().length == 0) {
|
if (getData().length == 0) {
|
||||||
// Happens at startup, starting with GDB 7.0
|
// Happens at startup, starting with GDB 7.0.
|
||||||
// This means the target is available
|
// This means the target is available
|
||||||
fTargetAvailable = true;
|
fTargetAvailable = true;
|
||||||
} else {
|
} else {
|
||||||
fContainerDmc = (IContainerDMContext)(getData()[0]);
|
// In all-stop, if any process is suspended, then all of them are suspended
|
||||||
fTargetAvailable = isSuspended(fContainerDmc);
|
// so we only need to check the first process.
|
||||||
|
fExecutionDmc = (IExecutionDMContext)(getData()[0]);
|
||||||
|
fTargetAvailable = isSuspended(fExecutionDmc);
|
||||||
}
|
}
|
||||||
rm.done();
|
rm.done();
|
||||||
}
|
}
|
||||||
|
@ -1001,6 +1204,8 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* If the execution context of interest is not suspended, this step
|
||||||
|
* will interrupt it.
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
protected class MakeTargetAvailableStep extends Sequence.Step {
|
protected class MakeTargetAvailableStep extends Sequence.Step {
|
||||||
|
@ -1033,6 +1238,44 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* This step of the sequence takes care of executing all the steps that
|
||||||
|
* were passed to ExecuteWithTargetAvailable().
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
protected class ExecuteQueuedOperationsStep extends Sequence.Step {
|
||||||
|
@Override
|
||||||
|
public void execute(final RequestMonitor rm) {
|
||||||
|
fCurrentlyExecutingSteps = true;
|
||||||
|
|
||||||
|
// It is important to use an ImmediateExecutor for this RM, to make sure we don't risk getting a new
|
||||||
|
// call to ExecuteWithTargetAvailable() when we just finished executing the steps.
|
||||||
|
fExecuteQueuedOpsStepMonitor = new MultiRequestMonitor<RequestMonitor>(ImmediateExecutor.getInstance(), rm) {
|
||||||
|
@Override
|
||||||
|
protected void handleCompleted() {
|
||||||
|
assert fOperationsPending.size() == 0;
|
||||||
|
|
||||||
|
// We don't handle errors here. Instead, we have already propagated any
|
||||||
|
// errors to each rm for each set of steps
|
||||||
|
|
||||||
|
fCurrentlyExecutingSteps = false;
|
||||||
|
// Continue the sequence
|
||||||
|
rm.done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Tell the RM that we need to confirm when we are done adding sub-rms
|
||||||
|
fExecuteQueuedOpsStepMonitor.requireDoneAdding();
|
||||||
|
|
||||||
|
// All pending operations are independent of each other so we can
|
||||||
|
// run them concurrently.
|
||||||
|
while (fOperationsPending.size() > 0) {
|
||||||
|
executeSteps(fOperationsPending.poll());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the sequence had to interrupt the execution context of interest,
|
||||||
|
* this step will resume it again to reach the same state as when we started.
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
protected class RestoreTargetStateStep extends Sequence.Step {
|
protected class RestoreTargetStateStep extends Sequence.Step {
|
||||||
|
@ -1078,6 +1321,10 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* ******************************************************************************
|
||||||
|
* End of section to support operations even when the target is unavailable.
|
||||||
|
* ******************************************************************************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
* @since 1.1
|
* @since 1.1
|
||||||
|
|
Loading…
Add table
Reference in a new issue