mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-08-04 14:55:41 +02:00
Bug 337893 - Support concurrent setting of bp on a running target for non-stop mode, single process
This commit is contained in:
parent
a9a26fba14
commit
7edc697389
1 changed files with 305 additions and 36 deletions
|
@ -14,13 +14,15 @@ package org.eclipse.cdt.dsf.gdb.service;
|
|||
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
|
||||
import org.eclipse.cdt.core.IAddress;
|
||||
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
||||
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.MultiRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.Sequence;
|
||||
import org.eclipse.cdt.dsf.concurrent.Sequence.Step;
|
||||
|
@ -898,25 +900,6 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
|
|||
threadState.fStateChangeDetails = event.getDetails();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
|
@ -925,19 +908,262 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
|
|||
* 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
|
||||
* Basically, we must make sure one thread 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;
|
||||
|
||||
/**
|
||||
* 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 container that needs to be suspended to perform the steps of the operation
|
||||
private IContainerDMContext fContainerDmcToSuspend;
|
||||
// The thread that we will actually suspend to make the container suspended.
|
||||
private IMIExecutionDMContext fExecutionDmcToSuspend;
|
||||
|
||||
// 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
|
||||
* @since 4.0
|
||||
*/
|
||||
protected boolean isTargetAvailable() {
|
||||
return fTargetAvailable;
|
||||
}
|
||||
|
||||
/** @since 4.0 */
|
||||
protected void setTargetAvailable(boolean available) {
|
||||
fTargetAvailable = available;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the container context that needs to be suspended to perform the
|
||||
* required operation.
|
||||
* @since 4.0
|
||||
*/
|
||||
protected IContainerDMContext getContainerToSuspend() {
|
||||
return fContainerDmcToSuspend;
|
||||
}
|
||||
|
||||
/** @since 4.0 */
|
||||
protected void setContainerToSuspend(IContainerDMContext context) {
|
||||
fContainerDmcToSuspend = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the container context that needs to be suspended to perform the
|
||||
* required operation.
|
||||
* @since 4.0
|
||||
*/
|
||||
protected IMIExecutionDMContext getExecutionToSuspend() {
|
||||
return fExecutionDmcToSuspend;
|
||||
}
|
||||
|
||||
/** @since 4.0 */
|
||||
protected void setExecutionToSuspend(IMIExecutionDMContext context) {
|
||||
fExecutionDmcToSuspend = 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
|
||||
*/
|
||||
protected class IsTargetAvailableStep extends Sequence.Step {
|
||||
|
@ -949,17 +1175,17 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
|
|||
|
||||
@Override
|
||||
public void execute(final RequestMonitor rm) {
|
||||
fContainerDmcToSuspend = DMContexts.getAncestorOfType(fCtx, IContainerDMContext.class);
|
||||
fContainerDmcToSuspend = DMContexts.getAncestorOfType(fCtx, IMIContainerDMContext.class);
|
||||
if (fContainerDmcToSuspend != null) {
|
||||
// In non-stop, we don't actually need this particular process to be suspended,
|
||||
// all we need is one of any of the processes to be suspended.
|
||||
// However, for efficiency, we can first check if the process in question
|
||||
// is suspended.
|
||||
if (isSuspended(fContainerDmcToSuspend)) {
|
||||
fTargetAvailable = true;
|
||||
String groupId = ((IMIContainerDMContext)fContainerDmcToSuspend).getGroupId();
|
||||
if (groupId != null && groupId.length() > 0) {
|
||||
// If we have a fully formed containerDmc, we can use it directly.
|
||||
fTargetAvailable = isSuspended(fContainerDmcToSuspend);
|
||||
rm.done();
|
||||
return;
|
||||
}
|
||||
// else, the container is not fully formed, so let's fetch it below, since
|
||||
// we know we are running single-process and there is only one process
|
||||
}
|
||||
|
||||
// If we get here, we have to get the list of processes to know if any of
|
||||
|
@ -974,7 +1200,7 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
|
|||
assert getData() != null;
|
||||
|
||||
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
|
||||
fTargetAvailable = true;
|
||||
} else {
|
||||
|
@ -995,12 +1221,14 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
|
|||
};
|
||||
|
||||
/**
|
||||
* If the execution context of interest is not suspended, this step
|
||||
* will interrupt it.
|
||||
* @since 3.0
|
||||
*/
|
||||
protected class MakeTargetAvailableStep extends Sequence.Step {
|
||||
@Override
|
||||
public void execute(final RequestMonitor rm) {
|
||||
if (!fTargetAvailable) {
|
||||
if (!isTargetAvailable()) {
|
||||
// Instead of suspending the entire process, let's find its first thread and use that
|
||||
IProcesses processControl = getServicesTracker().getService(IProcesses.class);
|
||||
processControl.getProcessesBeingDebugged(
|
||||
|
@ -1028,7 +1256,6 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
|
|||
super.handleFailure();
|
||||
};
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
@ -1043,12 +1270,50 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
|
|||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
protected class RestoreTargetStateStep extends Sequence.Step {
|
||||
@Override
|
||||
public void execute(final RequestMonitor rm) {
|
||||
if (!fTargetAvailable) {
|
||||
if (!isTargetAvailable()) {
|
||||
assert fDisableNextRunningEventDmc == null;
|
||||
fDisableNextRunningEventDmc = fExecutionDmcToSuspend;
|
||||
|
||||
|
@ -1089,6 +1354,10 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
|
|||
}
|
||||
};
|
||||
|
||||
/* ******************************************************************************
|
||||
* End of section to support operations even when the target is unavailable.
|
||||
* ******************************************************************************/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Event handlers
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Add table
Reference in a new issue