mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-29 19:45:01 +02:00
[205142] Refactored use of DM contexts to make them more flexible.
This commit is contained in:
parent
18fd2d6ef6
commit
1d31b66b19
10 changed files with 586 additions and 271 deletions
|
@ -15,7 +15,6 @@ import org.eclipse.core.runtime.Status;
|
|||
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
|
||||
import org.eclipse.dd.dsf.concurrent.Immutable;
|
||||
import org.eclipse.dd.dsf.datamodel.DMContexts;
|
||||
import org.eclipse.dd.dsf.debug.service.INativeProcesses;
|
||||
import org.eclipse.dd.dsf.debug.service.IRunControl;
|
||||
import org.eclipse.dd.dsf.debug.service.IStepQueueManager;
|
||||
import org.eclipse.dd.dsf.debug.service.IRunControl.IExecutionDMContext;
|
||||
|
@ -40,10 +39,6 @@ public abstract class DsfCommandRunnable extends DsfRunnable {
|
|||
return fTracker.getService(IStepQueueManager.class);
|
||||
}
|
||||
|
||||
public INativeProcesses getProcesses() {
|
||||
return fTracker.getService(INativeProcesses.class);
|
||||
}
|
||||
|
||||
public DsfCommandRunnable(DsfServicesTracker servicesTracker, Object element, IDebugCommandRequest request) {
|
||||
fTracker = servicesTracker;
|
||||
if (element instanceof DMVMContext) {
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2006 Wind River Systems and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Wind River Systems - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.dd.dsf.debug.ui.actions;
|
||||
|
||||
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
|
||||
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
|
||||
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
|
||||
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
|
||||
import org.eclipse.dd.dsf.datamodel.DMContexts;
|
||||
import org.eclipse.dd.dsf.debug.service.INativeProcesses;
|
||||
import org.eclipse.dd.dsf.debug.service.IRunControl.IExecutionDMContext;
|
||||
import org.eclipse.dd.dsf.debug.ui.DsfDebugUIPlugin;
|
||||
import org.eclipse.dd.dsf.service.DsfServicesTracker;
|
||||
import org.eclipse.dd.dsf.service.DsfSession;
|
||||
import org.eclipse.dd.dsf.ui.viewmodel.dm.AbstractDMVMLayoutNode;
|
||||
import org.eclipse.dd.dsf.ui.viewmodel.dm.AbstractDMVMLayoutNode.DMVMContext;
|
||||
import org.eclipse.debug.core.commands.IDebugCommandRequest;
|
||||
import org.eclipse.debug.core.commands.IEnabledStateRequest;
|
||||
import org.eclipse.debug.core.commands.ITerminateHandler;
|
||||
|
||||
public class DsfTerminateCommand implements ITerminateHandler {
|
||||
private final DsfExecutor fExecutor;
|
||||
private final DsfServicesTracker fTracker;
|
||||
|
||||
public DsfTerminateCommand(DsfSession session) {
|
||||
fExecutor = session.getExecutor();
|
||||
fTracker = new DsfServicesTracker(DsfDebugUIPlugin.getBundleContext(), session.getId());
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
fTracker.dispose();
|
||||
}
|
||||
|
||||
// Run control may not be avilable after a connection is terminated and shut down.
|
||||
public void canExecute(final IEnabledStateRequest request) {
|
||||
if (request.getElements().length != 1 ||
|
||||
!(request.getElements()[0] instanceof DMVMContext) )
|
||||
{
|
||||
request.setEnabled(false);
|
||||
request.done();
|
||||
return;
|
||||
}
|
||||
|
||||
// Javac doesn't like the cast to "(AbstractDMVMLayoutNode<?>.DMVMContext)" need to use the
|
||||
// construct below and suppress warnings.
|
||||
@SuppressWarnings("unchecked")
|
||||
AbstractDMVMLayoutNode.DMVMContext vmc = (AbstractDMVMLayoutNode.DMVMContext)request.getElements()[0];
|
||||
final IExecutionDMContext dmc = DMContexts.getAncestorOfType(vmc.getDMC(), IExecutionDMContext.class);
|
||||
if (dmc == null) {
|
||||
request.setEnabled(false);
|
||||
request.done();
|
||||
return;
|
||||
}
|
||||
|
||||
fExecutor.execute(
|
||||
new DsfRunnable() {
|
||||
public void run() {
|
||||
// Get the processes service and the exec context.
|
||||
INativeProcesses processes = fTracker.getService(INativeProcesses.class);
|
||||
if (processes == null || dmc == null) {
|
||||
// Context or service already invalid.
|
||||
request.done();
|
||||
} else {
|
||||
// Check the teriminate.
|
||||
processes.canTerminate(
|
||||
processes.getProcessForDebugContext(dmc),
|
||||
new DataRequestMonitor<Boolean>(fExecutor, null) {
|
||||
@Override
|
||||
public void handleCompleted() {
|
||||
request.setEnabled(getData());
|
||||
request.done();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean execute(final IDebugCommandRequest request) {
|
||||
if (request.getElements().length != 1) {
|
||||
request.done();
|
||||
return false;
|
||||
}
|
||||
|
||||
fExecutor.submit(new DsfCommandRunnable(fTracker, request.getElements()[0], request) {
|
||||
@Override public void doExecute() {
|
||||
getProcesses().terminate(
|
||||
getProcesses().getProcessForDebugContext(getContext()), new RequestMonitor(fExecutor, null));
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -33,6 +33,7 @@ import org.eclipse.dd.dsf.debug.service.IExpressions.IExpressionDMContext;
|
|||
import org.eclipse.dd.dsf.debug.service.IExpressions.IExpressionDMData;
|
||||
import org.eclipse.dd.dsf.debug.service.IFormattedValues.FormattedValueDMContext;
|
||||
import org.eclipse.dd.dsf.debug.service.IFormattedValues.FormattedValueDMData;
|
||||
import org.eclipse.dd.dsf.debug.service.IMemory.IMemoryDMContext;
|
||||
import org.eclipse.dd.dsf.service.DsfSession;
|
||||
import org.eclipse.dd.dsf.service.IDsfService;
|
||||
import org.eclipse.debug.core.DebugException;
|
||||
|
@ -66,7 +67,7 @@ public class DsfMemoryBlockRetrieval extends PlatformObject implements IMemoryBl
|
|||
private final String fModelId;
|
||||
private final DsfSession fSession;
|
||||
private final DsfExecutor fExecutor;
|
||||
private IDMContext fContext;
|
||||
private IMemoryDMContext fContext;
|
||||
private final ServiceTracker fMemoryServiceTracker;
|
||||
private final ServiceTracker fExpressionServiceTracker;
|
||||
|
||||
|
@ -77,7 +78,7 @@ public class DsfMemoryBlockRetrieval extends PlatformObject implements IMemoryBl
|
|||
* @param dmc
|
||||
* @throws DebugException
|
||||
*/
|
||||
public DsfMemoryBlockRetrieval(String modelId, IDMContext dmc) throws DebugException {
|
||||
public DsfMemoryBlockRetrieval(String modelId, IMemoryDMContext dmc) throws DebugException {
|
||||
|
||||
fModelId = modelId;
|
||||
fContext = dmc;
|
||||
|
@ -263,11 +264,8 @@ public class DsfMemoryBlockRetrieval extends PlatformObject implements IMemoryBl
|
|||
return null;
|
||||
}
|
||||
|
||||
// Update the DMC
|
||||
fContext = dmc;
|
||||
|
||||
// Resolve the expression
|
||||
blockAddress = resolveMemoryAddress(fContext, expression);
|
||||
blockAddress = resolveMemoryAddress(dmc, expression);
|
||||
if (blockAddress == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -10,12 +10,9 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.dd.dsf.debug.service;
|
||||
|
||||
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
|
||||
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
|
||||
import org.eclipse.dd.dsf.datamodel.IDMContext;
|
||||
import org.eclipse.dd.dsf.datamodel.IDMData;
|
||||
import org.eclipse.dd.dsf.datamodel.IDMEvent;
|
||||
import org.eclipse.dd.dsf.datamodel.IDMService;
|
||||
import org.eclipse.debug.core.model.IBreakpoint;
|
||||
import org.eclipse.dd.dsf.service.IDsfService;
|
||||
|
||||
/**
|
||||
* Breakpoint service interface. The breakpoint service tracks platform breakpoint
|
||||
|
@ -24,26 +21,28 @@ import org.eclipse.debug.core.model.IBreakpoint;
|
|||
* breakpoint status in more detail and more dynamically than it it possible with
|
||||
* just the marker-based breakpoint object.
|
||||
*/
|
||||
public interface IBreakpoints extends IDMService {
|
||||
public interface IBreakpoints extends IDsfService {
|
||||
|
||||
public enum BreakpointStatus { INSTALLED, FAILED_TO_INSTALL, FILTERED_OUT }
|
||||
/**
|
||||
* Marker interface for a context for which breakpoints can be installed.
|
||||
*/
|
||||
public interface IBreakpointsDMContext extends IDMContext {};
|
||||
|
||||
public interface IBreakpointDMContext extends IDMContext {}
|
||||
/**
|
||||
* Install and begin tracking breakpoints for given context. The service
|
||||
* will keep installing new breakpoints that appear in the IDE for this
|
||||
* context until {@link #uninstallBreakpoints(IDMContext)} is called for that
|
||||
* context.
|
||||
* @param dmc Context to start tracking breakpoints for.
|
||||
* @param rm Completion callback.
|
||||
*/
|
||||
public void installBreakpoints(IDMContext dmc, RequestMonitor rm);
|
||||
|
||||
public interface IBreakpointDMData extends IDMData {
|
||||
IBreakpoint getPlatformBreakpoint();
|
||||
BreakpointStatus getStatus();
|
||||
}
|
||||
|
||||
public interface IBreakpointDMEvent extends IDMEvent<IBreakpointDMContext> {}
|
||||
|
||||
public interface IBreakpointInstalledDMEvent extends IBreakpointDMEvent {}
|
||||
public interface IBreakpointUninstalledDMEvent extends IBreakpointDMEvent {}
|
||||
public interface IBreakpointInstallFailedDMEvent extends IBreakpointDMEvent {}
|
||||
|
||||
public interface IBreakpointHitEvent extends IBreakpointDMEvent {}
|
||||
|
||||
public void getAllBreakpoints(IDMContext ctx, DataRequestMonitor<IBreakpointDMContext[]> rm);
|
||||
public void getBreakpoints(IDMContext ctx, IBreakpoint platformBp, DataRequestMonitor<IBreakpointDMContext[]> rm);
|
||||
/**
|
||||
* Uninstall and stop tracking breakpoints for the given context.
|
||||
* @param dmc Context to start tracking breakpoints for.
|
||||
* @param rm Completion callback.
|
||||
*/
|
||||
public void uninstallBreakpoints(IDMContext dmc, RequestMonitor rm);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@ import org.eclipse.debug.core.model.MemoryByte;
|
|||
*/
|
||||
public interface IMemory extends IDsfService {
|
||||
|
||||
public interface IMemoryDMContext extends IDMContext {}
|
||||
|
||||
/**
|
||||
* Event generated every time a range of bytes is modified.
|
||||
*
|
||||
|
|
|
@ -1,131 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2006 Wind River Systems and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Wind River Systems - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.dd.dsf.debug.service;
|
||||
|
||||
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
|
||||
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
|
||||
import org.eclipse.dd.dsf.datamodel.IDMContext;
|
||||
import org.eclipse.dd.dsf.datamodel.IDMData;
|
||||
import org.eclipse.dd.dsf.datamodel.IDMEvent;
|
||||
import org.eclipse.dd.dsf.datamodel.IDMService;
|
||||
|
||||
/**
|
||||
* This interface provides access to the native OS's process
|
||||
* information, manipulation methods, and debugging methods.
|
||||
* This service provides a relatively simple interface for
|
||||
* manipulating processes as compared with a full-blown
|
||||
* remote target debugger.
|
||||
*/
|
||||
public interface INativeProcesses extends IDMService {
|
||||
|
||||
public interface IThreadDMContext extends IDMContext {}
|
||||
public interface IProcessDMContext extends IDMContext {}
|
||||
|
||||
/**
|
||||
* Interface for thread and process object data.
|
||||
*/
|
||||
public interface IThreadDMData extends IDMData {
|
||||
String getName();
|
||||
String getId();
|
||||
boolean isDebuggerAttached();
|
||||
IDMContext getDebugContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for thread and process object data.
|
||||
*/
|
||||
public interface IProcessDMData extends IDMData {
|
||||
String getName();
|
||||
String getId();
|
||||
boolean isDebuggerAttached();
|
||||
IDMContext getDebugContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Event indicating that process data has changed.
|
||||
*/
|
||||
public interface IProcessChangedDMEvent extends IDMEvent<IProcessDMContext> {}
|
||||
|
||||
|
||||
public interface IProcessStartedEvent extends IDMEvent<IDMContext> {
|
||||
IProcessDMContext getProcess();
|
||||
}
|
||||
|
||||
public interface IProcessExitedEvent extends IDMEvent<IDMContext> {
|
||||
IProcessDMContext getProcess();
|
||||
}
|
||||
|
||||
public void getThreadData(IThreadDMContext dmc, DataRequestMonitor<IThreadDMData> rm);
|
||||
|
||||
public void getProcessData(IProcessDMContext dmc, DataRequestMonitor<IProcessDMData> rm);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a thread for the corresponding context. <code>null</code> if no corresponding
|
||||
* thread exists.
|
||||
* @param execCtx
|
||||
* @return
|
||||
*/
|
||||
public IThreadDMContext getThreadForDebugContext(IDMContext execCtx);
|
||||
|
||||
/**
|
||||
* Returns a process context corresponding to the given context. <code>null</code> if no
|
||||
* corresponding process exists.
|
||||
*/
|
||||
public IProcessDMContext getProcessForDebugContext(IDMContext execCtx);
|
||||
|
||||
/**
|
||||
* Retrieves the current list of processes running on target.
|
||||
* @param rm Request completion monitor, to be filled in with array of process contexts.
|
||||
*/
|
||||
void getRunningProcesses(DataRequestMonitor<IProcessDMContext[]> rm);
|
||||
|
||||
/**
|
||||
* Attaches debugger to the given process.
|
||||
*/
|
||||
void attachDebuggerToProcess(IProcessDMContext procCtx, RequestMonitor requestMonitor);
|
||||
|
||||
/**
|
||||
* Starts a new process.
|
||||
* @param file Process image to use for the new process.
|
||||
* @param rm Request completion monitor, to be filled in with the process context.
|
||||
*/
|
||||
void runNewProcess(String file, DataRequestMonitor<IProcessDMContext> rm);
|
||||
|
||||
/**
|
||||
* Starts a new process with debugger attached.
|
||||
* @param file Process image to use for the new process.
|
||||
* @param rm Request completion monitor, to be willed in with the process context.
|
||||
*/
|
||||
void debugNewProcess(String file, DataRequestMonitor<IProcessDMContext> rm);
|
||||
|
||||
/**
|
||||
* Retrieves the list of processes which are currently under
|
||||
* debugger control.
|
||||
* @param rm Request completion monitor.
|
||||
*/
|
||||
void getProcessesBeingDebugged(DataRequestMonitor<IProcessDMContext[]> rm);
|
||||
|
||||
/**
|
||||
* Checks whether the given process or thread can be terminated.
|
||||
* @param thread Thread or process to terminate.
|
||||
* @param rm Return token.
|
||||
*/
|
||||
void canTerminate(IDMContext ctx, DataRequestMonitor<Boolean> rm);
|
||||
|
||||
/**
|
||||
* Terminates the selected process or thread.
|
||||
* @param thread Thread or process to terminate.
|
||||
* @param rm Request completion monitor, indicates success or failure.
|
||||
*/
|
||||
void terminate(IDMContext ctx, RequestMonitor requestMonitor);
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2007 Wind River Systems and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Wind River Systems - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.dd.dsf.debug.service;
|
||||
|
||||
import org.eclipse.dd.dsf.datamodel.IDMContext;
|
||||
import org.eclipse.dd.dsf.service.IDsfService;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface ISignals extends IDsfService {
|
||||
/**
|
||||
* Marker interface for a context for which signals can be set.
|
||||
*/
|
||||
public interface ISignalsDMContext extends IDMContext {};
|
||||
|
||||
}
|
|
@ -104,10 +104,12 @@ public interface IStack extends IDMService {
|
|||
|
||||
/**
|
||||
* Retrieves the stack depth of the specified stack frame.
|
||||
* @param dmc Context to retrieve data for.
|
||||
* @param The maximum depth of stack to calculate. Should be 0 to calculate
|
||||
* depth with no limit.
|
||||
* @param rm Callback
|
||||
*/
|
||||
void getStackDepth(DataRequestMonitor<Integer> rm);
|
||||
|
||||
void getStackDepth(int maxDepth, DataRequestMonitor<Integer> rm);
|
||||
void getStackDepth(IDMContext dmc, int maxDepth, DataRequestMonitor<Integer> rm);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,518 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2007 Wind River Systems and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Wind River Systems - initial API and implementation
|
||||
* Ericsson - Modified for caching commands corresponding to multiple execution contexts
|
||||
*******************************************************************************/
|
||||
|
||||
package org.eclipse.dd.dsf.debug.service.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
|
||||
import org.eclipse.dd.dsf.datamodel.DMContexts;
|
||||
import org.eclipse.dd.dsf.datamodel.IDMContext;
|
||||
import org.eclipse.dd.dsf.debug.DsfDebugPlugin;
|
||||
import org.eclipse.dd.dsf.service.IDsfService;
|
||||
|
||||
/**
|
||||
* This is a utility class for caching results of MI Commands. Return MIInfo
|
||||
* data is retrieved from the cache if command was previously executed, and
|
||||
* it is executed with MICommand service if it was not previously seen.
|
||||
*
|
||||
* Resetting the cache has to be performed by the object owning the cache when
|
||||
* when an event indicates that the data is obsolete (which is specific to the
|
||||
* types of commands being cached).
|
||||
*/
|
||||
|
||||
public class CommandCache implements ICommandListener
|
||||
{
|
||||
static enum CommandStyle { COALESCED, NONCOALESCED }
|
||||
|
||||
/**
|
||||
* Holds cache information for a given command.
|
||||
* @param <V> Type matches the result type associated with the command.
|
||||
*/
|
||||
class CommandInfo {
|
||||
|
||||
/*
|
||||
* Control variables.
|
||||
*/
|
||||
|
||||
/** List of the request monitors associated with this command */
|
||||
List<DataRequestMonitor<ICommandResult>> fCurrentRequestMonitors ;
|
||||
|
||||
/** Original command. Need for reference from Queue completion notification */
|
||||
ICommand<ICommandResult> fCommand;
|
||||
|
||||
/** Style of this command ( internal coalesced or not) */
|
||||
CommandStyle fCmdStyle;
|
||||
|
||||
/** Command being processed for this command */
|
||||
CommandInfo fCoalescedCmd;
|
||||
|
||||
/** No longer really used and needs to be deleted */
|
||||
int fResetCounterStatus;
|
||||
|
||||
public CommandInfo( CommandStyle cmdstyle, ICommand<ICommandResult> cmd, DataRequestMonitor<ICommandResult> rm ) {
|
||||
|
||||
fCmdStyle = cmdstyle;
|
||||
fCommand = cmd;
|
||||
fCurrentRequestMonitors = new LinkedList<DataRequestMonitor<ICommandResult>>();
|
||||
fCurrentRequestMonitors.add(rm);
|
||||
fCoalescedCmd = null;
|
||||
fResetCounterStatus = fResetCounter;
|
||||
}
|
||||
|
||||
public CommandStyle getCommandstyle() { return fCmdStyle; }
|
||||
public List<DataRequestMonitor<ICommandResult>> getRequestMonitorList() { return fCurrentRequestMonitors; }
|
||||
public ICommand<ICommandResult> getCommand() { return fCommand; }
|
||||
public CommandInfo getCoalescedCmd() { return fCoalescedCmd; }
|
||||
public void setCoalescedCmd( CommandInfo cmd ) { fCoalescedCmd = cmd; }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof CommandInfo)) return false;
|
||||
CommandInfo otherCmd = (CommandInfo)other;
|
||||
|
||||
return otherCmd.fCommand.equals(fCommand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (fCommand.hashCode()+ (fResetCounterStatus + 1));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This class contains 5 significant lists.
|
||||
*
|
||||
* Cached Results :
|
||||
*
|
||||
* Contains a mapping of commands and their completed results. Until the cached
|
||||
* results are cleared by the owner of the cache.
|
||||
*
|
||||
* Pending Commands Not Queued :
|
||||
*
|
||||
* The Control object has not yet indicated that it has recognized the command
|
||||
* yet. The user is not allowed to interrogate these objects until the Control
|
||||
* object indicates they have been queued ( commandQueued notification ).
|
||||
*
|
||||
* Pending Commands Unsent :
|
||||
*
|
||||
* This is the list of commands which have been issued to the Control object but
|
||||
* have not been actually issued to the backend. These commands represent coalesce
|
||||
* options. They may be compared against the Queued list being maintained by the
|
||||
* Control object until told otherwise - commandSent notification ).
|
||||
*
|
||||
* Pending Commands Sent :
|
||||
*
|
||||
* This is a list of commands which have been issued to the Control object and
|
||||
* have also been sent to the backend. It is not possible use these objects for
|
||||
* coalescents.
|
||||
*
|
||||
* Coalesced Pending Q :
|
||||
*
|
||||
* These represent original commands for which a new coalesced command has been
|
||||
* created. When the coalesced commands completes the results will be decomposed
|
||||
* when back into individual results from this command.
|
||||
*/
|
||||
|
||||
private boolean fIsTargetAvailable = true;
|
||||
private int fResetCounter = 0;
|
||||
|
||||
private ICommandControl fCommandControl;
|
||||
|
||||
private Map<IDMContext, HashMap<CommandInfo, ICommandResult>> fCachedContexts = new HashMap<IDMContext, HashMap<CommandInfo, ICommandResult>>();
|
||||
|
||||
private ArrayList<CommandInfo> fPendingQCommandsSent = new ArrayList<CommandInfo>();
|
||||
|
||||
private ArrayList<CommandInfo> fPendingQCommandsNotYetSent = new ArrayList<CommandInfo>();
|
||||
|
||||
private ArrayList<CommandInfo> fPendingQWaitingForCoalescedCompletion = new ArrayList<CommandInfo>();
|
||||
|
||||
public CommandCache(ICommandControl control) {
|
||||
fCommandControl = control;
|
||||
|
||||
/*
|
||||
* We listen for the notifications that the commands have been sent to the
|
||||
* backend from the GDB/MI Communications engine.
|
||||
*/
|
||||
fCommandControl.addCommandListener(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructs a coalesced command if possible.
|
||||
*/
|
||||
private CommandInfo getCoalescedCommand(CommandInfo cmd) {
|
||||
|
||||
for ( CommandInfo currentUnsentEntry : new ArrayList<CommandInfo>(fPendingQCommandsNotYetSent) ) {
|
||||
/*
|
||||
* Get the current unsent entry to determine if we can coalesced with it.
|
||||
*/
|
||||
ICommand<?> unsentCommand = currentUnsentEntry.getCommand();
|
||||
|
||||
/*
|
||||
* Check if we can so construct a new COALESCED command from scratch.
|
||||
*/
|
||||
|
||||
// For sanity's sake, cast the generic ?'s to concrete types in the cache implementation.
|
||||
@SuppressWarnings("unchecked")
|
||||
ICommand<ICommandResult> coalescedCmd =
|
||||
(ICommand<ICommandResult>)unsentCommand.coalesceWith( cmd.getCommand() );
|
||||
|
||||
if ( coalescedCmd != null ) {
|
||||
CommandInfo coalescedCmdInfo = new CommandInfo( CommandStyle.COALESCED, coalescedCmd, null) ;
|
||||
|
||||
if ( currentUnsentEntry.getCommandstyle() == CommandStyle.COALESCED ) {
|
||||
/*
|
||||
* We matched a command which is itself already a COALESCED command. So
|
||||
* we need to run through the reference list and point all the current
|
||||
* command which are referencing the command we just subsumed and change
|
||||
* them to point to the new super command.
|
||||
*/
|
||||
|
||||
for ( CommandInfo waitingEntry : new ArrayList<CommandInfo>(fPendingQWaitingForCoalescedCompletion) ) {
|
||||
|
||||
if ( waitingEntry.getCoalescedCmd() == currentUnsentEntry ) {
|
||||
/*
|
||||
* This referenced the old command change it to point to the new one.
|
||||
*/
|
||||
waitingEntry.setCoalescedCmd(coalescedCmdInfo);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* This currently unsent entry needs to go into the coalescing list. To
|
||||
* be completed when the coalesced command comes back with a result.
|
||||
*/
|
||||
fPendingQWaitingForCoalescedCompletion.add(currentUnsentEntry);
|
||||
currentUnsentEntry.setCoalescedCmd(coalescedCmdInfo);
|
||||
}
|
||||
|
||||
/*
|
||||
* Either way we want to take the command back from the Control object so it
|
||||
* does not continue to process it.
|
||||
*/
|
||||
fPendingQCommandsNotYetSent.remove(currentUnsentEntry);
|
||||
fCommandControl.removeCommand(unsentCommand);
|
||||
|
||||
return( coalescedCmdInfo );
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes given ICommand, or retrieves the cached result if known.
|
||||
* @param command Command to execute.
|
||||
* @param rm Return token, contains the retrieved MIInfo object as
|
||||
* well as its cache status.
|
||||
*/
|
||||
public <V extends ICommandResult> void execute(ICommand<V> command, DataRequestMonitor<V> rm) {
|
||||
assert fCommandControl.getExecutor().isInExecutorThread();
|
||||
|
||||
// Cast the generic ?'s to concrete types in the cache implementation.
|
||||
@SuppressWarnings("unchecked")
|
||||
final ICommand<ICommandResult> genericCommand = (ICommand<ICommandResult>)command;
|
||||
@SuppressWarnings("unchecked")
|
||||
final DataRequestMonitor<ICommandResult> genericDone = (DataRequestMonitor<ICommandResult>) rm;
|
||||
|
||||
CommandInfo cachedCmd = new CommandInfo( CommandStyle.NONCOALESCED, genericCommand, genericDone) ;
|
||||
|
||||
final IDMContext context = genericCommand.getContext();
|
||||
|
||||
/*
|
||||
* If command is already cached, just return the cached data.
|
||||
*/
|
||||
if(fCachedContexts.get(context) != null && fCachedContexts.get(context).containsKey(cachedCmd)){
|
||||
// Cast to the erased type of the returned command result.
|
||||
// To ensure type safety, we are relying on the correct matching
|
||||
// of command and result in the cached results map.
|
||||
@SuppressWarnings("unchecked")
|
||||
V v = (V) fCachedContexts.get(context).get(cachedCmd);
|
||||
|
||||
rm.setData(v);
|
||||
rm.done();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return an error if the target is available anymore.
|
||||
*/
|
||||
if (!fIsTargetAvailable) {
|
||||
rm.setStatus(new Status(IStatus.ERROR, DsfDebugPlugin.PLUGIN_ID, IDsfService.INVALID_STATE, "Target not available.", null)); //$NON-NLS-1$
|
||||
rm.done();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are waiting for this command to complete ( but the command has
|
||||
* been sent to the debug engine), add this request monitor to list of waiting monitors.
|
||||
*/
|
||||
|
||||
for ( CommandInfo sentCommand : fPendingQCommandsSent ) {
|
||||
if ( sentCommand.equals( cachedCmd )) {
|
||||
sentCommand.getRequestMonitorList().add(genericDone);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We see if this command can be combined into a coalesced one. The
|
||||
* coalesce routine will take care of the already enqueued one which
|
||||
* this command is being coalesced with.
|
||||
*/
|
||||
|
||||
CommandInfo coalescedCmd = getCoalescedCommand(cachedCmd);
|
||||
|
||||
if ( coalescedCmd != null ) {
|
||||
/*
|
||||
* The original command we were handed needs to go into the waiting QUEUE.
|
||||
* We also need to point it it to the coalesced command.
|
||||
*/
|
||||
fPendingQWaitingForCoalescedCompletion.add(cachedCmd);
|
||||
cachedCmd.setCoalescedCmd(coalescedCmd);
|
||||
cachedCmd = coalescedCmd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we have a command to send ( coalesced or not ). Put it in the cannot touch
|
||||
* it list and give it to the Control object. Our state handlers will move it into
|
||||
* the proper list as the Control object deals with it.
|
||||
*/
|
||||
final CommandInfo finalCachedCmd = cachedCmd;
|
||||
fPendingQCommandsNotYetSent.add(finalCachedCmd);
|
||||
|
||||
fCommandControl.queueCommand(
|
||||
finalCachedCmd.getCommand(),
|
||||
new DataRequestMonitor<ICommandResult>(fCommandControl.getExecutor(), null) {
|
||||
@Override
|
||||
public void handleCompleted() {
|
||||
|
||||
/*
|
||||
* Match this up with a command set we know about.
|
||||
*/
|
||||
if ( ! fPendingQCommandsSent.remove(finalCachedCmd) ) {
|
||||
/*
|
||||
* It should not be the case that this is possible. It would mean we
|
||||
* have mismanaged the queues or completions are lost at the lower
|
||||
* levels. When the removal and cancellation is completed this code
|
||||
* will probably not be here. But for now just return.
|
||||
*/
|
||||
return ;
|
||||
}
|
||||
|
||||
if ( finalCachedCmd.getCommandstyle() == CommandStyle.COALESCED ) {
|
||||
/*
|
||||
* We matched a command which is itself already a COALESCED command. So
|
||||
* we need to go throught the list of unsent commands which were not sent
|
||||
* because the coalesced command represented it. For each match we find
|
||||
* we create a new result from the coalesced command for it.
|
||||
*/
|
||||
ICommandResult result = getData();
|
||||
|
||||
for ( CommandInfo waitingEntry : new ArrayList<CommandInfo>(fPendingQWaitingForCoalescedCompletion) ) {
|
||||
|
||||
if ( waitingEntry.getCoalescedCmd() == finalCachedCmd ) {
|
||||
|
||||
/*
|
||||
* Remove this entry from the list since we can complete it.
|
||||
*/
|
||||
fPendingQWaitingForCoalescedCompletion.remove(waitingEntry);
|
||||
|
||||
// Cast the calculated result back to the requested type.
|
||||
@SuppressWarnings("unchecked")
|
||||
V newresult = (V)result.getSubsetResult(waitingEntry.getCommand());
|
||||
|
||||
|
||||
if(fCachedContexts.get(context) != null){
|
||||
fCachedContexts.get(context).put(waitingEntry, newresult);
|
||||
}
|
||||
else{
|
||||
HashMap<CommandInfo, ICommandResult> map = new HashMap<CommandInfo, ICommandResult>();
|
||||
map.put(waitingEntry, newresult);
|
||||
fCachedContexts.put(context, map);
|
||||
}
|
||||
|
||||
if (!getStatus().isOK()) {
|
||||
|
||||
/*
|
||||
* We had some form of error with the original command. So notify the
|
||||
* original requestors of the issues.
|
||||
*/
|
||||
for (DataRequestMonitor<?> pendingRM : waitingEntry.getRequestMonitorList()) {
|
||||
pendingRM.setStatus(getStatus());
|
||||
pendingRM.done();
|
||||
}
|
||||
} else {
|
||||
assert newresult != null;
|
||||
|
||||
/*
|
||||
* Notify the original requestors of the positive results.
|
||||
*/
|
||||
for (DataRequestMonitor<? extends ICommandResult> pendingRM : waitingEntry.getRequestMonitorList()) {
|
||||
// Cast the pending return token to match the requested type.
|
||||
@SuppressWarnings("unchecked")
|
||||
DataRequestMonitor<V> vPendingRM = (DataRequestMonitor<V>) pendingRM;
|
||||
|
||||
vPendingRM.setData(newresult);
|
||||
vPendingRM.done();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* This is an original request which completed. Indicate success or
|
||||
* failure to the original requestors.
|
||||
*/
|
||||
|
||||
if (!getStatus().isOK()) {
|
||||
/*
|
||||
* We had some form of error with the original command. So notify the
|
||||
* original requestors of the issues.
|
||||
*/
|
||||
for (DataRequestMonitor<?> pendingRM : finalCachedCmd.getRequestMonitorList()) {
|
||||
pendingRM.setStatus(getStatus());
|
||||
pendingRM.done();
|
||||
}
|
||||
} else {
|
||||
// Cast the calculated result back to the requested type.
|
||||
@SuppressWarnings("unchecked")
|
||||
V result = (V)getData();
|
||||
|
||||
if(fCachedContexts.get(context) != null){
|
||||
fCachedContexts.get(context).put(finalCachedCmd, result);
|
||||
}
|
||||
else{
|
||||
HashMap<CommandInfo, ICommandResult> map = new HashMap<CommandInfo, ICommandResult>();
|
||||
map.put(finalCachedCmd, result);
|
||||
fCachedContexts.put(context, map);
|
||||
}
|
||||
|
||||
for (DataRequestMonitor<? extends ICommandResult> pendingRM : finalCachedCmd.getRequestMonitorList()) {
|
||||
// Cast the pending return token to match the requested type.
|
||||
@SuppressWarnings("unchecked")
|
||||
DataRequestMonitor<V> vPendingRM = (DataRequestMonitor<V>) pendingRM;
|
||||
|
||||
vPendingRM.setData(result);
|
||||
vPendingRM.done();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache to a state in which target access is not allowed.
|
||||
* When target is not available, commands to the target will either
|
||||
* return data that is found in the cache already, or will return an
|
||||
* error. This is useful in avoiding sending commands to target when
|
||||
* they are known to fail or return unreliable results, while still
|
||||
* providing access to the cached data.
|
||||
*
|
||||
* @param isAvailable Flag indicating whether target can be accessed.
|
||||
*/
|
||||
public void setTargetAvailable(boolean isAvailable) {
|
||||
fIsTargetAvailable = isAvailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves current flag indicating target availability.
|
||||
* @see #setTargetAvailable(boolean)
|
||||
*/
|
||||
public boolean isTargetAvailable() {
|
||||
return fIsTargetAvailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the cache data.
|
||||
*/
|
||||
public void reset() {
|
||||
fCachedContexts.clear();
|
||||
fResetCounter++;
|
||||
}
|
||||
|
||||
public void commandRemoved(ICommand<? extends ICommandResult> command) {
|
||||
/*
|
||||
* Do nothing.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.eclipse.dd.dsf.mi.service.control.IDebuggerControl.ICommandListener#commandQueued(org.eclipse.dd.dsf.mi.core.command.ICommand)
|
||||
*/
|
||||
public void commandQueued(ICommand<? extends ICommandResult> command) {
|
||||
/*
|
||||
* Do nothing.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.eclipse.dd.dsf.mi.service.control.IDebuggerControl.ICommandListener#commandDone(org.eclipse.dd.dsf.mi.core.command.ICommand, org.eclipse.dd.dsf.mi.core.command.ICommandResult)
|
||||
*/
|
||||
public void commandDone(ICommand<? extends ICommandResult> command, ICommandResult result) {
|
||||
/*
|
||||
* We handle the done with a runnable where we initiated the command
|
||||
* so there is nothing to do here.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Move the command into our internal sent list. This means we can no longer look at
|
||||
* this command for possible coalescence since it has been given to the debug engine
|
||||
* and is currently being processed.
|
||||
*
|
||||
* (non-Javadoc)
|
||||
* @see org.eclipse.dd.dsf.mi.service.control.IDebuggerControl.ICommandListener#commandSent(org.eclipse.dd.dsf.mi.core.command.ICommand)
|
||||
*/
|
||||
public void commandSent(ICommand<? extends ICommandResult> command) {
|
||||
|
||||
// Cast the generic ?'s to concrete types in the cache implementation.
|
||||
@SuppressWarnings("unchecked")
|
||||
ICommand<ICommandResult> genericCommand = (ICommand<ICommandResult>)command;
|
||||
|
||||
CommandInfo cachedCmd = new CommandInfo( CommandStyle.NONCOALESCED, genericCommand, null) ;
|
||||
|
||||
for ( CommandInfo unqueuedCommand : new ArrayList<CommandInfo>(fPendingQCommandsNotYetSent) ) {
|
||||
if ( unqueuedCommand.equals( cachedCmd )) {
|
||||
fPendingQCommandsNotYetSent.remove(unqueuedCommand);
|
||||
fPendingQCommandsSent.add(unqueuedCommand);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the cache entries for given context. Clears the whole cache if
|
||||
* context parameter is null.
|
||||
*/
|
||||
public void reset(IDMContext dmc) {
|
||||
if (dmc == null) {
|
||||
fCachedContexts.clear();
|
||||
}
|
||||
for (Iterator<IDMContext> itr = fCachedContexts.keySet().iterator(); itr.hasNext();) {
|
||||
IDMContext keyDmc = itr.next();
|
||||
if (keyDmc != null && DMContexts.isAncestorOf(keyDmc, dmc)) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ package org.eclipse.dd.dsf.concurrent;
|
|||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.MultiStatus;
|
||||
import org.eclipse.dd.dsf.DsfPlugin;
|
||||
|
||||
|
@ -88,4 +89,12 @@ public class CountingRequestMonitor extends RequestMonitor {
|
|||
public String toString() {
|
||||
return "CountingRequestMonitor: " + getStatus().toString(); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setStatus(IStatus status) {
|
||||
if (!(getStatus() instanceof MultiStatus)) {
|
||||
super.setStatus(new MultiStatus(DsfPlugin.PLUGIN_ID, 0, "Combined status of multiple asynchronous operations", null)); //$NON-NLS-1$
|
||||
}
|
||||
((MultiStatus)getStatus()).add(status);
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue